fix need for only single argument to gpg_sphere
[monkeysphere.git] / src / share / common
index 914fffa5e9536d69365deab301aba75899812b71..34ac52da7f5dcfcdd2e893be3234ac400002b18c 100644 (file)
@@ -103,12 +103,12 @@ su_monkeysphere_user() {
     case $(id -un) in
        # if monkeysphere user, run the command under bash
        "$MONKEYSPHERE_USER")
-           bash -c "$@"
+           bash -c "$*"
            ;;
 
          # if root, su command as monkeysphere user
        'root')
-           su "$MONKEYSPHERE_USER" -c "$@"
+           su "$MONKEYSPHERE_USER" -c "$*"
            ;;
 
        # otherwise, fail
@@ -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`}
@@ -456,7 +478,7 @@ gpg2ssh() {
     
     keyID="$1"
 
-    gpg --export "$keyID" | openpgp2ssh "$keyID" 2>/dev/null
+    gpg --export --no-armor "$keyID" | openpgp2ssh "$keyID" 2>/dev/null
 }
 
 # output known_hosts line from ssh key
@@ -483,13 +505,15 @@ ssh2known_hosts() {
 
 # output authorized_keys line from ssh key
 ssh2authorized_keys() {
-    local userID
-    local key
-    
-    userID="$1"
-    key="$2"
+    local koptions="$1"
+    local userID="$2"
+    local key="$3"
 
-    printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID"
+    if [[ -z "$koptions" ]]; then
+        printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID"
+    else
+        printf "%s %s MonkeySphere%s %s\n" "$koptions" "$key" "$DATE" "$userID"
+    fi
 }
 
 # convert key from gpg to ssh known_hosts format
@@ -559,6 +583,10 @@ gpg_fetch_userid() {
        --search ="$userID" &>/dev/null
     returnCode="$?"
 
+    if [ "$returnCode" != 0 ] ; then
+        log error "Failure ($returnCode) searching keyserver $KEYSERVER for user id '$userID'"
+    fi
+
     return "$returnCode"
 }
 
@@ -576,13 +604,13 @@ gpg_fetch_userid() {
 # flag:sshKey
 #
 # "flag" is an acceptability flag, 0 = ok, 1 = bad
-# "sshKey" is the translated gpg key
+# "sshKey" is the relevant OpenPGP key, in the form accepted by OpenSSH
 #
 # all log output must go to stderr, as stdout is used to pass the
 # flag:sshKey to the calling function.
 process_user_id() {
     local returnCode=0
-    local userID
+    local userID="$1"
     local requiredCapability
     local requiredPubCapability
     local gpgOut
@@ -597,8 +625,6 @@ process_user_id() {
     local lastKeyOK
     local fingerprint
 
-    userID="$1"
-
     # set the required key capability based on the mode
     requiredCapability=${REQUIRED_KEY_CAPABILITY:="a"}
     requiredPubCapability=$(echo "$requiredCapability" | tr "[:lower:]" "[:upper:]")
@@ -683,7 +709,7 @@ process_user_id() {
                if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then
                    log verbose "  * acceptable primary key."
                    if [ -z "$sshKey" ] ; then
-                       log error "    ! primary key could not be translated (not RSA?)."
+                       log verbose "    ! primary key could not be translated (not RSA?)."
                    else
                        echo "0:${sshKey}"
                    fi
@@ -859,13 +885,25 @@ process_host_known_hosts() {
 
            # hash if specified
            if [ "$HASH_KNOWN_HOSTS" = 'true' ] ; then
+                if (type ssh-keygen >/dev/null) ; then
                # FIXME: this is really hackish cause ssh-keygen won't
                # hash from stdin to stdout
-               tmpfile=$(mktemp ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX)
-               ssh2known_hosts "$host" "$sshKey" > "$tmpfile"
-               ssh-keygen -H -f "$tmpfile" 2>/dev/null
-               cat "$tmpfile" >> "$KNOWN_HOSTS"
-               rm -f "$tmpfile" "${tmpfile}.old"
+                   tmpfile=$(mktemp ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX)
+                   ssh2known_hosts "$host" "$sshKey" > "$tmpfile"
+                   ssh-keygen -H -f "$tmpfile" 2>/dev/null
+                   cat "$tmpfile" >> "$KNOWN_HOSTS"
+                   rm -f "$tmpfile" "${tmpfile}.old"
+                else
+       # FIXME: we could do this without needing ssh-keygen.  hashed
+       # known_hosts looks like: |1|X|Y where 1 means SHA1 (nothing
+       # else is defined in openssh sources), X is the salt (same
+       # length as the digest output), base64-encoded, and Y is the
+       # digested hostname (also base64-encoded).
+
+       # see hostfile.{c,h} in openssh sources.
+
+                    failure "Cannot hash known_hosts as requested"
+                fi
            else
                ssh2known_hosts "$host" "$sshKey" >> "$KNOWN_HOSTS"
            fi
@@ -1004,6 +1042,7 @@ process_known_hosts() {
 # process uids for the authorized_keys file
 process_uid_authorized_keys() {
     local userID
+    local koptions
     local nKeys
     local nKeysOK
     local ok
@@ -1012,7 +1051,8 @@ process_uid_authorized_keys() {
     # set the key processing mode
     export REQUIRED_KEY_CAPABILITY="$REQUIRED_USER_KEY_CAPABILITY"
 
-    userID="$1"
+    koptions="$1"
+    userID="$2"
 
     log verbose "processing: $userID"
 
@@ -1039,7 +1079,7 @@ process_uid_authorized_keys() {
            # note that key was found ok
            nKeysOK=$((nKeysOK+1))
 
-           ssh2authorized_keys "$userID" "$sshKey" >> "$AUTHORIZED_KEYS"
+           ssh2authorized_keys "$koptions" "$userID" "$sshKey" >> "$AUTHORIZED_KEYS"
        fi
     done
 
@@ -1067,9 +1107,14 @@ update_authorized_keys() {
     local nIDsOK
     local nIDsBAD
     local fileCheck
+    local x koptions
+    declare -i argtype
+
+    if (( $# % 2 )); then log error "Bad number of arguments; this should never happen."; return 1; fi
 
     # the number of ids specified on command line
-    nIDs="$#"
+    (( nIDs=$#/2 ))
+    (( argtype=0 ))
 
     nIDsOK=0
     nIDsBAD=0
@@ -1091,23 +1136,30 @@ update_authorized_keys() {
     # remove any monkeysphere lines from authorized_keys file
     remove_monkeysphere_lines "$AUTHORIZED_KEYS"
 
-    for userID ; do
-       # process the user ID, change return code if key not found for
-       # user ID
-       process_uid_authorized_keys "$userID" || returnCode="$?"
-
-       # note the result
-       case "$returnCode" in
-           0)
-               nIDsOK=$((nIDsOK+1))
-               ;;
-           2)
-               nIDsBAD=$((nIDsBAD+1))
-               ;;
-       esac
-
-       # touch the lockfile, for good measure.
-       lock touch "$AUTHORIZED_KEYS"
+    for x; do
+        (( argtype++ ))
+        if (( $argtype % 2 )); then
+            koptions="$x"
+        else
+            userID="$x"
+
+           # process the user ID, change return code if key not found
+           # for user ID
+           process_uid_authorized_keys "$koptions" "$userID" || returnCode="$?"
+
+           # note the result
+           case "$returnCode" in
+               0)
+                   nIDsOK=$((nIDsOK+1))
+                   ;;
+               2)
+                   nIDsBAD=$((nIDsBAD+1))
+                   ;;
+           esac
+
+           # touch the lockfile, for good measure.
+           lock touch "$AUTHORIZED_KEYS"
+       fi
     done
 
     # remove the lockfile and the trap
@@ -1140,11 +1192,15 @@ update_authorized_keys() {
 # process an authorized_user_ids file for authorized_keys
 process_authorized_user_ids() {
     local line
-    local nline
-    local userIDs
+    declare -i nline
+    declare -a userIDs
+    declare -a koptions
+    declare -a export_array
 
     authorizedUserIDs="$1"
 
+    (( nline=0 ))
+
     # exit if the authorized_user_ids file is empty
     if [ ! -e "$authorizedUserIDs" ] ; then
        failure "authorized_user_ids file '$authorizedUserIDs' does not exist."
@@ -1166,11 +1222,27 @@ process_authorized_user_ids() {
     # extract user IDs from authorized_user_ids file
     IFS=$'\n'
     for line in $(meat "$authorizedUserIDs") ; do
-       userIDs["$nline"]="$line"
-       nline=$((nline+1))
+       case "$line" in
+           (" "*|$'\t'*)
+               if [[ -z ${koptions[${nline}]} ]]; then
+                   koptions[${nline}]=$(echo $line | sed 's/^[         ]*//;s/[        ]$//;')
+               else
+                   koptions[${nline}]="${koptions[${nline}]},$(echo $line | sed 's/^[  ]*//;s/[        ]$//;')"
+               fi
+               ;;
+            (*)
+               ((nline++))
+               userIDs[${nline}]="$line"
+               unset koptions[${nline}] || true
+               ;;
+       esac
     done
 
-    update_authorized_keys "${userIDs[@]}"
+    for i in $(seq 1 $nline); do
+        export_array+=("${koptions[$i]}" "${userIDs[$i]}")
+    done
+
+    update_authorized_keys "${export_array[@]}"
 }
 
 # takes a gpg key or keys on stdin, and outputs a list of
@@ -1178,13 +1250,25 @@ process_authorized_user_ids() {
 list_primary_fingerprints() {
     local fake=$(msmktempdir)
     trap "rm -rf $fake" EXIT
-    GNUPGHOME="$fake" gpg --no-tty --quiet --import
+    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"
+}
+
 
 check_cruft_file() {
     local loc="$1"