Index: head/share/pgpkeys/checkkey.sh =================================================================== --- head/share/pgpkeys/checkkey.sh (revision 49298) +++ head/share/pgpkeys/checkkey.sh (revision 49299) @@ -1,261 +1,264 @@ #!/bin/sh # # $FreeBSD$ # # # This script is intended to sanity-check PGP keys used by folks with # @FreeBSD.org email addresses. The checks are intended to be derived # from the information at # # +# make sure we are in a well known language +LANG=C + \unalias -a progname=${0##*/} # Print an informational message info() { echo "$@" >&2 } # Print a warning message warning() { echo "WARNING: $@" >&2 } # Print an error message and exit error() { echo "ERROR: $@" >&2 exit 1 } # Print usage message and exit usage() { echo "usage: ${progname} [user] [keyid ...]\n" >&2 exit 1 } # Look for gpg gpg=$(which gpg) if [ $? -gt 0 -o -z "${gpg}" -o ! -x "${gpg}" ] ; then error "Cannot find gpg" fi # Set up our internal default gpg invocation options _gpg() { ${gpg} \ --display-charset utf-8 \ --no-greeting \ --no-secmem-warning \ --keyid-format long \ --list-options no-show-uid-validity \ "$@" } # Look up key by key ID getkeybyid() { _gpg --with-colons --list-keys "$1" 2>/dev/null | awk -F: \ '$5 ~ /^\([0-9A-F]{8}\)?'"$1"'$/i && $12 ~ /ESC/ { print $5 }' } # Look up key by email getkeybyemail() { _gpg --with-colons --list-keys "$1" 2>/dev/null | awk -F: \ '$10 ~ /<'"$1"'>/i && $12 ~ /ESC/ { print $5 }' } # The first command-line argument can be a user name or a key ID. if [ $# -gt 0 ] ; then id="$1" shift else id=$(id -nu) warning "No argument specified, calculating user ID" fi # Now let's try to figure out what kind of thing we have as an ID. # We'll check for a keyid first, as it's readily distinguishable # from other things, but if we see that we have one, we push it back # onto the argument list for later processing (becasue we may have # been given a list of keyods). if echo "${id}" | egrep -q '^[0-9A-F]{16}$'; then id_type="keyid" set -- "${id}" $@ elif echo "${id}" | egrep -q '^[0-9A-F]{8}$'; then id_type="keyid" set -- "${id}" $@ elif echo "${id}" | egrep -iq '^[a-z][-0-9a-z_]*@([-0-9a-z]+\.)[-0-9a-z]+$'; then id_type="email" email="${id}" elif echo "${id}" | egrep -iq '^[a-z][-0-9a-z_]*$'; then id_type="login" login="${id}" email="${id}@FreeBSD.org" else error "Cannot recognize type of ${id} (keyid, login, or email)" fi if [ $# -ne 0 ] ; then # Verify the keys that were specified on the command line for arg ; do case $(expr "${arg}" : '^[0-9A-Fa-f]\{8,16\}$') in 8) warning "${arg}: recommend using 16-digit keyid" ;& 16) keyid=$(getkeybyid "${arg}") if [ -n "${keyid}" ] ; then keyids="${keyids} ${keyid}" else warning "${arg} not found" fi ;; *) warning "${arg} does not appear to be a valid key ID" ;; esac shift done else # Search for keys by freebsd.org email keyids=$(getkeybyemail "${email}") case $(echo "${keyids}" | wc -w) in 0) error "no keys found for ${email}" ;; 1) ;; *) warning "Multiple keys found for <${email}>; checking all." warning "If this is not what you want, specify a key ID" \ "on the command line." ;; esac fi # :( if [ -z "${keyids}" ] ; then error "no valid keys were found" fi # add a problem report to the list of problems with this key badkey() { key_problems=" ${key_problems}$@ " } exitstatus=0 # Check the keys for key in ${keyids} ; do # no problems found yet key_problems="" IFS_save="${IFS}" key_info=$( ${gpg} --no-secmem-warning --export-options export-minimal --export ${key} \ | ${gpg} --no-secmem-warning --list-packets ) # primary keys should be RSA or DSA-2 IFS="" version=$( echo $key_info | \ awk '$1 == "version" && $3 == "algo" {sub(",", "", $2); print $2; exit 0}' ) IFS="${IFS_save}" if [ $version -lt 4 ]; then badkey "This key is a deprecated version $version key!" fi IFS="" algonum=$( echo $key_info | \ awk '$1 == "version" && $3 == "algo" {sub(",", "", $4); print $4; exit 0}' ) IFS="${IFS_save}" case ${algonum} in "1") algo="RSA" ;; "17") algo="DSA" ;; "18") algo="ECC" ;; "19") algo="ECDSA" ;; *) algo="*UNKNOWN*" ;; esac IFS="" bitlen=$( echo $key_info | \ awk -F : '$1 ~ "pkey" { gsub("[^0-9]*","", $2); print $2; exit 0}' ) IFS="${IFS_save}" echo "key ${key}: ${algo}, ${bitlen} bits" case ${algo} in RSA) ;; DSA) if [ "${bitlen}" -le 1024 ]; then \ badkey "DSA, but not DSA-2"; \ fi ;; *) badkey "non-preferred algorithm" esac # self-signatures must not use MD5 or SHA1 IFS="" sig_algonum=$( echo $key_info | \ awk '$1 == "digest" && $2 == "algo" {sub(",", "", $3); print $3; exit 0}' ) IFS="${IFS_save}" case sig_algonum in 1) sigs="MD5";; 2) sigs="SHA1";; 3) sigs="RIPEMD160";; 8) sigs="SHA256";; 9) sigs="SHA384";; 10) sigs="SHA512";; 11) sigs="SHA224";; *) esac for sig in ${sigs}; do if [ "${sig}" = "MD5" -o "${sig}" = "SHA1" ]; then badkey "self-signature ${sig}" fi done # digest algo pref must include at least one member of SHA-2 # at a higher priority than both MD5 and SHA1 IFS="" algopref=$( echo $key_info | \ awk -F : '$1 ~ "pref-hash-algos" {gsub("[^ 0-9]", "", $2); print $2; exit 0}' ) IFS="${IFS_save}" # if 3, 2, or 1 are before 11, 10, 9, or 8, then set -- ${algopref} if [ $1 -lt 4 ]; then badkey "algorithm prefs do not have SHA-2 higher than MD5 or SHA1" fi # primary keys should have an expiration date at least a year # in the future to make them worth committing, but no more # than three years in the future expires=$( _gpg --list-keys ${key} | \ awk "/$keyid .*expires:/ {sub(\"[^-0-9]\", \"\", \$NF); print \$NF; exit 0}" ) if [ -z "${expires}" ]; then badkey "this key does not expire" else expires_s=$( date -jf "%F" "+%s" "${expires}" ) now_s=$( date "+%s" ) # 86400 == # seconds in a normal day expire_int_d=$(( ( ${expires_s} - ${now_s} ) / 86400 )) exp_min=$(( 1 * 365 )) # Min expiry time is 1 year exp_max=$(( 3 * 365 + 1 )) # Max expiry time is 3 years # We add 1 day because in a 3-year # period, probability of a leap day # is 297/400, about 0.74250 if [ ${expire_int_d} -lt ${exp_min} ]; then badkey "Key $key expires in less than 1 year ($expire_int_d days)" fi if [ ${expire_int_d} -gt ${exp_max} ]; then badkey "Key $key expires in more than 3 years ($expire_int_d days)" fi fi # report problems if [ -z "${key_problems}" ]; then echo " key okay, ${key} meets minimal requirements" >&2 else exitstatus=1 echo " ** problems found:" >&2 echo "${key_problems}" >&2 echo " ** key ${key} should not be used!" fi echo done exit ${exitstatus}