buildEVRoots   [plain text]


#!/bin/bash
#
# Build EVRoots.plist in ./BuiltKeychains/, given the embedded list of
# OIDs in this script and their associated root certificate(s) which can
# be found by the specified filename in ./roots.
#

CWD=`/bin/pwd`
ROOT_CERT_DIR="$CWD"/roots
ALT_ROOT_CERT_DIR="$CWD"/roots_incoming
KC_DIR="$CWD"/BuiltKeychains

# Set USE_PUBKEY to a non-zero value to generate public key hashes;
# default behavior is to generate a hash of the certificate itself
USE_PUBKEY=0

if [ "$USE_PUBKEY" -ne 0 ]; then
### NOTE: the certlist tool is used here to generate the MD5 hash of a
### certificate's public key. This functionality needs to be added to the
### security command at some point; there doesn't seem to be a way to get
### it from openssl.
    CERTLIST="$CWD"/../../tests/certlist
    if [ ! -e "$CERTLIST" ]; then
    printf "### BUILD FAILED: $CERTLIST is missing\n"
    exit 1
    fi
fi

SECURITY=/usr/bin/security
OPENSSL=/usr/bin/openssl
PLB=/usr/libexec/PlistBuddy
#PLB="$CWD"/../../tests/PlistBuddy

SaveKeychainList() {
    SAVED_KC_LIST=`"$SECURITY" list -d user`
}

RestoreKeychainList() {
   /bin/echo -n "$SAVED_KC_LIST" | xargs "$SECURITY" list -d user -s
}

if [ ! -e "$ROOT_CERT_DIR" ] || [ ! -e "$KC_DIR" ]; then
   printf "You do not seem to be in a current security_certificates directory. Aborting.\n"
   exit 1
fi

EVROOTS_CONFIG="$CWD"/evroot.config

EVROOTS_KC=EVRoots.keychain
EVROOTS_KC_PATH="/tmp/$EVROOTS_KC"
EVROOTS_PLIST=EVRoots.plist
EVROOTS_PLIST_PATH="$KC_DIR/$EVROOTS_PLIST"

# save keychain list so we don't add EVRoots.keychain to it
SaveKeychainList

printf "Creating empty %s...\n" "$EVROOTS_KC"
/bin/rm -f "$EVROOTS_KC_PATH" || exit 1
"$SECURITY" create-keychain -p "$EVROOTS_KC" "$EVROOTS_KC_PATH" || exit 1

TMPIFS=$IFS
IFS=$'\x0A'$'\x0D'

# first pass: build the EVRoots keychain
for OID in `cat "$EVROOTS_CONFIG"`; do
	# ignore comments and blank lines
	OID=`echo "$OID" | sed -e 's/^#.*//'`
	if [ "$OID" = "" ]; then continue; fi
	# grab OID key
	OIDKEY=`echo "$OID" | awk '{print $1}'`
	# convert rest of line into comma-delimited filename list
	CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'`
	IFS=$'\x3A'
	for CERTFILE in $CERTFILES; do
		if [ "$CERTFILE" = "" ]; then continue; fi
		printf "Adding cert from file: %s\n" "$CERTFILE"
		CERT_TO_ADD="$ROOT_CERT_DIR/$CERTFILE"
		if [ ! -e "$CERT_TO_ADD" ]; then
			CERT_TO_ADD="$ALT_ROOT_CERT_DIR/$CERTFILE"
		fi
		# should prune duplicates first; for now, just ignore errors
		"$SECURITY" \
		-q add-certificates \
		-k "$EVROOTS_KC_PATH" \
		"$CERT_TO_ADD"
	done
	IFS=$'\x0A'$'\x0D'
done

printf "Removing %s...\n" "$EVROOTS_PLIST"
/bin/rm -f "$EVROOTS_PLIST_PATH"

# second pass: get hashes and build the EVRoots plist
for OID in `cat "$EVROOTS_CONFIG"`; do
	# ignore comments and blank lines
	OID=`echo "$OID" | sed -e 's/^#.*//'`
	if [ "$OID" = "" ]; then continue; fi
	# grab OID key
	OIDKEY=`echo "$OID" | awk '{print $1}'`
	# add an array for this OID key
	"$PLB" -c "add :$OIDKEY array" "$EVROOTS_PLIST_PATH"
	# convert rest of line into comma-delimited filename list
	CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'`
	IFS=$'\x3A'
	# process each certificate file
	IDX=0
	for CERTFILE in $CERTFILES; do
		if [ "$CERTFILE" = "" ]; then continue; fi
		CERT_TO_HASH="$ROOT_CERT_DIR/$CERTFILE"
		if [ ! -e "$CERT_TO_HASH" ]; then
	                printf "... Could not find file to hash: \"%s\"\n" "$CERT_TO_HASH"
			CERT_TO_HASH="$ALT_ROOT_CERT_DIR/$CERTFILE"
		fi
		
		if [ "$USE_PUBKEY" -ne 0 ]; then
		    # get hash values for the certificate's public key
		    PK_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -ocspid | grep "Public key" | awk '{print $5}'`
		    PK_MD5=`"$CERTLIST" -k "$EVROOTS_KC_PATH" -p --md5 --sha1 | grep "$PK_SHA1" | sed -e 's/^.\{37\}//' | awk '{print $1}'`
		    printf "Public key hashes for \"%s\":\n" "$CERTFILE"
		    printf "  MD5: %s  SHA1: %s\n" "$PK_MD5" "$PK_SHA1"
		    printf "%s" "$PK_MD5" | xxd -r -p > /tmp/md5hashtmp
		    printf "%s" "$PK_SHA1" | xxd -r -p > /tmp/sha1hashtmp
		    
		    # add hash values to the array
		    IDX_NEXT=`expr $IDX + 1`
		    "$PLB"  -c "add :$OIDKEY:$IDX data" \
			    -c "import :$OIDKEY:$IDX /tmp/md5hashtmp" \
			    -c "add :$OIDKEY:$IDX_NEXT data" \
			    -c "import :$OIDKEY:$IDX_NEXT /tmp/sha1hashtmp" \
			    "$EVROOTS_PLIST_PATH"
    
		    # verify the hash values were added correctly
		    VERIFY_ERROR=0
		    DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \
			    "$EVROOTS_PLIST_PATH" | \
			    xxd -u -p | sed -e 's/0A$//'`
		    if [ "$DATA" != "$PK_MD5" ]; then VERIFY_ERROR=1; fi
		    DATA=`"$PLB" -c "print :$OIDKEY:$IDX_NEXT data" \
			    "$EVROOTS_PLIST_PATH" | \
			    xxd -u -p | sed -e 's/0A$//'`
		    if [ "$DATA" != "$PK_SHA1" ]; then VERIFY_ERROR=1; fi
		    if [ ! "$VERIFY_ERROR" -eq 0 ]; then
			    printf "### BUILD FAILED: data verification error!\n"
			    printf "You likely need to install a newer version of $PLB; see <rdar://6208924> for details\n"
			    RestoreKeychainList
			    /bin/rm -f "$EVROOTS_PLIST_PATH"
			    exit 1
		    fi
		else
		    # get SHA-1 hash value for the certificate
		    CERT_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -fingerprint -noout | sed -e 's/SHA1 Fingerprint=//' -e 's/://g'`
		    printf "Certificate fingerprint for \"%s\":\n" "$CERTFILE"
		    printf "  SHA1: %s\n" "$CERT_SHA1"
		    printf "%s" "$CERT_SHA1" | xxd -r -p > /tmp/certsha1hashtmp
		    # add hash value to the array
		    IDX_NEXT=`expr $IDX + 1`
		    "$PLB"  -c "add :$OIDKEY:$IDX data" \
			    -c "import :$OIDKEY:$IDX /tmp/certsha1hashtmp" \
			    "$EVROOTS_PLIST_PATH"
		    # verify the hash value was added correctly
		    VERIFY_ERROR=0
		    DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \
			    "$EVROOTS_PLIST_PATH" | \
			    xxd -u -p | sed -e 's/0A$//'`
		    if [ "$DATA" != "$CERT_SHA1" ]; then VERIFY_ERROR=1; fi
		    if [ ! "$VERIFY_ERROR" -eq 0 ]; then
			    printf "### BUILD FAILED: data verification error!\n"
			    printf "You likely need to install a newer version of $PLB; see <rdar://6208924> for details\n"
			    RestoreKeychainList
			    /bin/rm -f "$EVROOTS_PLIST_PATH"
			    exit 1
		    fi
		fi

		IDX="$IDX_NEXT"
	done
	IFS=$'\x0A'$'\x0D'
done
IFS="$TMPIFS"

RestoreKeychainList
/bin/chmod 0644 "$EVROOTS_PLIST_PATH"

printf "Built $EVROOTS_PLIST_PATH successfully\n"

exit 0