X-Git-Url: https://codewiz.org/gitweb?p=monkeysphere.git;a=blobdiff_plain;f=src%2Fmonkeysphere;h=6db4827c15a5ea8fc9248cf2a4c1e0f9e6cd5d79;hp=1db4f20ce48fba8670aeb4507d4bed4b9ca0f853;hb=e71c7bb4dff26178f714cd0fcdbb3058effa4066;hpb=91d16f389d27fa8e9e7ed721029334e187506da1 diff --git a/src/monkeysphere b/src/monkeysphere index 1db4f20..6db4827 100755 --- a/src/monkeysphere +++ b/src/monkeysphere @@ -1,31 +1,34 @@ #!/usr/bin/env bash -# monkeysphere: MonkeySphere client tool +# monkeysphere: Monkeysphere client tool # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor +# Micah Anderson # -# They are Copyright 2008, and are all released under the GPL, version 3 +# They are Copyright 2008-2009, and are all released under the GPL, version 3 # or later. ######################################################################## +set -e + PGRM=$(basename $0) SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"/usr/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/common" || exit 1 +# sharedir for host functions +MSHAREDIR="${SYSSHAREDIR}/m" + # 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 - # set the file creation mask to be only owner rw umask 077 @@ -43,260 +46,136 @@ subcommands: update-authorized_keys (a) update authorized_keys file gen-subkey (g) [KEYID] generate an authentication subkey --length (-l) BITS key length in bits (2048) - --expire (-e) EXPIRE date to expire + ssh-proxycommand monkeysphere ssh ProxyCommand subkey-to-ssh-agent (s) store authentication subkey in ssh-agent + version (v) show version number help (h,?) this help EOF } -# generate a subkey with the 'a' usage flags set -gen_subkey(){ - local keyLength - local keyExpire - local keyID - local gpgOut - local userID - - # set default key parameter values - keyLength= - keyExpire= - - # get options - TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o l:e: -l length:,expire: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" - - if [ $? != 0 ] ; then - exit 1 - fi +# user gpg command to define common options +gpg_user() { + gpg --no-greeting --quiet --no-tty "$@" +} - # 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 - ;; - --) - shift - ;; - *) - break - ;; - esac - done +# take a secret key ID and check that only zero or one ID is provided, +# and that it corresponds to only a single secret key ID +check_gpg_sec_key_id() { + local gpgSecOut + + case "$#" in + 0) + gpgSecOut=$(gpg_user --fixed-list-mode --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:') + ;; + 1) + gpgSecOut=$(gpg_user --fixed-list-mode --list-secret-keys --with-colons "$keyID" | egrep '^sec:') || failure + ;; + *) + failure "You must specify only a single primary key ID." + ;; + esac + + # check that only a single secret key was found + case $(echo "$gpgSecOut" | grep -c '^sec:') in + 0) + failure "No secret keys found. Create an OpenPGP key with the following command: + gpg --gen-key" + ;; + 1) + echo "$gpgSecOut" | cut -d: -f5 + ;; + *) + echo "Multiple primary secret keys found:" + echo "$gpgSecOut" | cut -d: -f5 + echo "Please specify which primary key to use." + failure + ;; + esac +} - if [ -z "$1" ] ; then - # find all secret keys - keyID=$(gpg --with-colons --list-secret-keys | grep ^sec | cut -f5 -d: | sort -u) - # if multiple sec keys exist, fail - if (( $(echo "$keyID" | wc -l) > 1 )) ; then - echo "Multiple secret keys found:" - echo "$keyID" - failure "Please specify which primary key to use." +# check that a valid authentication subkey does not already exist +check_gpg_authentication_subkey() { + local keyID + local IFS + local line + local type + local validity + local usage + + keyID="$1" + + # check that a valid authentication key does not already exist + IFS=$'\n' + for line in $(gpg_user --fixed-list-mode --list-keys --with-colons "$keyID") ; do + type=$(echo "$line" | cut -d: -f1) + validity=$(echo "$line" | cut -d: -f2) + usage=$(echo "$line" | cut -d: -f12) + + # look at keys only + if [ "$type" != 'pub' -a "$type" != 'sub' ] ; then + continue fi - else - keyID="$1" - fi - if [ -z "$keyID" ] ; then - failure "You have no secret key available. You should create an OpenPGP -key before joining the monkeysphere. You can do this with: - gpg --gen-key" - fi - - # get key output, and fail if not found - gpgOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons \ - "$keyID") || failure - - # fail if multiple sec lines are returned, which means the id - # given is not unique - if [ $(echo "$gpgOut" | grep -c '^sec:') -gt '1' ] ; then - failure "Key ID '$keyID' is not unique." - fi - - # prompt if an authentication subkey already exists - if echo "$gpgOut" | egrep "^(sec|ssb):" | cut -d: -f 12 | grep -q a ; then - echo "An authentication subkey already exists for key '$keyID'." - read -p "Are you sure you would like to generate another one? (y/N) " OK; OK=${OK:N} - if [ "${OK/y/Y}" != 'Y' ] ; then - failure "aborting." + # check for authentication capability + if ! check_capability "$usage" 'a' ; then + continue fi - fi - - # set subkey defaults - # prompt about key expiration if not specified - keyExpire=$(get_gpg_expiration "$keyExpire") - - # generate the list of commands that will be passed to edit-key - editCommands=$(cat </dev/null ; then - failure "No ssh-agent available." - fi - - # and if it looks like it's running, but we can't actually talk to - # it, bail out: - ssh-add -l >/dev/null - sshaddresponse="$?" - if [ "$sshaddresponse" = "2" ]; then - failure "Could not connect to ssh-agent" - fi - - # get list of secret keys (to work around https://bugs.g10code.com/gnupg/issue945): - secretkeys=$(gpg --list-secret-keys --with-colons --fixed-list-mode --fingerprint | \ - grep '^fpr:' | cut -f10 -d: | awk '{ print "0x" $1 "!" }') - - if [ -z "$secretkeys" ]; then - failure "You have no secret keys in your keyring! -You might want to run 'gpg --gen-key'." - fi - - authsubkeys=$(gpg --list-secret-keys --with-colons --fixed-list-mode \ - --fingerprint --fingerprint $secretkeys | \ - cut -f1,5,10,12 -d: | grep -A1 '^ssb:[^:]*::[^:]*a[^:]*$' | \ - grep '^fpr::' | cut -f3 -d: | sort -u) - - if [ -z "$authsubkeys" ]; then - failure "no authentication-capable subkeys available. -You might want to 'monkeysphere gen-subkey'" - fi - - workingdir=$(mktemp -d ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX) - umask 077 - mkfifo "$workingdir/passphrase" - keysuccess=1 - - # FIXME: we're currently allowing any other options to get passed - # through to ssh-add. should we limit it to known ones? For - # example: -d or -c and/or -t - - for subkey in $authsubkeys; do - # choose a label by which this key will be known in the agent: - # we are labelling the key by User ID instead of by - # fingerprint, but filtering out all / characters to make sure - # the filename is legit. - - primaryuid=$(gpg --with-colons --list-key "0x${subkey}!" | grep '^pub:' | cut -f10 -d: | tr -d /) - - #kname="[monkeysphere] $primaryuid" - kname="$primaryuid" - - if [ "$1" = '-d' ]; then - # we're removing the subkey: - gpg --export "0x${subkey}!" | openpgp2ssh "$subkey" > "$workingdir/$kname" - (cd "$workingdir" && ssh-add -d "$kname") - else - # we're adding the subkey: - mkfifo "$workingdir/$kname" - gpg --quiet --passphrase-fd 3 3<"$workingdir/passphrase" \ - --export-options export-reset-subkey-passwd,export-minimal,no-export-attributes \ - --export-secret-subkeys "0x${subkey}!" | openpgp2ssh "$subkey" > "$workingdir/$kname" & - (cd "$workingdir" && DISPLAY=nosuchdisplay SSH_ASKPASS=/bin/false ssh-add "$@" "$kname" &2 + if [ "$PROMPT" = "true" ] ; then + read -p "Are you sure you would like to generate another one? (y/N) " OK; OK=${OK:N} + if [ "${OK/y/Y}" != 'Y' ] ; then + failure "aborting." + fi + break + else + failure "aborting." + fi fi - keysuccess="$?" - - rm -f "$workingdir/$kname" done - - rm -rf "$workingdir" - - # FIXME: sort out the return values: we're just returning the - # success or failure of the final authentication subkey in this - # case. What if earlier ones failed? - exit "$keysuccess" } ######################################################################## # MAIN ######################################################################## -# unset variables that should be defined only in config file -unset KEYSERVER +# set unset default variables +GNUPGHOME=${GNUPGHOME:="${HOME}/.gnupg"} +KNOWN_HOSTS="${HOME}/.ssh/known_hosts" +HASH_KNOWN_HOSTS="true" +AUTHORIZED_KEYS="${HOME}/.ssh/authorized_keys" + +# unset the check keyserver variable, since that needs to have +# different defaults for the different functions unset CHECK_KEYSERVER -unset KNOWN_HOSTS -unset HASH_KNOWN_HOSTS -unset AUTHORIZED_KEYS # load global config -[ -r "${SYSCONFIGDIR}/monkeysphere.conf" ] && . "${SYSCONFIGDIR}/monkeysphere.conf" +[ -r "${SYSCONFIGDIR}/monkeysphere.conf" ] \ + && . "${SYSCONFIGDIR}/monkeysphere.conf" # set monkeysphere home directory MONKEYSPHERE_HOME=${MONKEYSPHERE_HOME:="${HOME}/.monkeysphere"} mkdir -p -m 0700 "$MONKEYSPHERE_HOME" # load local config -[ -e ${MONKEYSPHERE_CONFIG:="${MONKEYSPHERE_HOME}/monkeysphere.conf"} ] && . "$MONKEYSPHERE_CONFIG" - -# set empty config variables with ones from the environment, or from -# config file, or with defaults -LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=${LOG_LEVEL:="INFO"}} -GNUPGHOME=${MONKEYSPHERE_GNUPGHOME:=${GNUPGHOME:="${HOME}/.gnupg"}} -KEYSERVER=${MONKEYSPHERE_KEYSERVER:="$KEYSERVER"} -# if keyserver not specified in env or monkeysphere.conf, -# look in gpg.conf +[ -e ${MONKEYSPHERE_CONFIG:="${MONKEYSPHERE_HOME}/monkeysphere.conf"} ] \ + && . "$MONKEYSPHERE_CONFIG" + +# set empty config variables with ones from the environment +GNUPGHOME=${MONKEYSPHERE_GNUPGHOME:=$GNUPGHOME} +LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL} +KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER} +# if keyserver not specified in env or conf, then look in gpg.conf if [ -z "$KEYSERVER" ] ; then if [ -f "${GNUPGHOME}/gpg.conf" ] ; then KEYSERVER=$(grep -e "^[[:space:]]*keyserver " "${GNUPGHOME}/gpg.conf" | tail -1 | awk '{ print $2 }') fi fi -# if it's still not specified, use the default -KEYSERVER=${KEYSERVER:="subkeys.pgp.net"} -CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} -KNOWN_HOSTS=${MONKEYSPHERE_KNOWN_HOSTS:=${KNOWN_HOSTS:="${HOME}/.ssh/known_hosts"}} -HASH_KNOWN_HOSTS=${MONKEYSPHERE_HASH_KNOWN_HOSTS:=${HASH_KNOWN_HOSTS:="true"}} -AUTHORIZED_KEYS=${MONKEYSPHERE_AUTHORIZED_KEYS:=${AUTHORIZED_KEYS:="${HOME}/.ssh/authorized_keys"}} +PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT} +KNOWN_HOSTS=${MONKEYSPHERE_KNOWN_HOSTS:=$KNOWN_HOSTS} +HASH_KNOWN_HOSTS=${MONKEYSPHERE_HASH_KNOWN_HOSTS:=$HASH_KNOWN_HOSTS} +AUTHORIZED_KEYS=${MONKEYSPHERE_AUTHORIZED_KEYS:=$AUTHORIZED_KEYS} # other variables not in config file AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:="${MONKEYSPHERE_HOME}/authorized_user_ids"} @@ -316,65 +195,53 @@ shift case $COMMAND in 'update-known_hosts'|'update-known-hosts'|'k') - MODE='known_hosts' + # whether or not to check keyservers + CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} - # check permissions on the known_hosts file path - if ! check_key_file_permissions "$USER" "$KNOWN_HOSTS" ; then - failure "Improper permissions on known_hosts file path." - fi - - # if hosts are specified on the command line, process just - # those hosts + # if hosts are specified on the command line, process just + # those hosts if [ "$1" ] ; then update_known_hosts "$@" - RETURN="$?" - # otherwise, if no hosts are specified, process every host - # in the user's known_hosts file + # otherwise, if no hosts are specified, process every host + # in the user's known_hosts file else - # exit if the known_hosts file does not exist - if [ ! -e "$KNOWN_HOSTS" ] ; then - log error "known_hosts file '$KNOWN_HOSTS' does not exist." - exit - fi - process_known_hosts - RETURN="$?" fi ;; 'update-authorized_keys'|'update-authorized-keys'|'a') - MODE='authorized_keys' - - # check permissions on the authorized_user_ids file path - if ! check_key_file_permissions "$USER" "$AUTHORIZED_USER_IDS" ; then - failure "Improper permissions on authorized_user_ids file path." - fi - - # check permissions on the authorized_keys file path - if ! check_key_file_permissions "$USER" "$AUTHORIZED_KEYS" ; then - failure "Improper permissions on authorized_keys file path." - fi - - # exit if the authorized_user_ids file is empty - if [ ! -e "$AUTHORIZED_USER_IDS" ] ; then - log error "authorized_user_ids file '$AUTHORIZED_USER_IDS' does not exist." - exit - fi + # whether or not to check keyservers + CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} # process authorized_user_ids file process_authorized_user_ids "$AUTHORIZED_USER_IDS" - RETURN="$?" + ;; + + 'import-subkey'|'i') + source "${MSHAREDIR}/import_subkey" + import_subkey "$@" ;; 'gen-subkey'|'g') + source "${MSHAREDIR}/gen_subkey" gen_subkey "$@" ;; + 'ssh-proxycommand'|'p') + source "${MSHAREDIR}/ssh_proxycommand" + ssh_proxycommand "$@" + ;; + 'subkey-to-ssh-agent'|'s') + source "${MSHAREDIR}/subkey_to_ssh_agent" subkey_to_ssh_agent "$@" ;; + 'version'|'v') + version + ;; + '--help'|'help'|'-h'|'h'|'?') usage ;; @@ -384,5 +251,3 @@ case $COMMAND in Type '$PGRM help' for usage." ;; esac - -exit "$RETURN"