From bd64869a3b68ff8a020c381371a8ab1e24a5a0e4 Mon Sep 17 00:00:00 2001
From: Jameson Graef Rollins <jrollins@finestructure.net>
Date: Thu, 19 Feb 2009 15:19:02 -0500
Subject: The monkeysphere {import,gen}_subkey functions were not up-to-date.
 did a lot of work to bring them up-to-date, and better handle argument
 checking.  also updated man page, changelog, and tests/basic.

---
 man/man1/monkeysphere.1    | 41 ++++++++++++++---------
 packaging/debian/changelog |  6 ++--
 src/monkeysphere           | 82 ++++++++++++++++++++++++++++++++++++++++++---
 src/share/m/gen_subkey     | 83 ++++++++--------------------------------------
 src/share/m/import_subkey  | 62 ++++++++++++++++++----------------
 src/share/mh/import_key    |  9 +++--
 tests/basic                |  2 +-
 7 files changed, 161 insertions(+), 124 deletions(-)

diff --git a/man/man1/monkeysphere.1 b/man/man1/monkeysphere.1
index 3ed43e1..345e1d8 100644
--- a/man/man1/monkeysphere.1
+++ b/man/man1/monkeysphere.1
@@ -56,24 +56,32 @@ ID, 1 if no matching keys were found at all, and 2 if matching keys
 were found but none were acceptable.  `a' may be used in place of
 `update-authorized_keys'.
 .TP
+.B import-subkey FILE [KEYID]
+Import an existing ssh RSA key as an authentication subkey for a
+private key in your GnuPG keyring.  KEYID is the key ID for the
+primary key for which the subkey with "authentication" capability will
+be imported.  If no key ID is specified, but only one key exists in
+the secret keyring, that key will be used.  `i' may be used in place
+of `import-subkey'.
+.TP
 .B gen-subkey [KEYID]
 Generate an authentication subkey for a private key in your GnuPG
-keyring.  For the primary key with the specified key ID, generate a
-subkey with "authentication" capability that can be used for
-monkeysphere transactions.  An expiration length can be specified with
-the `-e' or `--expire' option (prompt otherwise).  If no key ID is
+keyring.  KEYID is the key ID for the primary key for which the subkey
+with "authentication" capability will be generated.  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'.
+will be used.  The length of the generated key can be specified with
+the `--length` or `-l` option.  `g' may be used in place of
+`gen-subkey'.
 .TP
 .B ssh-proxycommand
-an ssh proxy command that can be used
-to trigger a monkeysphere update of the ssh known_hosts file for a
-host that is being connected to with ssh.  This works by updating the
-known_hosts file for the host first, before an attempted connection to
-the host is made.  Once the known_hosts file has been updated, a TCP
-connection to the host is made by exec'ing netcat(1).  Regular ssh
-communication is then done over this netcat TCP connection (see
-ProxyCommand in ssh_config(5) for more info).
+An ssh ProxyCommand that can be used to trigger a monkeysphere update
+of the ssh known_hosts file for a host that is being connected to with
+ssh.  This works by updating the known_hosts file for the host first,
+before an attempted connection to the host is made.  Once the
+known_hosts file has been updated, a TCP connection to the host is
+made by exec'ing netcat(1).  Regular ssh communication is then done
+over this netcat TCP connection (see ProxyCommand in ssh_config(5) for
+more info).
 
 This command is meant to be run as the ssh "ProxyCommand".  This can
 either be done by specifying the proxy command on the command line:
@@ -108,9 +116,10 @@ change in the future, possibly by adding a deferred check, so that
 hosts that go from non-monkeysphere-enabled to monkeysphere-enabled
 will be properly checked.
 
-Setting the MONKEYSPHERE_CHECK_KEYSERVER
-variable (to `true' or `false') will override the keyserver-checking policy
-defined above.
+Setting the CHECK_KEYSERVER variable in the config file or the
+MONKEYSPHERE_CHECK_KEYSERVER environment variable to either `true' or
+`false' will override the keyserver-checking policy defined above and
+either always or never check the keyserver for host key updates.
 
 .TP
 .B subkey-to-ssh-agent [ssh-add arguments]
diff --git a/packaging/debian/changelog b/packaging/debian/changelog
index 6a9ea18..fc317d9 100644
--- a/packaging/debian/changelog
+++ b/packaging/debian/changelog
@@ -16,12 +16,12 @@ monkeysphere (0.23~pre-1) UNRELEASED; urgency=low
     functions that require it to be there.
   * get rid of getopts dependency
   * added version output option
-  * check that existing authentication keys are valid in gen_key
-     function.
+  * better checks on validity of existing authentication subkeys when
+    doing monkeysphere {import,gen}_subkey.
   * add transition infrastructure for major changes between releases (see
     transitions/README.txt)
 
- -- Daniel Kahn Gillmor <dkg@fifthhorseman.net>  Thu, 19 Feb 2009 02:14:44 -0500
+ -- Jameson Graef Rollins <jrollins@finestructure.net>  Thu, 19 Feb 2009 15:11:04 -0500
 
 monkeysphere (0.22-1) unstable; urgency=low
 
diff --git a/src/monkeysphere b/src/monkeysphere
index 992ca06..4169f2a 100755
--- a/src/monkeysphere
+++ b/src/monkeysphere
@@ -45,12 +45,9 @@ Monkeysphere client tool.
 subcommands:
  update-known_hosts (k) [HOST]...    update known_hosts file
  update-authorized_keys (a)          update authorized_keys file
- import-subkey (i)                   import existing ssh key as gpg subkey
-   --keyfile (-f) FILE                 key file to import
-   --expire (-e) EXPIRE                date to expire
+ import-subkey (i) FILE [KEYID]      import existing ssh key as gpg subkey
  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
@@ -59,6 +56,83 @@ subcommands:
 EOF
 }
 
+# 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 --quiet --fixed-list-mode --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:')
+	    ;;
+	1)
+	    gpgSecOut=$(gpg --quiet --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:" | log error
+	    echo "$gpgSecOut" | cut -d: -f5 | log error
+	    echo "Please specify which primary key to use." | log error
+	    failure
+	    ;;
+    esac
+}
+
+# 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 --quiet --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
+	# check for authentication capability
+	if ! check_capability "$usage" 'a' ; then
+	    continue
+	fi
+	# if authentication key is valid, prompt to continue
+	if [ "$validity" = 'u' ] ; then
+	    log error "A valid authentication key already exists for primary key '$keyID'."
+	    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
+    done
+}
+
 ########################################################################
 # MAIN
 ########################################################################
diff --git a/src/share/m/gen_subkey b/src/share/m/gen_subkey
index d926ad5..7c3ebb7 100644
--- a/src/share/m/gen_subkey
+++ b/src/share/m/gen_subkey
@@ -15,10 +15,10 @@
 
 gen_subkey(){
     local keyLength
-    local keyExpire
+    local gpgSecOut
     local keyID
-    local gpgOut
-    local userID
+    local editCommands
+    local fifoDir
 
     # get options
     while true ; do
@@ -27,10 +27,6 @@ gen_subkey(){
 		keyLength="$2"
 		shift 2
 		;;
-	    -e|--expire)
-		keyExpire="$2"
-		shift 2
-		;;
 	    *)
 		if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then
 		    failure "Unknown option '$1'.
@@ -41,67 +37,11 @@ Type '$PGRM help' for usage."
 	esac
     done
 
-    case "$#" in
-	0)
-	    gpgSecOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:')
-	    ;;
-	1)
-	    gpgSecOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons "$1" | 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)
-	    keyID=$(echo "$gpgSecOut" | cut -d: -f5)
-	    ;;
-	*)
-	    echo "Multiple primary secret keys found:"
-	    echo "$gpgSecOut" | cut -d: -f5
-	    failure "Please specify which primary key to use."
-	    ;;
-    esac
+    # check that the keyID is unique
+    keyID=$(check_gpg_sec_key_id "$@")
 
-    # check that a valid authentication key does not already exist
-    IFS=$'\n'
-    for line in $(gpg --quiet --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
-	# check for authentication capability
-	if ! check_capability "$usage" 'a' ; then
-	    continue
-	fi
-	# if authentication key is valid, prompt to continue
-	if [ "$validity" = 'u' ] ; then
-	    log error "A valid authentication key already exists for primary key '$keyID'."
-	    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
-    done
-
-    # set subkey defaults
-    # prompt about key expiration if not specified
-    keyExpire=$(get_gpg_expiration "$keyExpire")
+    # check that an authentication subkey does not already exist
+    check_gpg_authentication_subkey "$keyID"
 
     # generate the list of commands that will be passed to edit-key
     editCommands=$(cat <<EOF
@@ -112,19 +52,24 @@ E
 A
 Q
 $keyLength
-$keyExpire
+0
 save
 EOF
 )
 
-    log verbose "generating subkey..."
+    # setup the temp fifo dir for retrieving the key password
+    log debug "creating password fifo..."
     fifoDir=$(msmktempdir)
+    trap "rm -rf $fifoDir" EXIT
     (umask 077 && mkfifo "$fifoDir/pass")
+
+    log verbose "generating subkey..."
     echo "$editCommands" | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --edit-key "$keyID" &
 
     # FIXME: this needs to fail more gracefully if the passphrase is incorrect
     passphrase_prompt  "Please enter your passphrase for $keyID: " "$fifoDir/pass"
 
+    trap - EXIT
     rm -rf "$fifoDir"
     wait
     log verbose "done."
diff --git a/src/share/m/import_subkey b/src/share/m/import_subkey
index 8b04456..d71c258 100644
--- a/src/share/m/import_subkey
+++ b/src/share/m/import_subkey
@@ -14,40 +14,46 @@
 # import an existing ssh key as a gpg subkey
 
 import_subkey() {
-    local keyFile="~/.ssh/id_rsa"
-    local keyExpire
+    local sshKeyFile
     local keyID
-    local gpgOut
-    local userID
-
-    # get options
-    while true ; do
-	case "$1" in
-	    -f|--keyfile)
-		keyFile="$2"
-		shift 2
-		;;
-	    -e|--expire)
-		keyExpire="$2"
-		shift 2
-		;;
-	    *)
-		if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then
-		    failure "Unknown option '$1'.
-Type '$PGRM help' for usage."
-		fi
-		break
-		;;
-	esac
-    done
-
-    log verbose "importing ssh key..."
+    local gpgSecOut
+    local fifoDir
+
+    sshKeyFile="$1"
+    shift
+
+    # check that key file specified
+    if [ -z "$sshKeyFile" ] ; then
+	failure "Must specify ssh key file to import, or specify '-' for stdin."
+    fi
+
+    # check that the keyID is unique
+    keyID=$(check_gpg_sec_key_id "$@")
+
+    # check that an authentication subkey does not already exist
+    check_gpg_authentication_subkey "$keyID"
+
+    # setup the temp fifo dir for retrieving the key password
+    log debug "creating password fifo..."
     fifoDir=$(msmktempdir)
+    trap "rm -rf $fifoDir" EXIT
     (umask 077 && mkfifo "$fifoDir/pass")
-    ssh2openpgp | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --import &
 
+    # import ssh key to as authentication subkey
+    if [ "$sshKeyFile" = '-' ] ; then
+	log verbose "importing ssh key from stdin..."
+	ssh2openpgp \
+	    | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --import &
+    else
+	log verbose "importing ssh key from file '$sshKeyFile'..."
+	ssh2openpgp <"$sshKeyFile" \
+	    | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --import &
+    fi
+
+    # get the password if needed
     passphrase_prompt  "Please enter your passphrase for $keyID: " "$fifoDir/pass"
 
+    trap - EXIT
     rm -rf "$fifoDir"
     wait
     log verbose "done."
diff --git a/src/share/mh/import_key b/src/share/mh/import_key
index 6394ad7..040b41c 100644
--- a/src/share/mh/import_key
+++ b/src/share/mh/import_key
@@ -21,6 +21,11 @@ local userID
 sshKeyFile="$1"
 hostName="$2"
 
+# check that key file specified
+if [ -z "$sshKeyFile" ] ; then
+    failure "Must specify ssh key file to import, or specify '-' for stdin."
+fi
+
 # use the default hostname if not specified
 if [ -z "$hostName" ] ; then
     hostName=$(hostname -f) || failure "Could not determine hostname."
@@ -45,9 +50,7 @@ mkdir -p "${GNUPGHOME_HOST}"
 chmod 700 "${GNUPGHOME_HOST}"
 
 # import ssh key to a private key
-if [ -z "$sshKeyFile" ] ; then
-    failure "Must specify ssh key file to import, or specify '-' for stdin."
-elif [ "$sshKeyFile" = '-' ] ; then
+if [ "$sshKeyFile" = '-' ] ; then
     log verbose "importing ssh key from stdin..."
     PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$userID" \
 	| gpg_host --import
diff --git a/tests/basic b/tests/basic
index 0b91531..3d50977 100755
--- a/tests/basic
+++ b/tests/basic
@@ -243,7 +243,7 @@ monkeysphere-authentication list-certifiers
 # generate an auth subkey for the test user that expires in 2 days
 echo "##################################################"
 echo "### generating key for testuser..."
-monkeysphere gen-subkey --expire 2
+monkeysphere gen-subkey
 
 # add server key to testuser keychain
 echo "##################################################"
-- 
cgit v1.2.3