OpenSSL c_rehash translation
| Newsgroups | gmane.os.netbsd.devel.crypto |
|---|---|
| Message-ID | <[email protected]> |
I wanted to use a hashed certificate directory as the CApath argument to OpenSSL's SSL_CTX_load_verify_locations(). The normal OpenSSL distribution installs the Perl script c_rehash from the tools directory to automate the process of building a directory of hash-as-filename symlinks to the contained certificates. This script is not installed on NetBSD since Perl is not in the base system. I have translated the script to a shell script and tested it on a few situations. I'm posting it here so others may find and use it as necessary. 8<-----8<-----8<------8<------8<-----8<-----8<------8<------ #!/bin/sh # # Ben Secrest <[email protected]> # # sh c_rehash script, scan all files in a directory # and add symbolic links to their hash values. # # based on the c_rehash perl script distributed with openssl # # LICENSE: See OpenSSL license # ^^acceptable?^^ # # default certificate location DIR=/etc/openssl # for filetype bitfield IS_CERT=$(( 1 << 0 )) IS_CRL=$(( 1 << 1 )) # # check to see if a file is a certificate file or a CRL file # arguments: # 1. the filename to be scanned # returns: # bitfield of file type; uses ${IS_CERT} and ${IS_CRL} # check_file() { local IS_TYPE=0 # make IFS a newline so we can process grep output line by line local OLDIFS=${IFS} IFS=$( printf "\n" ) # XXX: could be more efficient to have two 'grep -m' but is -m portable? for LINE in $( grep '^-----BEGIN .*-----' ${1} ) do if echo ${LINE} \ | grep -q -E '^-----BEGIN (X509 |TRUSTED )?CERTIFICATE-----' then IS_TYPE=$(( ${IS_TYPE} | ${IS_CERT} )) if [ $(( ${IS_TYPE} & ${IS_CRL} )) -ne 0 ] then break fi elif echo ${LINE} | grep -q '^-----BEGIN X509 CRL-----' then IS_TYPE=$(( ${IS_TYPE} | ${IS_CRL} )) if [ $(( ${IS_TYPE} & ${IS_CERT} )) -ne 0 ] then break fi fi done # restore IFS IFS=${OLDIFS} return ${IS_TYPE} } # # use openssl to fingerprint a file # arguments: # 1. the filename to fingerprint # 2. the method to use (x509, crl) # returns: # none # assumptions: # user will capture output from last stage of pipeline # fingerprint() { ${SSL_CMD} ${2} -fingerprint -noout -in ${1} | sed 's/^.*=//' | tr -d ':' } # # link_hash - create links to certificate files # arguments: # 1. the filename to create a link for # 2. the type of certificate being linked (x509, crl) # returns: # 0 on success, 1 otherwise # link_hash() { local FINGERPRINT=$( fingerprint ${1} ${2} ) local HASH=$( ${SSL_CMD} ${2} -hash -noout -in ${1} ) local SUFFIX=0 local LINKFILE='' local TAG='' if [ ${2} = "crl" ] then TAG='r' fi LINKFILE=${HASH}.${TAG}${SUFFIX} while [ -f ${LINKFILE} ] do if [ ${FINGERPRINT} = $( fingerprint ${LINKFILE} ${2} ) ] then printf "WARNING: Skipping duplicate file ${1}\n" >&2 return 1 fi SUFFIX=$(( ${SUFFIX} + 1 )) LINKFILE=${HASH}.${TAG}${SUFFIX} done printf "${1} => ${LINKFILE}\n" # assume any system with a POSIX shell will either support symlinks or # do something to handle this gracefully ln -s ${1} ${LINKFILE} return 0 } # hash_dir create hash links in a given directory hash_dir() { printf "Doing ${1}\n" cd ${1} for FILE in * do # no files in directory at all, no point in continuing if ! [ -f ${FILE} ] then return 1 fi if echo ${FILE} | grep -q -E '^[[:xdigit:]]{8}\.r?[[:digit:]]+$' \ && [ -h "${FILE}" ] then rm ${FILE} fi done for FILE in *.pem do # no pem files so FILE gets set to the unexpanded *.pem if ! [ -f ${FILE} ] then break fi check_file ${FILE} local FILE_TYPE=${?} local TYPE_STR='' if [ $(( ${FILE_TYPE} & ${IS_CERT} )) -ne 0 ] then TYPE_STR='x509' elif [ $(( ${FILE_TYPE} & ${IS_CRL} )) -ne 0 ] then TYPE_STR='crl' else printf "WARNING: ${FILE} does not contain a certificate or CRL: skipping\n" >&2 continue fi link_hash ${FILE} ${TYPE_STR} done } # choose the name of an ssl application if [ -n "${OPENSSL}" ] then SSL_CMD=${OPENSSL} else SSL_CMD=openssl OPENSSL=${SSL_CMD} export ${OPENSSL} fi # fix paths PATH=${PATH}:${DIR}/bin export PATH # confirm existance/executability of ssl command if ! [ -x $( which ${SSL_CMD} ) ] then printf "${0}: rehashing skipped ('openssl' program not available)\n" >&2 exit 0 fi # determine which directories to process # XXX: can't handle directories with spaces in names # XXX: ...use \n as dir separator and manipulate IFS? if [ ${#} -gt 0 ] then DIRLIST=${*} elif [ -n "${SSL_CERT_DIR}" ] then DIRLIST=$( echo ${SSL_CERT_DIR} | tr ':' ' ' ) else DIRLIST=${DIR}/certs fi # process directories for CERT_DIR in ${DIRLIST} do if [ -d ${CERT_DIR} -a -w ${CERT_DIR} ] then hash_dir ${CERT_DIR} fi done 8<-----8<-----8<------8<------8<-----8<-----8<------8<------ -- Ben Secrest <[email protected]>