doc/documentation/tools/checkkey.sh
Sergio Carlavilla Delgado 989d921f5d Migrate doc to Hugo/AsciiDoctor
I'm very pleased to announce the release of
our new website and documentation using
the new toolchain with Hugo and AsciiDoctor.

To get more information about the new toolchain
please read the FreeBSD Documentation Project Primer[1],
Hugo docs[2] and AsciiDoctor docs[3].

Acknowledgment:
Benedict Reuschling <bcr@>
Glen Barber <gjb@>
Hiroki Sato <hrs@>
Li-Wen Hsu <lwhsu@>
Sean Chittenden <seanc@>
The FreeBSD Foundation

[1] https://docs.FreeBSD.org/en/books/fdp-primer/
[2] https://gohugo.io/documentation/
[3] https://docs.asciidoctor.org/home/

Approved by:    doceng, core
2021-01-26 00:31:29 +01:00

264 lines
6.5 KiB
Bash
Executable file

#!/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
# <https://riseup.net/en/security/message-security/openpgp/gpg-best-practices#openpgp-key-checks>
#
# 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}