| #!/bin/bash |
| |
| # fips_crypto_hmac.sh |
| # |
| # Author : Rohit Kothari (r.kothari@samsung.com) |
| # Created on : 14 Feb 2014 |
| # Modified on: 22 Jun 2015 by Jia Ma (jia.ma@samsung.com) /* To adapt to KASLR change */ |
| # Modified on: 09 Jul 2015 by Wenbo Shen (wenbo.s@samsung.com) /* To reduce the hmac generation time */ |
| # Copyright (c) Samsung Electronics 2014 |
| |
| # Given a vmlinux file and a System.map, this scripts finds bytes belonging to |
| # Kernel Crypto within vmlinux file.(Under section .text, .init.text, .exit.text and .rodata) |
| # After collecting all the bytes, it calculates a hmac(sha256) on those bytes. |
| # Generated hmac is put back into a crypto rodata variable within vmlinux file itself. |
| # This makes the build time hmac available at runtime, for integrity check. |
| # |
| # To find crypto bytes, this scripts heavily relies on output of arm-eabi-readelf. |
| # If the output of arm-eabi-readelf changes in future, this script might need changes. |
| # |
| # Pre-conditions : $READELF, $HOSTCC variables are set. |
| # |
| # |
| |
| # Here we want to seperate the oneshot code from the repeated code |
| ################ the repeated code #################### |
| kaslr_patch () { |
| index=$1 |
| ./kaslr_fips $vmlinux_var $reloc_start_addr $reloc_end_addr $dynsym_addr $index $first_fmp_rodata $last_fmp_rodata $2 |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : kaslr_fips : unable to patch the vmlinux" |
| exit 1 |
| fi |
| } |
| |
| hmac_patch() { |
| index=$1 |
| offsets_sizes_file=$2 |
| |
| fips_utils=./fips_fmp_utils |
| hmac_offset=`expr $fmp_hmac_offset_base + $((32*$index)) ` |
| |
| |
| rm -f builtime_bytes.txt #used for debugging |
| rm -f builtime_bytes.bin #used for calculating hmac |
| |
| date_var=`date` |
| echo "Created on : " $date_var > builtime_bytes.txt |
| |
| |
| #Using offsets_sizes.txt, dump crypto bytes from vmlinux file into builtime_bytes.bin |
| #Also gather printf's into builtime_bytes.txt, for debugging if required |
| while read args; do |
| $fips_utils -g $vmlinux_var $args builtime_bytes.bin >> builtime_bytes.txt |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : $fips_utils : unable to gather $3 bytes from vmlinux" |
| exit 1 |
| fi |
| echo "" >> builtime_bytes.txt |
| done < $offsets_sizes_file |
| |
| |
| if [[ ! -f builtime_bytes.bin ]]; then |
| echo "$0 : builtime_bytes.bin does not exist" |
| exit 1 |
| fi |
| |
| #file_size=`cat builtime_bytes.bin| wc -c` |
| |
| # Make sure that file size of fmp_hmac.bin is as expected |
| #if [ $total_bytes -ne $file_size ]; then |
| #echo "$0: Bytes mismatch" |
| #exit 1 |
| #fi |
| |
| |
| key="The quick brown fox jumps over the lazy dog" |
| |
| # Now, generate the hmac. |
| openssl dgst -sha256 -hmac "$key" -binary -out fmp_hmac.bin builtime_bytes.bin |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : openssl dgst command returned error" |
| exit 1 |
| fi |
| |
| # Just, for debugging, print the same hmac on console |
| #openssl dgst -sha256 -hmac "$key" builtime_bytes.bin |
| |
| if [[ ! -f fmp_hmac.bin ]]; then |
| echo "$0 : fmp_hmac.bin does not exist" |
| exit 1 |
| fi |
| |
| file_size=`cat fmp_hmac.bin| wc -c` |
| |
| # hmac(sha256) produces 32 bytes of hmac |
| if [ $file_size -ne 32 ]; then |
| echo "$0: Unexpected size of Hash file : " $file_size |
| exit 1 |
| fi |
| |
| # Now that we have the hmac, update this hmac into an rodata "builtime_crypto_hmac" varialble |
| # in vmlinux file. |
| # This variable has a place holder 32 bytes that will be over-written with generated hmac. |
| # This way, this build time hmac, will be available as a read-only variable at run-time. |
| |
| if [[ $hmac_offset -le 0 ]]; then echo "$0 : hmac_offset invalid"; exit 1; fi |
| |
| # This does the actual update of hmac into vmlinux file, at given offset |
| $fips_utils -u $vmlinux_var fmp_hmac.bin $hmac_offset |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : fips_crypto_utils : unable to update hmac in vmlinux" |
| exit 1 |
| fi |
| |
| rm -f fmp_hmac.bin |
| rm -f builtime_bytes.txt |
| rm -f builtime_bytes.bin |
| } |
| |
| ################ the kaslr patch oneshot code #################### |
| # 1st parameter is the array, 2nd is the output file name |
| offsets_sizes_gen(){ |
| name=$1[@] |
| array=("${!name}") |
| |
| outfile=$2 |
| rm -f $outfile |
| |
| #Addresses retrieved must be a valid hex |
| reg='^[0-9A-Fa-f]+$' |
| |
| #Total bytes of all crypto sections scanned. Used later for error checking |
| #total_bytes=0; |
| |
| # For each type of Section : |
| # first_addr = Address of first_crypto_text, first_crypto_rodata, etc. |
| # last_addr = Address of last_crypto_text, last_crypto_rodata etc. |
| # start_addr = Starting Address of a section within vmlinux |
| # offset = Offset in vmlinux file where the section begins |
| # file_offset = Offset in vmlinux file where the crypto bytes begins. |
| # size = size of crypto bytes. |
| |
| # Output is offsets_sizes.txt, of the format |
| # Section Name crypto_bytes_offset crypto_bytes_size |
| # (in decimal) (in decimal) |
| # .text 2531072 114576 |
| # .rodata 9289648 55388 |
| # : : : |
| |
| for i in "${array[@]}"; do |
| |
| var1=var2=var3=var4=var5="" |
| first_addr=last_addr=start_addr=offset=file_offset=size="" |
| k=1 |
| #This loop creates var1, var2 etc and set them to individual strings of a row in array |
| for j in $i; do |
| export var$k=$j |
| let k+=1 |
| done |
| |
| first_addr=`cat $system_map_var|grep -w $var2|awk '{print $1}'` |
| if [[ ! $first_addr =~ $reg ]]; then echo "$0 : first_addr invalid"; exit 1; fi |
| |
| last_addr=`cat $system_map_var|grep -w $var3|awk '{print $1}'` |
| if [[ ! $last_addr =~ $reg ]]; then echo "$0 : last_addr invalid"; exit 1; fi |
| |
| start_addr=`cat vmlinux.elf |grep -w "$var1 "|grep PROGBITS|awk '{print '$var4'}'` |
| if [[ ! $start_addr =~ $reg ]]; then echo "$0 : start_addr invalid"; exit 1; fi |
| |
| offset=`cat vmlinux.elf |grep -w "$var1 "|grep PROGBITS|awk '{print '$var5'}'` |
| if [[ ! $offset =~ $reg ]]; then echo "$0 : offset invalid"; exit 1; fi |
| |
| if [[ $((16#$first_addr)) -lt $((16#$start_addr)) ]]; then echo "$0 : first_addr < start_addr"; exit 1; fi |
| |
| if [[ $((16#$last_addr)) -le $((16#$first_addr)) ]]; then echo "$0 : last_addr <= first_addr"; exit 1; fi |
| |
| file_offset=`expr $((16#$offset)) + $((16#$first_addr)) - $((16#$start_addr))` |
| if [[ $file_offset -le 0 ]]; then echo "$0 : file_offset invalid"; exit 1; fi |
| |
| size=`expr $((16#$last_addr)) - $((16#$first_addr))` |
| if [[ $size -le 0 ]]; then echo "$0 : crypto section size invalid"; exit 1; fi |
| |
| echo "$var1 " $file_offset " " $size >> $outfile |
| |
| #let "total_bytes += `expr $((16#$last_addr)) - $((16#$first_addr))`" |
| done |
| |
| if [[ ! -f $outfile ]]; then |
| echo "$0 : offset_sizes.txt does not exist" |
| exit 1 |
| fi |
| |
| |
| } |
| |
| cal_hmac_offset(){ |
| first_addr=`cat $system_map_var|grep -w $1|awk '{print $1}' ` |
| if [[ ! $first_addr =~ $reg ]]; then echo "$0 : first_addr of hmac variable invalid"; exit 1; fi |
| |
| start_addr=`cat vmlinux.elf |grep -w $2|grep PROGBITS|awk '{print $5}' ` |
| if [[ ! $start_addr =~ $reg ]]; then echo "$0 : start_addr of .rodata invalid"; exit 1; fi |
| |
| offset=`cat vmlinux.elf |grep -w $2|grep PROGBITS| awk '{print $6}' ` |
| if [[ ! $offset =~ $reg ]]; then echo "$0 : offset of .rodata invalid"; exit 1; fi |
| |
| if [[ $((16#$first_addr)) -le $((16#$start_addr)) ]]; then echo "$0 : hmac var first_addr <= start_addr"; exit 1; fi |
| |
| fmp_hmac_offset_base=`expr $((16#$offset)) + $((16#$first_addr)) - $((16#$start_addr)) ` |
| |
| } |
| |
| ################ Start Here ################ |
| |
| START_TIME=$SECONDS |
| |
| if test $# -ne 2; then |
| echo "Usage: $0 vmlinux System.map" |
| exit 1 |
| fi |
| |
| vmlinux_var=$1 |
| system_map_var=$2 |
| |
| if [[ -z "$vmlinux_var" || -z "$system_map_var" || -z "$READELF" || -z "$HOSTCC" ]]; then |
| echo "$0 : variables not set" |
| exit 1 |
| fi |
| |
| if [[ ! -f $vmlinux_var || ! -f $system_map_var ]]; then |
| echo "$0 : files does not exist" |
| exit 1 |
| fi |
| |
| rm -f vmlinux.elf |
| $READELF -S $vmlinux_var > vmlinux.elf |
| |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : $READELF returned error" |
| exit 1 |
| fi |
| |
| #start patching vmlinux with static patcher, temporary put it here |
| rm -f kaslr_fips |
| $HOSTCC -o kaslr_fips $srctree/scripts/kaslr_fips_fmp.c |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : $HOSTCC returned error" |
| exit 1 |
| fi |
| |
| rm -f fips_fmp_utils |
| $HOSTCC -o fips_fmp_utils $srctree/scripts/fips_fmp_utils.c |
| retval=$? |
| if [ $retval -ne 0 ]; then |
| echo "$0 : $HOSTCC returned error" |
| exit 1 |
| fi |
| |
| var="__reloc_start" |
| var1=`cat $system_map_var|grep -w $var|awk '{print $1}'` |
| var="__reloc_end" |
| var2=`cat $system_map_var|grep -w $var|awk '{print $1}'` |
| var="__dynsym_start" |
| var3=`cat $system_map_var|grep -w $var|awk '{print $1}'` |
| |
| var="first_fmp_rodata" |
| var4=`cat $system_map_var|grep -w $var|awk '{print $1}'` |
| var="last_fmp_rodata" |
| var5=`cat $system_map_var|grep -w $var|awk '{print $1}'` |
| |
| reloc_start_addr=`expr $((16#$var1))` |
| reloc_end_addr=`expr $((16#$var2))` |
| dynsym_addr=`expr $((16#$var3))` |
| first_fmp_rodata=`expr $((16#$var4))` |
| last_fmp_rodata=`expr $((16#$var5))` |
| |
| |
| ################# FMP ####################### |
| fips_fmp[0]=".text first_fmp_text last_fmp_text \$5 \$6" |
| fips_fmp[1]=".rodata first_fmp_rodata last_fmp_rodata \$5 \$6" |
| fips_fmp[2]=".init.text first_fmp_init last_fmp_init \$4 \$5" |
| |
| offsets_sizes_fmp="offsets_sizes_fmp.txt" |
| offsets_sizes_gen fips_fmp $offsets_sizes_fmp |
| |
| #find hmac_offset in vmlinux for updating |
| cal_hmac_offset builtime_fmp_hmac .rodata |
| |
| rodata_va_addr=`cat vmlinux.elf|grep -w '.rodata'|awk '{print $5}'` |
| rodata_file_addr=`cat vmlinux.elf|grep -w '.rodata'|awk '{print $6}'` |
| va_to_file=`expr $((16#$rodata_va_addr)) - $((16#$rodata_file_addr))` |
| |
| # the loop |
| for index in {0..63} |
| do |
| kaslr_patch $index $va_to_file |
| hmac_patch $index $offsets_sizes_fmp fmp |
| done |
| |
| rm -f kaslr_fips |
| rm -f fips_fmp_utils |
| rm -f vmlinux.elf |
| rm -f offsets_sizes* |
| |
| ELAPSED_TIME=$(($SECONDS - $START_TIME)) |
| echo ">>>>> Time used for generated all hashes is $(($ELAPSED_TIME)) sec" |
| |
| # And we are done... |