#!/usr/bin/env bash # monkeysphere-server: MonkeySphere server admin tool # # The monkeysphere scripts are written by: # Jameson Rollins <jrollins@fifthhorseman.net> # 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. ######################################################################## PGRM=$(basename $0) SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"/usr/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/common" || exit 1 SYSDATADIR=${MONKEYSPHERE_SYSDATADIR:-"/var/lib/monkeysphere"} export SYSDATADIR # UTC date in ISO 8601 format if needed DATE=$(date -u '+%FT%T') # unset some environment variables that could screw things up unset GREP_OPTIONS # default return code RETURN=0 ######################################################################## # FUNCTIONS ######################################################################## usage() { cat <<EOF >&2 usage: $PGRM <subcommand> [options] [args] Monkeysphere server admin tool. subcommands: update-users (u) [USER]... update user authorized_keys files gen-key (g) [NAME[:PORT]] generate gpg key for the server --length (-l) BITS key length in bits (2048) --expire (-e) EXPIRE date to expire --revoker (-r) FINGERPRINT add a revoker extend-key (e) EXPIRE extend expiration to EXPIRE add-hostname (n+) NAME[:PORT] add hostname user ID to server key revoke-hostname (n-) NAME[:PORT] revoke hostname user ID show-key (s) output all server host key information publish-key (p) publish server host key to keyserver diagnostics (d) report on server monkeysphere status add-id-certifier (c+) KEYID import and tsign a certification key --domain (-n) DOMAIN limit ID certifications to DOMAIN --trust (-t) TRUST trust level of certifier (full) --depth (-d) DEPTH trust depth for certifier (1) remove-id-certifier (c-) KEYID remove a certification key list-id-certifiers (c) list certification keys gpg-authentication-cmd CMD gnupg-authentication command help (h,?) this help EOF } # function to run command as monkeysphere user su_monkeysphere_user() { # if the current user is the monkeysphere user, then just eval # command if [ $(id -un) = "$MONKEYSPHERE_USER" ] ; then eval "$@" # otherwise su command as monkeysphere user else su "$MONKEYSPHERE_USER" -c "$@" fi } # function to interact with the host gnupg keyring gpg_host() { local returnCode GNUPGHOME="$GNUPGHOME_HOST" export GNUPGHOME # NOTE: we supress this warning because we need the monkeysphere # user to be able to read the host pubring. we realize this might # be problematic, but it's the simplest solution, without too much # loss of security. gpg --no-permission-warning "$@" returnCode="$?" # always reset the permissions on the host pubring so that the # monkeysphere user can read the trust signatures chgrp "$MONKEYSPHERE_USER" "${GNUPGHOME_HOST}/pubring.gpg" chmod g+r "${GNUPGHOME_HOST}/pubring.gpg" return "$returnCode" } # function to interact with the authentication gnupg keyring # FIXME: this function requires basically accepts only a single # argument because of problems with quote expansion. this needs to be # fixed/improved. gpg_authentication() { GNUPGHOME="$GNUPGHOME_AUTHENTICATION" export GNUPGHOME su_monkeysphere_user "gpg $@" } # output just key fingerprint fingerprint_server_key() { gpg_host --list-secret-keys --fingerprint \ --with-colons --fixed-list-mode 2> /dev/null | \ grep '^fpr:' | head -1 | cut -d: -f10 } # output key information show_server_key() { local fingerprint local tmpkey fingerprint=$(fingerprint_server_key) gpg_authentication "--fingerprint --list-key --list-options show-unusable-uids $fingerprint" # dumping to a file named ' ' so that the ssh-keygen output # doesn't claim any potentially bogus hostname(s): tmpkey=$(mktemp -d ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX) gpg_authentication "--export $fingerprint" | openpgp2ssh "$fingerprint" 2>/dev/null > "$tmpkey/ " echo -n "ssh fingerprint: " (cd "$tmpkey" && ssh-keygen -l -f ' ' | awk '{ print $2 }') rm -rf "$tmpkey" echo -n "OpenPGP fingerprint: " echo "$fingerprint" } # update authorized_keys for users update_users() { if [ "$1" ] ; then # get users from command line unames="$@" else # or just look at all users if none specified unames=$(getent passwd | cut -d: -f1) fi # set mode MODE="authorized_keys" # set gnupg home GNUPGHOME="$GNUPGHOME_AUTHENTICATION" # check to see if the gpg trust database has been initialized if [ ! -s "${GNUPGHOME}/trustdb.gpg" ] ; then failure "GNUPG trust database uninitialized. Please see MONKEYSPHERE-SERVER(8)." fi # make sure the authorized_keys directory exists mkdir -p "${SYSDATADIR}/authorized_keys" # loop over users for uname in $unames ; do # check all specified users exist if ! getent passwd "$uname" >/dev/null ; then log error "----- unknown user '$uname' -----" continue fi log verbose "----- user: $uname -----" # make temporary directory TMPLOC=$(mktemp -d ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX) # trap to delete temporary directory on exit trap "rm -rf $TMPLOC" EXIT # create temporary authorized_user_ids file TMP_AUTHORIZED_USER_IDS="${TMPLOC}/authorized_user_ids" touch "$TMP_AUTHORIZED_USER_IDS" # create temporary authorized_keys file AUTHORIZED_KEYS="${TMPLOC}/authorized_keys" touch "$AUTHORIZED_KEYS" # set restrictive permissions on the temporary files # FIXME: is there a better way to do this? chmod 0700 "$TMPLOC" chmod 0600 "$AUTHORIZED_KEYS" chmod 0600 "$TMP_AUTHORIZED_USER_IDS" chown -R "$MONKEYSPHERE_USER" "$TMPLOC" # process authorized_user_ids file # translating ssh-style path variables authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS") if [ -s "$authorizedUserIDs" ] ; then # check permissions on the authorized_user_ids file path if check_key_file_permissions "$uname" "$authorizedUserIDs" ; then # copy user authorized_user_ids file to temporary # location cat "$authorizedUserIDs" > "$TMP_AUTHORIZED_USER_IDS" # export needed variables export AUTHORIZED_KEYS export TMP_AUTHORIZED_USER_IDS # process authorized_user_ids file, as monkeysphere # user su_monkeysphere_user \ ". ${SYSSHAREDIR}/common; process_authorized_user_ids $TMP_AUTHORIZED_USER_IDS" RETURN="$?" else log error "Improper permissions on path '$AUTHORIZED_USER_IDS'." fi fi # add user-controlled authorized_keys file if specified # translate ssh-style path variables rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS") if [ "$rawAuthorizedKeys" -a -s "$rawAuthorizedKeys" ] ; then # check permissions on the authorized_keys file path if check_key_file_permissions "$uname" "$rawAuthorizedKeys" ; then log verbose "adding raw authorized_keys file... " cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS" else log error "Improper permissions on path '$RAW_AUTHORIZED_KEYS'. Not added to authorized_keys file." fi fi # move the new authorized_keys file into place if [ -s "$AUTHORIZED_KEYS" ] ; then # openssh appears to check the contents of the # authorized_keys file as the user in question, so the # file must be readable by that user at least. # FIXME: is there a better way to do this? chown root "$AUTHORIZED_KEYS" chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS" chmod g+r "$AUTHORIZED_KEYS" mv -f "$AUTHORIZED_KEYS" "${SYSDATADIR}/authorized_keys/${uname}" else rm -f "${SYSDATADIR}/authorized_keys/${uname}" fi # unset the trap trap - EXIT # destroy temporary directory rm -rf "$TMPLOC" done } # generate server gpg key gen_key() { local keyType local keyLength local keyUsage local keyExpire local revoker local hostName local userID local keyParameters local fingerprint # set default key parameter values keyType="RSA" keyLength="2048" keyUsage="auth" keyExpire= revoker= # get options TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o e:l:r -l expire:,length:,revoker: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" if [ $? != 0 ] ; then exit 1 fi # Note the quotes around `$TEMP': they are essential! eval set -- "$TEMP" while true ; do case "$1" in -l|--length) keyLength="$2" shift 2 ;; -e|--expire) keyExpire="$2" shift 2 ;; -r|--revoker) revoker="$2" shift 2 ;; --) shift ;; *) break ;; esac done hostName=${1:-$(hostname -f)} userID="ssh://${hostName}" # check for presense of key with user ID if gpg_host --list-key ="$userID" > /dev/null 2>&1 ; then failure "Key for '$userID' already exists" fi # prompt about key expiration if not specified keyExpire=$(get_gpg_expiration "$keyExpire") # set key parameters keyParameters=$(cat <<EOF Key-Type: $keyType Key-Length: $keyLength Key-Usage: $keyUsage Name-Real: $userID Expire-Date: $keyExpire EOF ) # add the revoker field if specified # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key. # FIXME: key is marked "sensitive"? is this appropriate? if [ "$revoker" ] ; then keyParameters="${keyParameters}"$(cat <<EOF Revoker: 1:$revoker sensitive EOF ) fi echo "The following key parameters will be used for the host private key:" echo "$keyParameters" read -p "Generate key? (Y/n) " OK; OK=${OK:=Y} if [ ${OK/y/Y} != 'Y' ] ; then failure "aborting." fi # add commit command keyParameters="${keyParameters}"$(cat <<EOF %commit %echo done EOF ) log verbose "generating server key..." echo "$keyParameters" | gpg_host --batch --gen-key # output the server fingerprint fingerprint_server_key "=${userID}" # find the key fingerprint of the newly generated key fingerprint=$(fingerprint_server_key) # export host ownertrust to authentication keyring log verbose "setting ultimate owner trust for server key..." echo "${fingerprint}:6:" | gpg_authentication "--import-ownertrust" # translate the private key to ssh format, and export to a file # for sshs usage. # NOTE: assumes that the primary key is the proper key to use (umask 077 && \ gpg_host --export-secret-key "$fingerprint" | \ openpgp2ssh "$fingerprint" > "${SYSDATADIR}/ssh_host_rsa_key") log info "Private SSH host key output to file: ${SYSDATADIR}/ssh_host_rsa_key" } # extend the lifetime of a host key: extend_key() { local fpr=$(fingerprint_server_key) local extendTo="$1" if [ -z "$fpr" ] ; then failure "You don't appear to have a MonkeySphere host key on this server. Try 'monkeysphere-server gen-key' first." fi # get the new expiration date extendTo=$(get_gpg_expiration "$extendTo") gpg_host --quiet --command-fd 0 --edit-key "$fpr" <<EOF expire $extendTo save EOF echo echo "NOTE: Host key expiration date adjusted, but not yet published." echo "Run '$PGRM publish-key' to publish the new expiration date." } # 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}" fingerprint=$(fingerprint_server_key) # match to only ultimately trusted user IDs tmpuidMatch="u:$(echo $userID | gpg_escape)" # find the index of the requsted user ID # NOTE: this is based on circumstantial evidence that the order of # this output is the appropriate index if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \ | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then failure "Host userID '$userID' already exists." fi echo "The following user ID will be added to the host key:" echo " $userID" read -p "Are you sure you would like to add this user ID? (y/N) " OK; OK=${OK:=N} if [ ${OK/y/Y} != 'Y' ] ; then failure "User ID not added." fi # edit-key script command to add user ID adduidCommand=$(cat <<EOF adduid $userID save EOF ) # execute edit-key script if echo "$adduidCommand" | \ gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then # update the trustdb for the authentication keyring gpg_authentication "--check-trustdb" show_server_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 } # revoke hostname user ID to server key revoke_hostname() { local userID local fingerprint local tmpuidMatch local line local uidIndex local message local revuidCommand if [ -z "$1" ] ; then failure "You must specify a hostname to revoke." fi echo "WARNING: There is a known bug in this function." echo "This function has been known to occasionally revoke the wrong user ID." echo "Please see the following bug report for more information:" echo "http://web.monkeysphere.info/bugs/revoke-hostname-revoking-wrong-userid/" read -p "Are you sure you would like to proceed? (y/N) " OK; OK=${OK:=N} if [ ${OK/y/Y} != 'Y' ] ; then failure "aborting." fi userID="ssh://${1}" fingerprint=$(fingerprint_server_key) # match to only ultimately trusted user IDs tmpuidMatch="u:$(echo $userID | gpg_escape)" # find the index of the requsted user ID # NOTE: this is based on circumstantial evidence that the order of # this output is the appropriate index if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \ | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then uidIndex=${line%%:*} else failure "No non-revoked user ID '$userID' is found." fi echo "The following host key user ID will be revoked:" echo " $userID" read -p "Are you sure you would like to revoke this user ID? (y/N) " OK; OK=${OK:=N} if [ ${OK/y/Y} != 'Y' ] ; then failure "User ID not revoked." fi message="Hostname removed by monkeysphere-server $DATE" # edit-key script command to revoke user ID revuidCommand=$(cat <<EOF $uidIndex revuid y 4 $message y save EOF ) # execute edit-key script if echo "$revuidCommand" | \ gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then # update the trustdb for the authentication keyring gpg_authentication "--check-trustdb" show_server_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 } # publish server key to keyserver publish_server_key() { read -p "Really publish host key to $KEYSERVER? (y/N) " OK; OK=${OK:=N} if [ ${OK/y/Y} != 'Y' ] ; then failure "key not published." fi # find the key fingerprint fingerprint=$(fingerprint_server_key) # publish host key gpg_authentication "--keyserver $KEYSERVER --send-keys '0x${fingerprint}!'" } diagnostics() { # * check on the status and validity of the key and public certificates local seckey local keysfound local curdate local warnwindow local warndate local create local expire local uid local fingerprint local badhostkeys local sshd_config local problemsfound=0 # FIXME: what's the correct, cross-platform answer? sshd_config=/etc/ssh/sshd_config seckey=$(gpg_host --list-secret-keys --fingerprint --with-colons --fixed-list-mode) keysfound=$(echo "$seckey" | grep -c ^sec:) 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 if ! [ -d "$SYSDATADIR" ] ; then echo "! no $SYSDATADIR directory found. Please create it." problemsfound=$(($problemsfound+1)) fi echo "Checking host GPG key..." if (( "$keysfound" < 1 )); then echo "! No host key found." echo " - Recommendation: run 'monkeysphere-server gen-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:) # check for key expiration: if [ "$expire" ]; then if (( "$expire" < "$curdate" )); then echo "! Host key is expired." echo " - Recommendation: extend lifetime of key with 'monkeysphere-server extend-key'" 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-server extend-key'" problemsfound=$(($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)) 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." # 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) # FIXME: recommend a way to resolve this problemsfound=$(($problemsfound+1)) fi fi done # FIXME: verify that the host key is properly published to the # keyservers (do this with the non-privileged user) # FIXME: check that there are valid, non-expired certifying signatures # attached to the host key after fetching from the public keyserver # (do this with the non-privileged user as well) # FIXME: propose adding a revoker to the host key if none exist (do we # have a way to do that after key generation?) # Ensure that the ssh_host_rsa_key file is present and non-empty: echo echo "Checking host SSH key..." if [ ! -s "${SYSDATADIR}/ssh_host_rsa_key" ] ; then echo "! The host key as prepared for SSH (${SYSDATADIR}/ssh_host_rsa_key) is missing or empty." problemsfound=$(($problemsfound+1)) else if [ $(ls -l "${SYSDATADIR}/ssh_host_rsa_key" | cut -f1 -d\ ) != '-rw-------' ] ; then echo "! Permissions seem wrong for ${SYSDATADIR}/ssh_host_rsa_key -- should be 0600." problemsfound=$(($problemsfound+1)) fi # propose changes needed for sshd_config (if any) if ! grep -q "^HostKey[[:space:]]\+${SYSDATADIR}/ssh_host_rsa_key$" "$sshd_config"; then echo "! $sshd_config does not point to the monkeysphere host key (${SYSDATADIR}/ssh_host_rsa_key)." echo " - Recommendation: add a line to $sshd_config: 'HostKey ${SYSDATADIR}/ssh_host_rsa_key'" problemsfound=$(($problemsfound+1)) fi if badhostkeys=$(grep -i '^HostKey' "$sshd_config" | grep -v "^HostKey[[:space:]]\+${SYSDATADIR}/ssh_host_rsa_key$") ; then echo "! $sshd_config refers to some non-monkeysphere host keys:" echo "$badhostkeys" echo " - Recommendation: remove the above HostKey lines from $sshd_config" problemsfound=$(($problemsfound+1)) fi fi fi # 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?) # FIXME: look to see that the ownertrust rules are set properly on the # authentication keyring # FIXME: make sure that at least one identity certifier exists # FIXME: look at the timestamps on the monkeysphere-generated # authorized_keys files -- warn if they seem out-of-date. # FIXME: check for a cronjob that updates monkeysphere-generated # authorized_keys? echo echo "Checking for MonkeySphere-enabled public-key authentication for users ..." # Ensure that User ID authentication is enabled: if ! grep -q "^AuthorizedKeysFile[[:space:]]\+${SYSDATADIR}/authorized_keys/%u$" "$sshd_config"; then echo "! $sshd_config does not point to monkeysphere authorized keys." echo " - Recommendation: add a line to $sshd_config: 'AuthorizedKeysFile ${SYSDATADIR}/authorized_keys/%u'" problemsfound=$(($problemsfound+1)) fi if badauthorizedkeys=$(grep -i '^AuthorizedKeysFile' "$sshd_config" | grep -v "^AuthorizedKeysFile[[:space:]]\+${SYSDATADIR}/authorized_keys/%u$") ; then echo "! $sshd_config refers to non-monkeysphere authorized_keys files:" echo "$badauthorizedkeys" echo " - Recommendation: remove the above AuthorizedKeysFile lines from $sshd_config" problemsfound=$(($problemsfound+1)) fi 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:" echo " monkeysphere-server diagnostics" else echo "Everything seems to be in order!" fi } # retrieve key from web of trust, import it into the host keyring, and # ltsign the key in the host keyring so that it may certify other keys add_certifier() { local domain local trust local depth local keyID local fingerprint local ltsignCommand local trustval # set default values for trust depth and domain domain= trust=full depth=1 # get options TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o n:t:d: -l domain:,trust:,depth: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" if [ $? != 0 ] ; then exit 1 fi # Note the quotes around `$TEMP': they are essential! eval set -- "$TEMP" while true ; do case "$1" in -n|--domain) domain="$2" shift 2 ;; -t|--trust) trust="$2" shift 2 ;; -d|--depth) depth="$2" shift 2 ;; --) shift ;; *) break ;; esac done keyID="$1" if [ -z "$keyID" ] ; then failure "You must specify the key ID of a key to add, or specify a file to read the key from." fi if [ -f "$keyID" ] ; then echo "Reading key from file '$keyID':" importinfo=$(gpg_authentication "--import" < "$keyID" 2>&1) || failure "could not read key from '$keyID'" # FIXME: if this is tried when the key database is not # up-to-date, i got these errors (using set -x): # ++ su -m monkeysphere -c '\''gpg --import'\'' # Warning: using insecure memory! # gpg: key D21739E9: public key "Daniel Kahn Gillmor <dkg@fifthhorseman.net>" imported # gpg: Total number processed: 1 # gpg: imported: 1 (RSA: 1) # gpg: can'\''t create `/var/monkeysphere/gnupg-host/pubring.gpg.tmp'\'': Permission denied # gpg: failed to rebuild keyring cache: Permission denied # gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model # gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u # gpg: next trustdb check due at 2009-01-10' # + failure 'could not read key from '\''/root/dkg.gpg'\''' # + echo 'could not read key from '\''/root/dkg.gpg'\''' keyID=$(echo "$importinfo" | grep '^gpg: key ' | cut -f2 -d: | cut -f3 -d\ ) if [ -z "$keyID" ] || [ $(echo "$keyID" | wc -l) -ne 1 ] ; then failure "Expected there to be a single gpg key in the file." fi else # get the key from the key server gpg_authentication "--keyserver $KEYSERVER --recv-key '0x${keyID}!'" || failure "Could not receive a key with this ID from the '$KEYSERVER' keyserver." fi export keyID # get the full fingerprint of a key ID fingerprint=$(gpg_authentication "--list-key --with-colons --with-fingerprint 0x${keyID}!" | \ grep '^fpr:' | grep "$keyID" | cut -d: -f10) if [ -z "$fingerprint" ] ; then failure "Key '$keyID' not found." fi echo echo "key found:" gpg_authentication "--fingerprint 0x${fingerprint}!" echo "Are you sure you want to add the above key as a" read -p "certifier of users on this system? (y/N) " OK; OK=${OK:-N} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Identity certifier not added." fi # export the key to the host keyring gpg_authentication "--export 0x${fingerprint}!" | gpg_host --import if [ "$trust" == marginal ]; then trustval=1 elif [ "$trust" == full ]; then trustval=2 else failure "Trust value requested ('$trust') was unclear (only 'marginal' or 'full' are supported)." fi # ltsign command # NOTE: *all* user IDs will be ltsigned ltsignCommand=$(cat <<EOF ltsign y $trustval $depth $domain y save EOF ) # ltsign the key if echo "$ltsignCommand" | \ gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then # update the trustdb for the authentication keyring gpg_authentication "--check-trustdb" echo echo "Identity certifier added." else failure "Problem adding identify certifier." fi } # delete a certifiers key from the host keyring remove_certifier() { local keyID local fingerprint keyID="$1" if [ -z "$keyID" ] ; then failure "You must specify the key ID of a key to remove." fi if gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-key 0x${keyID}!" ; then read -p "Really remove above listed identity certifier? (y/N) " OK; OK=${OK:-N} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Identity certifier not removed." fi else failure fi # delete the requested key if gpg_authentication "--delete-key --batch --yes 0x${keyID}!" ; then # delete key from host keyring as well gpg_host --delete-key --batch --yes "0x${keyID}!" # update the trustdb for the authentication keyring gpg_authentication "--check-trustdb" echo echo "Identity certifier removed." else failure "Problem removing identity certifier." fi } # list the host certifiers list_certifiers() { local keys local key # find trusted keys in authentication keychain keys=$(gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-keys --with-colons --fingerprint" | \ grep ^pub: | cut -d: -f2,5 | egrep '^(u|f):' | cut -d: -f2) # output keys for key in $keys ; do gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-key --fingerprint $key" done } # issue command to gpg-authentication keyring gpg_authentication_cmd() { gpg_authentication "$@" } ######################################################################## # MAIN ######################################################################## # unset variables that should be defined only in config file unset KEYSERVER unset AUTHORIZED_USER_IDS unset RAW_AUTHORIZED_KEYS unset MONKEYSPHERE_USER # load configuration file [ -e ${MONKEYSPHERE_SERVER_CONFIG:="${SYSCONFIGDIR}/monkeysphere-server.conf"} ] && . "$MONKEYSPHERE_SERVER_CONFIG" # set empty config variable with ones from the environment, or with # defaults LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=${LOG_LEVEL:="INFO"}} KEYSERVER=${MONKEYSPHERE_KEYSERVER:=${KEYSERVER:="pool.sks-keyservers.net"}} AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:=${AUTHORIZED_USER_IDS:="%h/.monkeysphere/authorized_user_ids"}} RAW_AUTHORIZED_KEYS=${MONKEYSPHERE_RAW_AUTHORIZED_KEYS:=${RAW_AUTHORIZED_KEYS:="%h/.ssh/authorized_keys"}} MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=${MONKEYSPHERE_USER:="monkeysphere"}} # other variables CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:="true"} REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"} GNUPGHOME_HOST=${MONKEYSPHERE_GNUPGHOME_HOST:="${SYSDATADIR}/gnupg-host"} GNUPGHOME_AUTHENTICATION=${MONKEYSPHERE_GNUPGHOME_AUTHENTICATION:="${SYSDATADIR}/gnupg-authentication"} # export variables needed in su invocation export DATE export MODE export MONKEYSPHERE_USER export LOG_LEVEL export KEYSERVER export CHECK_KEYSERVER export REQUIRED_USER_KEY_CAPABILITY export GNUPGHOME_HOST export GNUPGHOME_AUTHENTICATION export GNUPGHOME # get subcommand COMMAND="$1" [ "$COMMAND" ] || failure "Type '$PGRM help' for usage." shift case $COMMAND in 'update-users'|'update-user'|'u') update_users "$@" ;; 'gen-key'|'g') gen_key "$@" ;; 'extend-key'|'e') extend_key "$@" ;; 'add-hostname'|'add-name'|'n+') add_hostname "$@" ;; 'revoke-hostname'|'revoke-name'|'n-') revoke_hostname "$@" ;; 'show-key'|'show'|'s') show_server_key ;; 'publish-key'|'publish'|'p') publish_server_key ;; 'diagnostics'|'d') diagnostics ;; 'add-identity-certifier'|'add-id-certifier'|'add-certifier'|'c+') add_certifier "$@" ;; 'remove-identity-certifier'|'remove-id-certifier'|'remove-certifier'|'c-') remove_certifier "$@" ;; 'list-identity-certifiers'|'list-id-certifiers'|'list-certifiers'|'list-certifier'|'c') list_certifiers "$@" ;; 'gpg-authentication-cmd') gpg_authentication_cmd "$@" ;; '--help'|'help'|'-h'|'h'|'?') usage ;; *) failure "Unknown command: '$COMMAND' Type '$PGRM help' for usage." ;; esac exit "$RETURN"