1#!/bin/bash 2# 3# Build EVRoots.plist in ./BuiltKeychains/, given the embedded list of 4# OIDs in this script and their associated root certificate(s) which can 5# be found by the specified filename in ./roots. 6# 7 8CWD=`/bin/pwd` 9ROOT_CERT_DIR="$CWD"/roots 10ALT_ROOT_CERT_DIR="$CWD"/removed 11KC_DIR="$CWD"/BuiltKeychains 12 13# Set USE_PUBKEY to a non-zero value to generate public key hashes; 14# default behavior is to generate a hash of the certificate itself 15USE_PUBKEY=0 16 17if [ "$USE_PUBKEY" -ne 0 ]; then 18### NOTE: the certlist tool is used here to generate the MD5 hash of a 19### certificate's public key. This functionality needs to be added to the 20### security command at some point; there doesn't seem to be a way to get 21### it from openssl. 22 CERTLIST="$CWD"/../../tests/certlist 23 if [ ! -e "$CERTLIST" ]; then 24 printf "### BUILD FAILED: $CERTLIST is missing\n" 25 exit 1 26 fi 27fi 28 29SECURITY=/usr/bin/security 30OPENSSL=/usr/bin/openssl 31PLB=/usr/libexec/PlistBuddy 32#PLB="$CWD"/../../tests/PlistBuddy 33 34SaveKeychainList() { 35 SAVED_KC_LIST=`"$SECURITY" list -d user` 36} 37 38RestoreKeychainList() { 39 /bin/echo -n "$SAVED_KC_LIST" | xargs "$SECURITY" list -d user -s 40} 41 42if [ ! -e "$ROOT_CERT_DIR" ] || [ ! -e "$KC_DIR" ]; then 43 printf "You do not seem to be in a current security_certificates directory. Aborting.\n" 44 exit 1 45fi 46 47EVROOTS_CONFIG="$CWD"/evroot.config 48 49EVROOTS_KC=EVRoots.keychain 50EVROOTS_KC_PATH="/tmp/$EVROOTS_KC" 51EVROOTS_PLIST=EVRoots.plist 52EVROOTS_PLIST_PATH="$KC_DIR/$EVROOTS_PLIST" 53 54# save keychain list so we don't add EVRoots.keychain to it 55SaveKeychainList 56 57printf "Creating empty %s...\n" "$EVROOTS_KC" 58/bin/rm -f "$EVROOTS_KC_PATH" || exit 1 59"$SECURITY" create-keychain -p "$EVROOTS_KC" "$EVROOTS_KC_PATH" || exit 1 60 61TMPIFS=$IFS 62IFS=$'\x0A'$'\x0D' 63 64# first pass: build the EVRoots keychain 65for OID in `cat "$EVROOTS_CONFIG"`; do 66 # ignore comments and blank lines 67 OID=`echo "$OID" | sed -e 's/^#.*//'` 68 if [ "$OID" = "" ]; then continue; fi 69 # grab OID key 70 OIDKEY=`echo "$OID" | awk '{print $1}'` 71 # convert rest of line into comma-delimited filename list 72 CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'` 73 IFS=$'\x3A' 74 for CERTFILE in $CERTFILES; do 75 if [ "$CERTFILE" = "" ]; then continue; fi 76 printf "Adding cert from file: %s\n" "$CERTFILE" 77 CERT_TO_ADD="$ROOT_CERT_DIR/$CERTFILE" 78 if [ ! -e "$CERT_TO_ADD" ]; then 79 CERT_TO_ADD="$ALT_ROOT_CERT_DIR/$CERTFILE" 80 fi 81 # should prune duplicates first; for now, just ignore errors 82 "$SECURITY" \ 83 -q add-certificates \ 84 -k "$EVROOTS_KC_PATH" \ 85 "$CERT_TO_ADD" 2>/dev/null 86 done 87 IFS=$'\x0A'$'\x0D' 88done 89 90printf "Removing %s...\n" "$EVROOTS_PLIST" 91/bin/rm -f "$EVROOTS_PLIST_PATH" 92 93# second pass: get hashes and build the EVRoots plist 94for OID in `cat "$EVROOTS_CONFIG"`; do 95 # ignore comments and blank lines 96 OID=`echo "$OID" | sed -e 's/^#.*//'` 97 if [ "$OID" = "" ]; then continue; fi 98 # grab OID key 99 OIDKEY=`echo "$OID" | awk '{print $1}'` 100 # add an array for this OID key 101 "$PLB" -c "add :$OIDKEY array" "$EVROOTS_PLIST_PATH" 102 # convert rest of line into comma-delimited filename list 103 CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'` 104 IFS=$'\x3A' 105 # process each certificate file 106 IDX=0 107 for CERTFILE in $CERTFILES; do 108 if [ "$CERTFILE" = "" ]; then continue; fi 109 CERT_TO_HASH="$ROOT_CERT_DIR/$CERTFILE" 110 if [ ! -e "$CERT_TO_HASH" ]; then 111 printf "... Warning: \"%s\" has been removed, but its hash is still in EV map\n" "$CERTFILE" 112 CERT_TO_HASH="$ALT_ROOT_CERT_DIR/$CERTFILE" 113 fi 114 if [ ! -e "$CERT_TO_HASH" ]; then 115 printf "... Could not find file to hash: \"%s\"\n" "$CERT_TO_HASH" 116 continue 117 fi 118 119 if [ "$USE_PUBKEY" -ne 0 ]; then 120 # get hash values for the certificate's public key 121 PK_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -ocspid | grep "Public key" | awk '{print $5}'` 122 PK_MD5=`"$CERTLIST" -k "$EVROOTS_KC_PATH" -p --md5 --sha1 | grep "$PK_SHA1" | sed -e 's/^.\{37\}//' | awk '{print $1}'` 123 printf "Public key hashes for \"%s\":\n" "$CERTFILE" 124 printf " MD5: %s SHA1: %s\n" "$PK_MD5" "$PK_SHA1" 125 printf "%s" "$PK_MD5" | xxd -r -p > /tmp/md5hashtmp 126 printf "%s" "$PK_SHA1" | xxd -r -p > /tmp/sha1hashtmp 127 128 # add hash values to the array 129 IDX_NEXT=`expr $IDX + 1` 130 "$PLB" -c "add :$OIDKEY:$IDX data" \ 131 -c "import :$OIDKEY:$IDX /tmp/md5hashtmp" \ 132 -c "add :$OIDKEY:$IDX_NEXT data" \ 133 -c "import :$OIDKEY:$IDX_NEXT /tmp/sha1hashtmp" \ 134 "$EVROOTS_PLIST_PATH" 135 136 # verify the hash values were added correctly 137 VERIFY_ERROR=0 138 DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \ 139 "$EVROOTS_PLIST_PATH" | \ 140 xxd -u -p | sed -e 's/0A$//'` 141 if [ "$DATA" != "$PK_MD5" ]; then VERIFY_ERROR=1; fi 142 DATA=`"$PLB" -c "print :$OIDKEY:$IDX_NEXT data" \ 143 "$EVROOTS_PLIST_PATH" | \ 144 xxd -u -p | sed -e 's/0A$//'` 145 if [ "$DATA" != "$PK_SHA1" ]; then VERIFY_ERROR=1; fi 146 if [ ! "$VERIFY_ERROR" -eq 0 ]; then 147 printf "### BUILD FAILED: data verification error!\n" 148 printf "You likely need to install a newer version of $PLB; see <rdar://6208924> for details\n" 149 RestoreKeychainList 150 /bin/rm -f "$EVROOTS_PLIST_PATH" 151 exit 1 152 fi 153 else 154 # get SHA-1 hash value for the certificate 155 CERT_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -fingerprint -noout | sed -e 's/SHA1 Fingerprint=//' -e 's/://g'` 156 printf "Certificate fingerprint for \"%s\":\n" "$CERTFILE" 157 printf " SHA1: %s\n" "$CERT_SHA1" 158 printf "%s" "$CERT_SHA1" | xxd -r -p > /tmp/certsha1hashtmp 159 # add hash value to the array 160 IDX_NEXT=`expr $IDX + 1` 161 "$PLB" -c "add :$OIDKEY:$IDX data" \ 162 -c "import :$OIDKEY:$IDX /tmp/certsha1hashtmp" \ 163 "$EVROOTS_PLIST_PATH" 164 # verify the hash value was added correctly 165 VERIFY_ERROR=0 166 DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \ 167 "$EVROOTS_PLIST_PATH" | \ 168 xxd -u -p | sed -e 's/0A$//'` 169 if [ "$DATA" != "$CERT_SHA1" ]; then VERIFY_ERROR=1; fi 170 if [ ! "$VERIFY_ERROR" -eq 0 ]; then 171 printf "### BUILD FAILED: data verification error!\n" 172 printf "You likely need to install a newer version of $PLB; see <rdar://6208924> for details\n" 173 RestoreKeychainList 174 /bin/rm -f "$EVROOTS_PLIST_PATH" 175 exit 1 176 fi 177 fi 178 179 IDX="$IDX_NEXT" 180 done 181 IFS=$'\x0A'$'\x0D' 182done 183IFS="$TMPIFS" 184 185RestoreKeychainList 186/bin/chmod 0644 "$EVROOTS_PLIST_PATH" 187 188printf "Built $EVROOTS_PLIST_PATH successfully\n" 189 190exit 0 191