diff options
Diffstat (limited to 'src/share')
-rw-r--r-- | src/share/common | 116 | ||||
-rwxr-xr-x | src/share/keytrans | 44 | ||||
-rw-r--r-- | src/share/m/gen_subkey | 21 | ||||
-rw-r--r-- | src/share/ma/add_certifier | 2 | ||||
-rw-r--r-- | src/share/ma/remove_certifier | 2 | ||||
-rw-r--r-- | src/share/ma/update_users | 3 | ||||
-rw-r--r-- | src/share/mh/add_hostname | 62 | ||||
-rw-r--r-- | src/share/mh/add_name | 71 | ||||
-rw-r--r-- | src/share/mh/add_revoker | 53 | ||||
-rw-r--r-- | src/share/mh/diagnostics | 189 | ||||
-rw-r--r-- | src/share/mh/import_key | 51 | ||||
-rw-r--r-- | src/share/mh/publish_key | 19 | ||||
-rw-r--r-- | src/share/mh/revoke_hostname | 68 | ||||
-rw-r--r-- | src/share/mh/revoke_key | 18 | ||||
-rw-r--r-- | src/share/mh/revoke_name | 72 | ||||
-rw-r--r-- | src/share/mh/set_expire | 38 |
16 files changed, 509 insertions, 320 deletions
diff --git a/src/share/common b/src/share/common index 4aa3f7c..e735319 100644 --- a/src/share/common +++ b/src/share/common @@ -281,7 +281,7 @@ get_gpg_expiration() { keyExpire="$1" - if [ -z "$keyExpire" -a "$PROMPT" = 'true' ]; then + if [ -z "$keyExpire" -a "$PROMPT" != 'false' ]; then cat >&2 <<EOF Please specify how long the key should be valid. 0 = key does not expire @@ -436,6 +436,28 @@ list_users() { fi } +# take one argument, a service name. in response, print a series of +# lines, each with a unique numeric port number that might be +# associated with that service name. (e.g. in: "https", out: "443") +# if nothing is found, print nothing, and return 0. +# +# return 1 if there was an error in the search somehow +get_port_for_service() { + + [[ "$1" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || \ + failure $(printf "This is not a valid service name: '%s'" "$1") + if type getent &>/dev/null ; then + # for linux and FreeBSD systems (getent returns 2 if not found, 0 on success, 1 or 3 on various failures) + (getent services "$service" || if [ "$?" -eq 2 ] ; then true ; else false; fi) | awk '{ print $2 }' | cut -f1 -d/ | sort -u + elif [ -r /etc/services ] ; then + # fall back to /etc/services for systems that don't have getent (MacOS?) + # FIXME: doesn't handle aliases like "null" (or "http"?), which don't show up at the beginning of the line. + awk $(printf '/^%s[[:space:]]/{ print $2 }' "$1") /etc/services | cut -f1 -d/ | sort -u + else + return 1 + fi +} + # return the path to the home directory of a user get_homedir() { local uname=${1:-`whoami`} @@ -530,6 +552,15 @@ gpg2authorized_keys() { ### GPG UTILITIES +# script to determine if gpg version is equal to or greater than specified version +is_gpg_version_greater_equal() { + local gpgVersion=$(gpg --version | head -1 | awk '{ print $3 }') + local latest=$(printf '%s\n%s\n' "$1" "$gpgVersion" \ + | tr '.' ' ' | sort -g -k1 -k2 -k3 \ + | tail -1 | tr ' ' '.') + [[ "$gpgVersion" == "$latest" ]] +} + # retrieve all keys with given user id from keyserver # FIXME: need to figure out how to retrieve all matching keys # (not just first N (5 in this case)) @@ -559,7 +590,7 @@ gpg_fetch_userid() { # userid and key policy checking # the following checks policy on the returned keys # - checks that full key has appropriate valididy (u|f) -# - checks key has specified capability (REQUIRED_*_KEY_CAPABILITY) +# - checks key has specified capability (REQUIRED_KEY_CAPABILITY) # - checks that requested user ID has appropriate validity # (see /usr/share/doc/gnupg/DETAILS.gz) # output is one line for every found key, in the following format: @@ -571,8 +602,6 @@ gpg_fetch_userid() { # # all log output must go to stderr, as stdout is used to pass the # flag:sshKey to the calling function. -# -# expects global variable: "MODE" process_user_id() { local returnCode=0 local userID @@ -593,11 +622,7 @@ process_user_id() { userID="$1" # set the required key capability based on the mode - if [ "$MODE" = 'known_hosts' ] ; then - requiredCapability="$REQUIRED_HOST_KEY_CAPABILITY" - elif [ "$MODE" = 'authorized_keys' ] ; then - requiredCapability="$REQUIRED_USER_KEY_CAPABILITY" - fi + requiredCapability=${REQUIRED_KEY_CAPABILITY:="a"} requiredPubCapability=$(echo "$requiredCapability" | tr "[:lower:]" "[:upper:]") # fetch the user ID if necessary/requested @@ -758,6 +783,59 @@ process_user_id() { # being processed in the key files over "bad" keys (key flag '1') } +# output all valid keys for specified user ID literal +keys_for_userid() { + local userID + local noKey= + local nKeys + local nKeysOK + local ok + local sshKey + local tmpfile + + userID="$1" + + log verbose "processing: $userID" + + nKeys=0 + nKeysOK=0 + + IFS=$'\n' + for line in $(process_user_id "${userID}") ; do + # note that key was found + nKeys=$((nKeys+1)) + + ok=$(echo "$line" | cut -d: -f1) + sshKey=$(echo "$line" | cut -d: -f2) + + if [ -z "$sshKey" ] ; then + continue + fi + + # if key OK, output key to stdout + if [ "$ok" -eq '0' ] ; then + # note that key was found ok + nKeysOK=$((nKeysOK+1)) + + printf '%s\n' "$sshKey" + fi + done + + # if at least one key was found... + if [ "$nKeys" -gt 0 ] ; then + # if ok keys were found, return 0 + if [ "$nKeysOK" -gt 0 ] ; then + return 0 + # else return 2 + else + return 2 + fi + # if no keys were found, return 1 + else + return 1 + fi +} + # process a single host in the known_host file process_host_known_hosts() { local host @@ -770,7 +848,7 @@ process_host_known_hosts() { local tmpfile # set the key processing mode - export MODE='known_hosts' + export REQUIRED_KEY_CAPABILITY="$REQUIRED_HOST_KEY_CAPABILITY" host="$1" userID="ssh://${host}" @@ -954,7 +1032,7 @@ process_uid_authorized_keys() { local sshKey # set the key processing mode - export MODE='authorized_keys' + export REQUIRED_KEY_CAPABILITY="$REQUIRED_USER_KEY_CAPABILITY" userID="$1" @@ -1121,9 +1199,23 @@ process_authorized_user_ids() { # fingerprints, one per line: list_primary_fingerprints() { local fake=$(msmktempdir) - GNUPGHOME="$fake" gpg --no-tty --quiet --import + trap "rm -rf $fake" EXIT + GNUPGHOME="$fake" gpg --no-tty --quiet --import --ignore-time-conflict 2>/dev/null GNUPGHOME="$fake" gpg --with-colons --fingerprint --list-keys | \ awk -F: '/^fpr:/{ print $10 }' + trap - EXIT + rm -rf "$fake" +} + +# takes an OpenPGP key or set of keys on stdin, a fingerprint or other +# key identifier as $1, and outputs the gpg-formatted information for +# the requested keys from the material on stdin +get_cert_info() { + local fake=$(msmktempdir) + trap "rm -rf $fake" EXIT + GNUPGHOME="$fake" gpg --no-tty --quiet --import --ignore-time-conflict 2>/dev/null + GNUPGHOME="$fake" gpg --with-colons --fingerprint --fixed-list-mode --list-keys "$1" + trap - EXIT rm -rf "$fake" } diff --git a/src/share/keytrans b/src/share/keytrans index ae4fb09..255a271 100755 --- a/src/share/keytrans +++ b/src/share/keytrans @@ -722,6 +722,7 @@ sub findkey { my $foundfprstr = Crypt::OpenSSL::Bignum->new_from_bin($foundfpr)->to_hex(); # left-pad with 0's to bring up to full 40-char (160-bit) fingerprint: $foundfprstr = sprintf("%040s", $foundfprstr); + my $matched = 0; # is this a match? if ((!defined($data->{target}->{fpr})) || @@ -731,6 +732,7 @@ sub findkey { } $data->{key} = { 'rsa' => $pubkey, 'timestamp' => $key_timestamp }; + $matched = 1; } if ($tag != $packet_types->{seckey} && @@ -740,7 +742,7 @@ sub findkey { } return; } - if (!defined($data->{key})) { + if (!$matched) { # we don't think the public part of this key matches if ($readbytes < $packetlen) { read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; @@ -810,6 +812,40 @@ sub openpgp2rsa { return $data->{key}->{rsa}; } +sub findkeyfprs { + my $data = shift; + my $instr = shift; + my $tag = shift; + my $packetlen = shift; + + findkey($data, $instr, $tag, $packetlen); + if (defined($data->{key})) { + if (defined($data->{key}->{rsa}) && defined($data->{key}->{timestamp})) { + $data->{keys}->{fingerprint($data->{key}->{rsa}, $data->{key}->{timestamp})} = $data->{key}; + } else { + die "should have found some key here"; + } + undef($data->{key}); + } +}; + +sub getallprimarykeys { + my $instr = shift; + + my $subs = { $packet_types->{pubkey} => \&findkeyfprs, + $packet_types->{seckey} => \&findkeyfprs, + }; + my $data = {target => { } }; + + packetwalk($instr, $subs, $data); + + if (defined $data->{keys}) { + return $data->{keys}; + } else { + return {}; + } +} + sub adduserid { my $instr = shift; my $fpr = shift; @@ -1102,6 +1138,12 @@ for (basename($0)) { }); print $newuid; + } elsif (/^listfprs$/) { + my $instream; + open($instream,'-'); + binmode($instream, ":bytes"); + my $keys = getallprimarykeys($instream); + printf("%s\n", join("\n", map { uc(unpack('H*', $_)) } keys(%{$keys}))); } else { die "Unrecognized subcommand. keytrans subcommands are not a stable interface!\n"; } diff --git a/src/share/m/gen_subkey b/src/share/m/gen_subkey index a90c618..cf1ed0c 100644 --- a/src/share/m/gen_subkey +++ b/src/share/m/gen_subkey @@ -19,6 +19,7 @@ gen_subkey(){ local keyID local editCommands local fifoDir + local keyType # get options while true ; do @@ -43,9 +44,27 @@ Type '$PGRM help' for usage." # check that an authentication subkey does not already exist check_gpg_authentication_subkey "$keyID" + # determine which keyType to use from gpg version + keyType=7 + case $(gpg --version | head -1 | awk '{ print $3 }' | cut -d. -f1) in + 1) + if is_gpg_version_greater_equal 1.4.10 ; then + keyType=8 + fi + ;; + 2) + if is_gpg_version_greater_equal 2.0.13 ; then + keyType=8 + fi + ;; + *) + keyType=8 + ;; + esac + # generate the list of commands that will be passed to edit-key editCommands="addkey -8 +$keyType S E A diff --git a/src/share/ma/add_certifier b/src/share/ma/add_certifier index 1601997..bd38190 100644 --- a/src/share/ma/add_certifier +++ b/src/share/ma/add_certifier @@ -135,7 +135,7 @@ EOF log info "key found:" gpg_sphere "--fingerprint 0x${fingerprint}!" - if [ "$PROMPT" = "true" ] ; then + if [ "$PROMPT" != "false" ] ; then printf "Are you sure you want to add the above key as a certifier\nof users on this system? (Y/n) " >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then diff --git a/src/share/ma/remove_certifier b/src/share/ma/remove_certifier index 79f1cda..51c7ee7 100644 --- a/src/share/ma/remove_certifier +++ b/src/share/ma/remove_certifier @@ -26,7 +26,7 @@ fi # FIXME: should we be doing a fancier list_certifier output here? gpg_core --list-key --fingerprint "0x${keyID}!" || failure -if [ "$PROMPT" = "true" ] ; then +if [ "$PROMPT" != "false" ] ; then printf "Really remove the above listed identity certifier? (Y/n) " >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then diff --git a/src/share/ma/update_users b/src/share/ma/update_users index 31b53bf..0086cd3 100644 --- a/src/share/ma/update_users +++ b/src/share/ma/update_users @@ -27,9 +27,6 @@ else unames=$(list_users) fi -# set mode -MODE="authorized_keys" - # set gnupg home GNUPGHOME="$GNUPGHOME_SPHERE" diff --git a/src/share/mh/add_hostname b/src/share/mh/add_hostname deleted file mode 100644 index c1b32a9..0000000 --- a/src/share/mh/add_hostname +++ /dev/null @@ -1,62 +0,0 @@ -# -*-shell-script-*- -# This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) - -# Monkeysphere host add-hostname subcommand -# -# The monkeysphere scripts are written by: -# Jameson Rollins <jrollins@finestructure.net> -# Jamie McClelland <jm@mayfirst.org> -# Daniel Kahn Gillmor <dkg@fifthhorseman.net> -# -# They are Copyright 2008-2009, and are all released under the GPL, -# version 3 or later. - -# add hostname user ID to server key - -add_hostname() { - -local userID -local fingerprint -local tmpuidMatch -local line -local adduidCommand - -if [ -z "$1" ] ; then - failure "You must specify a hostname to add." -fi - -userID="ssh://${1}" - -# test that the desired user ID does not already exist -find_host_userid "$userID" && \ - failure "Host userID '$userID' already exists." - -if [ "$PROMPT" = "true" ] ; then - printf "The following user ID will be added to the host key:\n %s\nAre you sure you would like to add this user ID? (Y/n) " "$userID" >&2 - read OK; OK=${OK:=Y} - if [ "${OK/y/Y}" != 'Y' ] ; then - failure "User ID not added." - fi -else - log debug "adding user ID without prompting." -fi - -# execute edit-key script -if PEM2OPENPGP_USAGE_FLAGS=authenticate \ - <"$GNUPGHOME_HOST/secring.gpg" \ - "$SYSSHAREDIR/keytrans" adduserid \ - "$HOST_FINGERPRINT" "$userID" | gpg_host --import ; then - gpg_host --check-trustdb - - update_gpg_pub_file - - show_key - - echo - echo "NOTE: User ID added to key, but key not published." - echo "Run '$PGRM publish-key' to publish the new user ID." -else - failure "Problem adding user ID." -fi - -} diff --git a/src/share/mh/add_name b/src/share/mh/add_name new file mode 100644 index 0000000..39ebace --- /dev/null +++ b/src/share/mh/add_name @@ -0,0 +1,71 @@ +# -*-shell-script-*- +# This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) + +# Monkeysphere host add-hostname subcommand +# +# The monkeysphere scripts are written by: +# Jameson Rollins <jrollins@finestructure.net> +# Jamie McClelland <jm@mayfirst.org> +# Daniel Kahn Gillmor <dkg@fifthhorseman.net> +# +# They are Copyright 2008-2010, and are all released under the GPL, +# version 3 or later. + +# add servicename user ID to server key + +add_name() { + +local serviceName +local keyID +local fingerprint +local tmpuidMatch +local line +local adduidCommand + +if [ -z "$1" ] ; then + failure "You must specify a service name to add." +fi +serviceName="$1" +shift + +keyID=$(check_key_input "$@") + +# test that the desired user ID does not already exist +check_key_userid "$keyID" "$serviceName" && \ + failure "Service name '$serviceName' already exists on key '$keyID'." + +# test that a key with that user ID does not already exist +prompt_userid_exists "$serviceName" + +check_service_name "$serviceName" + +if [ "$PROMPT" != "false" ] ; then + printf "The following service name will be added to key '$keyID':\n %s\nAre you sure you would like to add this service name? (Y/n) " "$serviceName" >&2 + read OK; OK=${OK:=Y} + if [ "${OK/y/Y}" != 'Y' ] ; then + failure "Service name not added." + fi +else + log debug "adding service name without prompting." +fi + +# execute edit-key script +if PEM2OPENPGP_USAGE_FLAGS=authenticate \ + <"$GNUPGHOME_HOST/secring.gpg" \ + "$SYSSHAREDIR/keytrans" adduserid "$keyID" "$serviceName" \ + | gpg_host --import ; then + + gpg_host --check-trustdb + + update_pgp_pub_file + + show_key "$keyID" + + echo + echo "NOTE: Service name added to key, but key not published." + echo "Run '$PGRM publish-key' to publish the new service name." +else + failure "Problem adding service name." +fi + +} diff --git a/src/share/mh/add_revoker b/src/share/mh/add_revoker index 89e6fcf..41cf090 100644 --- a/src/share/mh/add_revoker +++ b/src/share/mh/add_revoker @@ -8,24 +8,27 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008, and are all released under the GPL, version 3 -# or later. +# They are Copyright 2008-2010, and are all released under the GPL, +# version 3 or later. # add a revoker to the host key add_revoker() { +local revokerKeyID local keyID local tmpDir local fingerprint local addrevokerCommand -keyID="$1" - # check that key ID or file is specified -if [ -z "$keyID" ] ; then +if [ -z "$1" ] ; then failure "You must specify the key ID of a revoker key, or specify a file to read the key from." fi +revokerKeyID="$1" +shift + +keyID=$(check_key_input "$@") # make a temporary directory for storing keys during import, and set # the trap to delete it on exit @@ -33,33 +36,33 @@ tmpDir=$(msmktempdir) trap "rm -rf $tmpDir" EXIT # if file is specified -if [ -f "$keyID" -o "$keyID" = '-' ] ; then +if [ -f "$revokerKeyID" -o "$revokerKeyID" = '-' ] ; then # load the key from stdin - if [ "$keyID" = '-' ] ; then + if [ "$revokerKeyID" = '-' ] ; then # make a temporary file to hold the key from stdin - keyID="$tmpDir"/importkey - log verbose "reading key from stdin..." - cat > "$keyID" + revokerKeyID="$tmpDir"/importkey + log verbose "reading revoker key from stdin..." + cat > "$revokerKeyID" # load the key from the file - elif [ -f "$keyID" ] ; then - log verbose "reading key from file '$keyID'..." + elif [ -f "$revokerKeyID" ] ; then + log verbose "reading revoker key from file '$revokerKeyID'..." fi # check the key is ok as monkeysphere user before loading log debug "checking keys in file..." fingerprint=$(su_monkeysphere_user \ - ". ${SYSSHAREDIR}/common; list_primary_fingerprints" < "$keyID") + ". ${SYSSHAREDIR}/common; list_primary_fingerprints" < "$revokerKeyID") if [ $(printf "%s" "$fingerprint" | egrep -c '^[A-F0-9]{40}$') -ne 1 ] ; then failure "There was not exactly one gpg key in the file." fi # load the key - gpg_host --import <"$keyID" \ - || failure "could not read key from '$keyID'" + gpg_host --import <"$revokerKeyID" \ + || failure "could not read revoker key from '$revokerKeyID'" -# else, get the key from the keyserver +# else, get the revoker key from the keyserver else # fix permissions and ownership on temporary directory which will # be used by monkeysphere user for storing the downloaded key @@ -67,13 +70,13 @@ else chown "$MONKEYSPHERE_USER":"$MONKEYSPHERE_GROUP" "$tmpDir" # download the key from the keyserver as the monkeysphere user - log verbose "searching keyserver $KEYSERVER for keyID $keyID..." - su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --quiet --keyserver $KEYSERVER --recv-key 0x${keyID}!" \ - || failure "Could not receive a key with this ID from the '$KEYSERVER' keyserver." + log verbose "searching keyserver $KEYSERVER for revoker keyID $revokerKeyID..." + su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --quiet --keyserver $KEYSERVER --recv-key 0x${revokerKeyID}!" \ + || failure "Could not receive a key with this ID from keyserver '$KEYSERVER'." # get the full fingerprint of new revoker key log debug "getting fingerprint of revoker key..." - fingerprint=$(su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --list-key --with-colons --with-fingerprint 0x${keyID}!" \ + fingerprint=$(su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --list-key --with-colons --with-fingerprint ${revokerKeyID}" \ | grep '^fpr:' | cut -d: -f10) # test that there is only a single fingerprint @@ -86,11 +89,11 @@ EOF failure fi - log info "key found:" + log info "revoker key found:" su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --fingerprint 0x${fingerprint}!" if [ "$PROMPT" = "true" ] ; then - printf "Are you sure you want to add the above key as a revoker\nof the host key? (Y/n) " >&2 + printf "Are you sure you want to add the above key as a revoker\nof the key '$keyID'? (Y/n) " >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "revoker not added." @@ -100,7 +103,7 @@ EOF fi # export the new key to the host keyring - log debug "loading key into host keyring..." + log debug "loading revoker key into host keyring..." su_monkeysphere_user "GNUPGHOME=$tmpDir gpg --quiet --export 0x${fingerprint}!" \ | gpg_host --import fi @@ -115,9 +118,9 @@ save # core ltsigns the newly imported revoker key log debug "executing add revoker script..." -if echo "$addrevokerCommand" | gpg_host_edit ; then +if echo "$addrevokerCommand" | gpg_host_edit "0x${keyID}!" ; then - update_gpg_pub_file + update_pgp_pub_file log info "Revoker added." else diff --git a/src/share/mh/diagnostics b/src/share/mh/diagnostics index b92d729..9409f1d 100644 --- a/src/share/mh/diagnostics +++ b/src/share/mh/diagnostics @@ -8,107 +8,88 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008-2009, and are all released under the GPL, +# They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. -# check on the status and validity of the key and public certificates +# check on the status and validity of the host's public certificates (and keys?) -diagnostics() { - -local seckey -local keysfound -local curdate -local warnwindow -local warndate -local create -local expire -local uid -local fingerprint -local badhostkeys -local problemsfound=0 - -if ! [ -d "$SYSDATADIR" ] ; then - echo "! no $SYSDATADIR directory found. Please create it." - exit -fi - -if ! [ -f "$HOST_KEY_FILE" ] ; then - echo "No host key gpg pub file found!" - echo " - Recommendation: run 'monkeysphere-host import-key'" - exit -fi +# global vars for communicating between functions: -# load the host key fingerprint -load_fingerprint - -seckey=$(gpg_host --list-secret-keys --fingerprint --with-colons --fixed-list-mode) -keysfound=$(echo "$seckey" | grep -c ^sec:) -curdate=$(date +%s) +MHD_CURDATE=$(date +%s) # warn when anything is 2 months away from expiration -warnwindow='2 months' -warndate=$(advance_date $warnwindow +%s) - -if ! id monkeysphere >/dev/null ; then - echo "! No monkeysphere user found! Please create a monkeysphere system user with bash as its shell." - problemsfound=$(($problemsfound+1)) -fi +MHD_WARNWINDOW='2 months' +MHD_WARNDATE=$(advance_date $MHD_WARNWINDOW +%s) +MHD_PROBLEMSFOUND=0 + + +diagnose_key() { + local fpr="$1" + local certinfo + local create + local expire + local uid + local keysfound + local uiderrs + local errcount + + printf "Checking OpenPGP Certificate for key 0x%s\n" "$fpr" + + certinfo=$(get_cert_info "0x$fpr" <"$HOST_KEY_FILE") + keysfound=$(grep -c ^pub: <<<"$certinfo") + + if [ "$keysfound" -lt 1 ] ; then + printf "! Could not find key with fingerprint 0x%s\n" "$fpr" + # FIXME: recommend a way to resolve this! + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) + fi -echo "Checking host GPG key..." -if (( "$keysfound" < 1 )); then - echo "! No host key found. The monkeysphere-host data directory is corrupt?!?!" - echo " - Recommendation: purge the MHDATADIR ($MHDATADIR) and rerun 'monkeysphere-host import-key'" - problemsfound=$(($problemsfound+1)) -elif (( "$keysfound" > 1 )); then - echo "! More than one host key found?" - # FIXME: recommend a way to resolve this - problemsfound=$(($problemsfound+1)) -else - create=$(echo "$seckey" | grep ^sec: | cut -f6 -d:) - expire=$(echo "$seckey" | grep ^sec: | cut -f7 -d:) - fingerprint=$(echo "$seckey" | grep ^fpr: | head -n1 | cut -f10 -d:) + create=$(echo "$certinfo" | grep ^pub: | cut -f6 -d:) + expire=$(echo "$certinfo" | grep ^pub: | cut -f7 -d:) # check for key expiration: if [ "$expire" ]; then - if (( "$expire" < "$curdate" )); then - echo "! Host key is expired." - echo " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire'" - problemsfound=$(($problemsfound+1)) - elif (( "$expire" < "$warndate" )); then - echo "! Host key expires in less than $warnwindow:" $(advance_date $(( $expire - $curdate )) seconds +%F) - echo " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire'" - problemsfound=$(($problemsfound+1)) + if (( "$expire" < "$MHD_CURDATE" )); then + printf "! Host key 0x%s is expired.\n" "$fpr" + printf " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire 0x%s'\n" "$fpr" + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) + elif (( "$expire" < "$MHD_WARNDATE" )); then + printf "! Host key 0x%s expires in less than %s: %s\n" "$fpr" "$MHD_WARNWINDOW" $(advance_date $(( $expire - $MHD_CURDATE )) seconds +%F) + printf " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire %s'\n" "$fpr" + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi fi # and weirdnesses: - if [ "$create" ] && (( "$create" > "$curdate" )); then - echo "! Host key was created in the future(?!). Is your clock correct?" - echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?" - problemsfound=$(($problemsfound+1)) + if [ "$create" ] && (( "$create" > "$MHD_CURDATE" )); then + printf "! Host key 0x%s was created in the future(?!): %s. Is your clock correct?\n" "$fpr" $(date -d "1970-01-01 + $create seconds" +%F) + printf " - Recommendation: Check your clock (is it really %s?); use NTP?\n" $(date +%F_%T) + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi # check for UserID expiration: - echo "$seckey" | grep ^uid: | cut -d: -f6,7,10 | \ - while IFS=: read create expire uid ; do - # FIXME: should we be doing any checking on the form - # of the User ID? Should we be unmangling it somehow? - - if [ "$create" ] && (( "$create" > "$curdate" )); then - echo "! User ID '$uid' was created in the future(?!). Is your clock correct?" - echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?" - problemsfound=$(($problemsfound+1)) - fi - if [ "$expire" ] ; then - if (( "$expire" < "$curdate" )); then - echo "! User ID '$uid' is expired." + uiderrs=$(printf '%s\n' "$certinfo" | grep ^uid: | cut -d: -f6,7,10 | \ + while IFS=: read -r create expire uid ; do + uid=$(gpg_unescape <<<"$uid") + + check_service_name "$uid" + if [ "$create" ] && (( "$create" > "$MHD_CURDATE" )); then + printf "! The latest self-sig on User ID '%s' was created in the future(?!): %s.\n - Is your clock correct?\n" "$uid" $(date -d "1970-01-01 + $create seconds" +%F) + printf " - Recommendation: Check your clock (is it really %s ?); use NTP?\n" $(date +%F_%T) + fi + if [ "$expire" ] ; then + if (( "$expire" < "$MHD_CURDATE" )); then + printf "! User ID '%s' is expired.\n" "$uid" # FIXME: recommend a way to resolve this - problemsfound=$(($problemsfound+1)) - elif (( "$expire" < "$warndate" )); then - echo "! User ID '$uid' expires in less than $warnwindow:" $(advance_date $(( $expire - $curdate )) seconds +%F) + elif (( "$expire" < "$MHD_WARNDATE" )); then + printf "! User ID '%s' expires in less than %s: %s\n" "%s" "$MHD_WARNWINDOW" $(advance_date $(( $expire - $MHD_CURDATE )) seconds +%F) # FIXME: recommend a way to resolve this - problemsfound=$(($problemsfound+1)) + fi fi - fi - done + done) + errcount=$(grep -c '^!' <<<"$uiderrs") || \ + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+ $errcount )) + printf '%s\n' "$uiderrs" + + # FIXME: verify that the host key is properly published to the # keyservers (do this with the non-privileged user) @@ -120,11 +101,45 @@ else # FIXME: propose adding a revoker to the host key if none exist (do we # have a way to do that after key generation?) -# FIXME: test (with ssh-keyscan?) that the running ssh -# daemon is actually offering the monkeysphere host key. +# FIXME: test (with ssh-keyscan?) that any running ssh daemon is +# actually offering the monkeysphere host key, if such a key is +# loaded. + +# FIXME: scan /proc/net/tcp and /proc/net/tcp6 to see what +# known-crypto ports (ssh, https, imaps?, ldaps?, etc) are in use +# locally. Propose bringing them into the monkeysphere. + +# FIXME: ensure that the key is of a reasonable size + +# FIXME: ensure that the cert has the right key usage flags + +# FIXME: ensure that the key doesn't match any known blacklist +} + +diagnostics() { + +MHD_PROBLEMSFOUND=0 + + +if ! [ -d "$SYSDATADIR" ] ; then + echo "! no $SYSDATADIR directory found. Please create it." + exit +fi +if ! [ -f "$HOST_KEY_FILE" ] ; then + echo "No host OpenPGP certificates file found!" + echo " - Recommendation: run 'monkeysphere-host import-key' with a service key" + exit fi +if ! id monkeysphere >/dev/null ; then + echo "! No monkeysphere user found! Please create a monkeysphere system user with bash as its shell." + MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) +fi + +echo "Checking host OpenPGP certificates..." +multi_key diagnose_key + # FIXME: look at the ownership/privileges of the various keyrings, # directories housing them, etc (what should those values be? can # we make them as minimal as possible?) @@ -132,8 +147,8 @@ fi # report on any cruft from old monkeysphere version report_cruft -if [ "$problemsfound" -gt 0 ]; then - echo "When the above $problemsfound issue"$(if [ "$problemsfound" -eq 1 ] ; then echo " is" ; else echo "s are" ; fi)" resolved, please re-run:" +if [ "$MHD_PROBLEMSFOUND" -gt 0 ]; then + echo "When the above $MHD_PROBLEMSFOUND issue"$(if [ "$MHD_PROBLEMSFOUND" -eq 1 ] ; then echo " is" ; else echo "s are" ; fi)" resolved, please re-run:" echo " monkeysphere-host diagnostics" else echo "Everything seems to be in order!" diff --git a/src/share/mh/import_key b/src/share/mh/import_key index f7c69c3..0f362b8 100644 --- a/src/share/mh/import_key +++ b/src/share/mh/import_key @@ -8,60 +8,53 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008-2009 and are all released under the GPL, +# They are Copyright 2008-2010 and are all released under the GPL, # version 3 or later. import_key() { -local sshKeyFile -local hostName -local domain -local userID - -sshKeyFile="$1" -hostName="$2" +local keyFile="$1" +local serviceName="$2" # check that key file specified -if [ -z "$sshKeyFile" ] ; then - failure "Must specify ssh key file to import, or specify '-' for stdin." +if [ -z "$keyFile" ] ; then + failure "Must specify PEM-encoded key file to import, or specify '-' for stdin." fi # fail if hostname not specified -if [ -z "$hostName" ] ; then - failure "You must specify a fully-qualified domain name for use in the host certificate user ID." +if [ -z "$serviceName" ] ; then + failure "You must specify a service name for use in the OpenPGP certificate user ID." fi -userID="ssh://${hostName}" +# test that a key with that user ID does not already exist +prompt_userid_exists "$serviceName" + +# check that the service name is well formatted +check_service_name "$serviceName" # create host home mkdir -p "${MHDATADIR}" mkdir -p "${GNUPGHOME_HOST}" chmod 700 "${GNUPGHOME_HOST}" -# import ssh key to a private key -if [ "$sshKeyFile" = '-' ] ; then - log verbose "importing ssh key from stdin..." - PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$userID" \ +# import pem-encoded key to an OpenPGP private key +if [ "$keyFile" = '-' ] ; then + log verbose "importing key from stdin..." + PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \ | gpg_host --import else - log verbose "importing ssh key from file '$sshKeyFile'..." - PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$userID" \ - <"$sshKeyFile" \ + log verbose "importing key from file '$keyFile'..." + PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \ + <"$keyFile" \ | gpg_host --import fi -# load the new host fpr into the fpr variable. this is so we can -# create the gpg pub key file. we have to do this from the secret key -# ring since we obviously don't have the gpg pub key file yet, since -# that's what we're trying to produce (see below). -load_fingerprint_secret - -# export to gpg public key to file -update_gpg_pub_file +# export to OpenPGP public key to file +update_pgp_pub_file log info "host key imported:" # show info about new key -show_key +show_key "$serviceName" } diff --git a/src/share/mh/publish_key b/src/share/mh/publish_key index 48e4cbb..f1c1723 100644 --- a/src/share/mh/publish_key +++ b/src/share/mh/publish_key @@ -8,23 +8,24 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008-2009, and are all released under the GPL, version 3 -# or later. +# They are Copyright 2008-2010, and are all released under the GPL, +# version 3 or later. -# publish server key to keyserver +# publish keys to keyserver publish_key() { +local keyID="$1" local GNUPGHOME -if [ "$PROMPT" = "true" ] ; then - printf "Really publish host key to $KEYSERVER? (Y/n) " >&2 +if [ "$PROMPT" != "false" ] ; then + printf "Really publish key '$keyID' to $KEYSERVER? (Y/n) " >&2 read OK; OK=${OK:=Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "key not published." fi else - log debug "publishing key without prompting." + log debug "publishing key '$keyID' without prompting." fi # create a temporary gnupg directory from which to publish the key @@ -35,13 +36,13 @@ chown "$MONKEYSPHERE_USER":"$MONKEYSPHERE_GROUP" "$GNUPGHOME" # trap to remove tmp dir if break trap "rm -rf $GNUPGHOME" EXIT -# import the host key into the tmp dir +# import the key into the tmp dir su_monkeysphere_user \ "gpg --quiet --import" <"$HOST_KEY_FILE" -# publish host key +# publish key su_monkeysphere_user \ - "gpg --keyserver $KEYSERVER --send-keys '0x${HOST_FINGERPRINT}!'" + "gpg --keyserver $KEYSERVER --send-keys '0x${keyID}!'" # remove the tmp file trap - EXIT diff --git a/src/share/mh/revoke_hostname b/src/share/mh/revoke_hostname deleted file mode 100644 index 6b80802..0000000 --- a/src/share/mh/revoke_hostname +++ /dev/null @@ -1,68 +0,0 @@ -# -*-shell-script-*- -# This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) - -# Monkeysphere host revoke-hostname subcommand -# -# The monkeysphere scripts are written by: -# Jameson Rollins <jrollins@finestructure.net> -# Jamie McClelland <jm@mayfirst.org> -# Daniel Kahn Gillmor <dkg@fifthhorseman.net> -# -# They are Copyright 2008-2009, and are all released under the GPL, -# version 3 or later. - -# revoke hostname user ID from host key - -revoke_hostname() { - -local userID -local fingerprint -local tmpuidMatch -local line -local message -local revuidCommand - -if [ -z "$1" ] ; then - failure "You must specify a hostname to revoke." -fi - -userID="ssh://${1}" - -# make sure the user ID to revoke -find_host_userid "$userID" || \ - failure "No non-revoked user ID found matching '$userID'." - -if [ "$PROMPT" = "true" ] ; then - printf "The following host key user ID will be revoked:\n %s\nAre you sure you would like to revoke this user ID? (Y/n) " "$userID" >&2 - read OK; OK=${OK:=Y} - if [ "${OK/y/Y}" != 'Y' ] ; then - failure "User ID not revoked." - fi -else - log debug "revoking user ID without prompting." -fi - -# actually revoke: - -# the gpg secring might not contain the host key we are trying to -# revoke (let alone any selfsig over that host key), but the plain -# --export won't contain the secret key. "keytrans revokeuserid" -# needs access to both pieces, so we feed it both of them. - -if (cat "$GNUPGHOME_HOST/secring.gpg" && gpg_host --export "$HOST_FINGERPRINT") | \ - "$SYSSHAREDIR/keytrans" revokeuserid \ - "$HOST_FINGERPRINT" "$userID" | gpg_host --import ; then - gpg_host --check-trustdb - - update_gpg_pub_file - - show_key - - echo - echo "NOTE: User ID revoked, but revocation not published." - echo "Run '$PGRM publish-key' to publish the revocation." -else - failure "Problem revoking user ID." -fi - -} diff --git a/src/share/mh/revoke_key b/src/share/mh/revoke_key index 5460e51..5a013e0 100644 --- a/src/share/mh/revoke_key +++ b/src/share/mh/revoke_key @@ -8,23 +8,24 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008-2009, and are all released under the GPL, +# They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. # revoke host key revoke_key() { -# Coming in here, we expect $HOST_FINGERPRINT to be set, and we -# believe that there is in fact a key. + local keyID + local publish + + keyID=$(check_key_input "$@") if [ "$PROMPT" = "false" ] ; then publish=N else cat <<EOF >&2 -This will generate a revocation certificate for your host key -(fingerprint: $HOST_FINGERPRINT) and -dump the certificate to standard output. +This will generate a revocation certificate for key $keyID +and dump the certificate to standard output. It can also directly publish the new revocation certificate to the public keyservers via $KEYSERVER if you want it to. @@ -65,14 +66,13 @@ Monkeysphere host key revocation (automated) $(date '+%F_%T%z') y " - revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg_host --command-fd 0 --armor --gen-revoke "0x${HOST_FINGERPRINT}!" <<<"$revoke_commands" ) \ + revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg_host --command-fd 0 --armor --gen-revoke "0x${keyID}!" <<<"$revoke_commands" ) \ || failure "Failed to generate revocation certificate!" - else # note: we're not using the gpg_host function because we actually # want to use gpg's UI in this case, so we want to omit --no-tty - revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg --no-greeting --quiet --armor --gen-revoke "0x${HOST_FINGERPRINT}!") \ + revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg --no-greeting --quiet --armor --gen-revoke "0x${keyID}!") \ || failure "Failed to generate revocation certificate!" fi diff --git a/src/share/mh/revoke_name b/src/share/mh/revoke_name new file mode 100644 index 0000000..532cb30 --- /dev/null +++ b/src/share/mh/revoke_name @@ -0,0 +1,72 @@ +# -*-shell-script-*- +# This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) + +# Monkeysphere host revoke-hostname subcommand +# +# The monkeysphere scripts are written by: +# Jameson Rollins <jrollins@finestructure.net> +# Jamie McClelland <jm@mayfirst.org> +# Daniel Kahn Gillmor <dkg@fifthhorseman.net> +# +# They are Copyright 2008-2010, and are all released under the GPL, +# version 3 or later. + +# revoke service name user ID from host key + +revoke_name() { + +local serviceName +local keyID +local fingerprint +local tmpuidMatch +local line +local message +local revuidCommand + +if [ -z "$1" ] ; then + failure "You must specify a service name to revoke." +fi +serviceName="$1" +shift + +keyID=$(check_key_input "$@") + +# make sure the user ID to revoke exists +check_key_userid "$keyID" "$serviceName" || \ + failure "No non-revoked service name found matching '$serviceName'." + +if [ "$PROMPT" != "false" ] ; then + printf "The following service name on key '$keyID' will be revoked:\n %s\nAre you sure you would like to revoke this service name? (Y/n) " "$serviceName" >&2 + read OK; OK=${OK:=Y} + if [ "${OK/y/Y}" != 'Y' ] ; then + failure "User ID not revoked." + fi +else + log debug "revoking service name without prompting." +fi + +# actually revoke: + +# the gpg secring might not contain the host key we are trying to +# revoke (let alone any selfsig over that host key), but the plain +# --export won't contain the secret key. "keytrans revokeuserid" +# needs access to both pieces, so we feed it both of them. + +if (cat "$GNUPGHOME_HOST/secring.gpg" && gpg_host --export "$keyID") \ + | "$SYSSHAREDIR/keytrans" revokeuserid "$keyID" "$serviceName" \ + | gpg_host --import ; then + + gpg_host --check-trustdb + + update_pgp_pub_file + + show_key "$keyID" + + echo + echo "NOTE: Service name revoked, but revocation not published." + echo "Run '$PGRM publish-key' to publish the revocation." +else + failure "Problem revoking service name." +fi + +} diff --git a/src/share/mh/set_expire b/src/share/mh/set_expire index 9889e76..68a8dfd 100644 --- a/src/share/mh/set_expire +++ b/src/share/mh/set_expire @@ -11,18 +11,32 @@ # Jamie McClelland <jm@mayfirst.org> # Daniel Kahn Gillmor <dkg@fifthhorseman.net> # -# They are Copyright 2008-2009, and are all released under the GPL, +# They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. set_expire() { -local extendTo +local extendBy +local keyID + +if [ -z "$1" ] ; then + cat <<EOF >&2 +Must specify expiration. The possibilities are: + 0 = key does not expire + <n> = key expires in n days + <n>w = key expires in n weeks + <n>m = key expires in n months + <n>y = key expires in n years +EOF + failure +fi +extendBy="$1" +shift -# get the new expiration date -extendTo=$(get_gpg_expiration "$1") +keyID=$(check_key_input "$@") -if [ "$PROMPT" = "true" ] ; then - printf "Are you sure you want to change the expiration on the host key to '%s'? (Y/n) " "$extendTo" >&2 +if [ "$PROMPT" != "false" ] ; then + printf "Are you sure you want to change the expiration on key '$keyID' by '%s'? (Y/n) " "$extendBy" >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "expiration not set." @@ -31,18 +45,18 @@ else log debug "extending without prompting." fi -log info "setting host key expiration to ${extendTo}." +log info "setting key expiration to ${extendBy}." -log debug "executing host expire script..." -gpg_host_edit expire <<EOF -$extendTo +log debug "executing key expire script..." +gpg_host_edit "0x${keyID}!" expire <<EOF +$extendBy save EOF -update_gpg_pub_file +update_pgp_pub_file log info <<EOF -NOTE: Host key expiration date adjusted, but not yet published. +NOTE: Key expiration date adjusted, but not yet published. Run '$PGRM publish-key' to publish the new expiration date. EOF |