diff options
-rw-r--r-- | man/man1/monkeysphere.1 | 36 | ||||
-rw-r--r-- | src/common | 61 | ||||
-rwxr-xr-x | src/monkeysphere | 109 |
3 files changed, 188 insertions, 18 deletions
diff --git a/man/man1/monkeysphere.1 b/man/man1/monkeysphere.1 index db35a38..92ba2fa 100644 --- a/man/man1/monkeysphere.1 +++ b/man/man1/monkeysphere.1 @@ -24,17 +24,20 @@ connection authentication. .B update-known_hosts [HOST]... Update the known_hosts file. For each specified host, gpg will be queried for a key associated with the host URI (see HOST -IDENTIFICATION in monkeysphere(5)), optionally querying a keyserver. +IDENTIFICATION in +.BR monkeysphere(5)), +optionally querying a keyserver. If an acceptable key is found for the host (see KEY ACCEPTABILITY in -monkeysphere(5)), the key is added to the user's known_hosts file. If -a key is found but is unacceptable for the host, any matching keys are -removed from the user's known_hosts file. If no gpg key is found for -the host, nothing is done. If no hosts are specified, all hosts -listed in the known_hosts file will be processed. This subcommand -will exit with a status of 0 if at least one acceptable key was found -for a specified host, 1 if no matching keys were found at all, and 2 -if matching keys were found but none were acceptable. `k' may be used -in place of `update-known_hosts'. +.BR monkeysphere(5)), +the key is added to the user's known_hosts file. If a key is found +but is unacceptable for the host, any matching keys are removed from +the user's known_hosts file. If no gpg key is found for the host, +nothing is done. If no hosts are specified, all hosts listed in the +known_hosts file will be processed. This subcommand will exit with a +status of 0 if at least one acceptable key was found for a specified +host, 1 if no matching keys were found at all, and 2 if matching keys +were found but none were acceptable. `k' may be used in place of +`update-known_hosts'. .TP .B update-authorized_keys Update the authorized_keys file for the user executing the command @@ -43,7 +46,8 @@ monkeysphere keys are cleared from the authorized_keys file. Then, or each user ID in the user's authorized_user_ids file, gpg will be queried for keys associated with that user ID, optionally querying a keyserver. If an acceptable key is found (see KEY ACCEPTABILITY in -monkeysphere(5)), the key is added to the user's authorized_keys file. +.BR monkeysphere (5)), +the key is added to the user's authorized_keys file. If a key is found but is unacceptable for the user ID, any matching keys are removed from the user's authorized_keys file. If no gpg key is found for the user ID, nothing is done. This subcommand will exit @@ -61,6 +65,15 @@ the `-e' or `--expire' option (prompt otherwise). If no key ID is specified, but only one key exists in the secret keyring, that key will be used. `g' may be used in place of `gen-subkey'. .TP +.B subkey-to-ssh-agent [ssh-add arguments] +Push all authentication-capable subkeys in your GnuPG secret keyring +into your running ssh-agent. Additional arguments are passed through +to +.BR ssh-add (1). +For example, to remove the authentication subkeys, pass an additional +`-d' argument. To require confirmation on each use of the key, pass +`-c'. `s' may be used in place of `subkey-to-ssh-agent'. +.TP .B help Output a brief usage summary. `h' or `?' may be used in place of `help'. @@ -112,4 +125,5 @@ Kahn Gillmor <dkg@fifthhorseman.net> .BR monkeysphere-server (8), .BR monkeysphere (5), .BR ssh (1), +.BR ssh-add (1), .BR gpg (1) @@ -105,6 +105,67 @@ EOF echo "$keyExpire" } +passphrase_prompt() { + local prompt="$1" + local fifo="$2" + local PASS + + if [ "$DISPLAY" ] && which "${SSH_ASKPASS:-ssh-askpass}" >/dev/null; then + "${SSH_ASKPASS:-ssh-askpass}" "$prompt" > "$fifo" + else + read -s -p "$prompt" PASS + # Uses the builtin echo, so should not put the passphrase into + # the process table. I think. --dkg + echo "$PASS" > "$fifo" + fi +} + +test_gnu_dummy_s2k_extension() { + +# this block contains a demonstration private key that has had the +# primary key stripped out using the GNU S2K extension known as +# "gnu-dummy" (see /usr/share/doc/gnupg/DETAILS.gz). The subkey is +# present in cleartext, however. + +# openpgp2ssh will be able to deal with this based on whether the +# local copy of GnuTLS contains read_s2k support that can handle it. + +# read up on that here: + +# http://lists.gnu.org/archive/html/gnutls-devel/2008-08/msg00005.html + +echo " +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.9 (GNU/Linux) + +lQCVBEO3YdABBACRqqEnucag4+vyZny2M67Pai5+5suIRRvY+Ly8Ms5MvgCi3EVV +xT05O/+0ShiRaf+QicCOFrhbU9PZzzU+seEvkeW2UCu4dQfILkmj+HBEIltGnHr3 +G0yegHj5pnqrcezERURf2e17gGFWX91cXB9Cm721FPXczuKraphKwCA9PwARAQAB +/gNlAkdOVQG0OURlbW9uc3RyYXRpb24gS2V5IGZvciBTMksgR05VIGV4dGVuc2lv +biAxMDAxIC0tIGdudS1kdW1teYi8BBMBAgAmBQJDt2HQAhsDBQkB4TOABgsJCAcD +AgQVAggDBBYCAwECHgECF4AACgkQQZUwSa4UDezTOQP/TMQXUVrWzHYZGopoPZ2+ +ZS3qddiznBHsgb7MGYg1KlTiVJSroDUBCHIUJvdQKZV9zrzrFl47D07x6hGyUPHV +aZXvuITW8t1o5MMHkCy3pmJ2KgfDvdUxrBvLfgPMICA4c6zA0mWquee43syEW9NY +g3q61iPlQwD1J1kX1wlimLCdAdgEQ7dh0AEEANAwa63zlQbuy1Meliy8otwiOa+a +mH6pxxUgUNggjyjO5qx+rl25mMjvGIRX4/L1QwIBXJBVi3SgvJW1COZxZqBYqj9U +8HVT07mWKFEDf0rZLeUE2jTm16cF9fcW4DQhW+sfYm+hi2sY3HeMuwlUBK9KHfW2 ++bGeDzVZ4pqfUEudABEBAAEAA/0bemib+wxub9IyVFUp7nPobjQC83qxLSNzrGI/ +RHzgu/5CQi4tfLOnwbcQsLELfker2hYnjsLrT9PURqK4F7udrWEoZ1I1LymOtLG/ +4tNZ7Mnul3wRC2tCn7FKx8sGJwGh/3li8vZ6ALVJAyOia5TZ/buX0+QZzt6+hPKk +7MU1WQIA4bUBjtrsqDwro94DvPj3/jBnMZbXr6WZIItLNeVDUcM8oHL807Am97K1 +ueO/f6v1sGAHG6lVPTmtekqPSTWBfwIA7CGFvEyvSALfB8NUa6jtk27NCiw0csql +kuhCmwXGMVOiryKEfegkIahf2bAd/gnWHPrpWp7bUE20v8YoW22I4wIAhnm5Wr5Q +Sy7EHDUxmJm5TzadFp9gq08qNzHBpXSYXXJ3JuWcL1/awUqp3tE1I6zZ0hZ38Ia6 +SdBMN88idnhDPqPoiKUEGAECAA8FAkO3YdACGyAFCQHhM4AACgkQQZUwSa4UDezm +vQP/ZhK+2ly9oI2z7ZcNC/BJRch0/ybQ3haahII8pXXmOThpZohr/LUgoWgCZdXg +vP6yiszNk2tIs8KphCAw7Lw/qzDC2hEORjWO4f46qk73RAgSqG/GyzI4ltWiDhqn +vnQCFl3+QFSe4zinqykHnLwGPMXv428d/ZjkIc2ju8dRsn4= +=CR5w +-----END PGP PRIVATE KEY BLOCK----- +" | openpgp2ssh 4129E89D17C1D591 >/dev/null 2>/dev/null + +} + # remove all lines with specified string from specified file remove_line() { local file diff --git a/src/monkeysphere b/src/monkeysphere index 303dc8d..2ca3636 100755 --- a/src/monkeysphere +++ b/src/monkeysphere @@ -42,6 +42,7 @@ subcommands: gen-subkey (g) [KEYID] generate an authentication subkey --length (-l) BITS key length in bits (2048) --expire (-e) EXPIRE date to expire + subkey-to-ssh-agent (s) store authentication subkey in ssh-agent help (h,?) this help EOF @@ -165,18 +166,108 @@ EOF fifoDir=$(mktemp -d) (umask 077 && mkfifo "$fifoDir/pass") echo "$editCommands" | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --edit-key "$keyID" & - - if [ "$DISPLAY" ] && which ssh-askpass >/dev/null; then - ssh-askpass "Please enter your passphrase for $keyID: " > "$fifoDir/pass" - else - read -s -p "Please enter your passphrase for $keyID: " PASS - echo "$PASS" > "$fifoDir/pass" - fi + + passphrase_prompt "Please enter your passphrase for $keyID: " "$fifoDir/pass" + rm -rf "$fifoDir" wait log "done." } +function subkey_to_ssh_agent() { + # try to add all authentication subkeys to the agent: + + local authsubkeys + local secretkeys + local subkey + local workingdir + local kname + local sshaddresponse + local keysuccess + + # if there's no agent running, don't bother: + if [ -z "$SSH_AUTH_SOCK" ] || ! which ssh-add >/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 '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 + + if ! test_gnu_dummy_s2k_extension ; then + failure "Your version of GnuTLS does not seem capable of using with gpg's exported subkeys. +You may want to consider patching or upgrading. + +For more details, see: + http://lists.gnu.org/archive/html/gnutls-devel/2008-08/msg00005.html" + fi + + workingdir=$(mktemp -d) + 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 <lifetime> + + 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" + + 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" </dev/null )& + + passphrase_prompt "Enter passphrase for key for $primaryuid: " "$workingdir/passphrase" + wait %2 + 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 ######################################################################## @@ -288,6 +379,10 @@ case $COMMAND in gen_subkey "$@" ;; + 'subkey-to-ssh-agent'|'s') + subkey_to_ssh_agent "$@" + ;; + '--help'|'help'|'-h'|'h'|'?') usage ;; |