+++ /dev/null
-#!/bin/sh
-
-# howler: monkeysphere server gpg generator/publisher/maintainer
-#
-# Written by
-# Jameson Rollins <jrollins@fifthhorseman.net>
-#
-# Copyright 2008, released under the GPL, version 3 or later
-
-PGRM=$(basename $0)
-
-########################################################################
-# FUNCTIONS
-########################################################################
-
-usage() {
-cat <<EOF
-usage: $PGRM gen-key
- $PGRM publish-key
- $PGRM trust-key KEYID [KEYID...]
- $PGRM help
-EOF
-}
-
-failure() {
- echo "$1" >&2
- exit ${2:-'1'}
-}
-
-# generate server gpg key
-gen_key() {
- KEY_TYPE=${KEY_TYPE:-RSA}
- KEY_LENGTH=${KEY_LENGTH:-2048}
- KEY_USAGE=${KEY_USAGE:-encrypt,auth}
- SERVICE=${SERVICE:-ssh}
- HOSTNAME_FQDN=${HOSTNAME_FQDN:-$(hostname -f)}
-
- USERID=${USERID:-"$SERVICE"://"$HOSTNAME_FQDN"}
-
- echo "key parameters:"
- cat <<EOF
-Key-Type: $KEY_TYPE
-Key-Length: $KEY_LENGTH
-Key-Usage: $KEY_USAGE
-Name-Real: $USERID
-EOF
-
- read -p "generate key? [Y|n]: " OK; OK=${OK:=Y}
- if [ ${OK/y/Y} != 'Y' ] ; then
- failure "aborting."
- fi
-
- if gpg --list-key ="$USERID" > /dev/null 2>&1 ; then
- failure "key for '$USERID' already exists"
- fi
-
- echo "generating server key..."
- gpg --batch --gen-key <<EOF
-Key-Type: $KEY_TYPE
-Key-Length: $KEY_LENGTH
-Key-Usage: $KEY_USAGE
-Name-Real: $USERID
-%commit
-EOF
-}
-
-publish_key() {
- read -p "publish key to $KEYSERVER? [Y|n]: " OK; OK=${OK:=Y}
- if [ ${OK/y/Y} != 'Y' ] ; then
- failure "aborting."
- fi
-
- keyID=$(gpg --list-key --with-colons ="$USERID" 2> /dev/null | grep '^pub:' | cut -d: -f5)
-
- # dummy command so as not to publish fakes keys during testing
- # eventually:
- #gpg --send-keys --keyserver "$KEYSERVER" "$keyID"
- echo "gpg --send-keys --keyserver $KEYSERVER $keyID"
-}
-
-trust_key() {
- for keyID ; do
- # get the key from the key server
- gpg --keyserver "$KEYSERVER" --recv-key "$keyID" || failure "could not retrieve key '$keyID'"
-
- # edit the key to change trust
- # FIXME: need to figure out how to automate this,
- # in a batch mode or something.
- gpg --edit-key "$keyID"
- done
-}
-
-########################################################################
-# MAIN
-########################################################################
-
-# set ms home directory
-MS_HOME=${MS_HOME:-/etc/monkeysphere}
-
-# load configuration file
-MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf}
-[ -e "$MS_CONF" ] && . "$MS_CONF"
-
-GNUPGHOME=${GNUPGHOME:-"$MS_HOME"/gnupg}
-export GNUPGHOME
-KEYSERVER=${KEYSERVER:-subkeys.pgp.net}
-export KEYSERVER
-
-COMMAND="$1"
-[ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
-shift 1
-
-case $COMMAND in
- 'gen-key')
- gen_key
- ;;
- 'publish-key')
- publish_key
- ;;
- 'trust-key')
- if [ -z "$1" ] ; then
- failure "you must specify at least one key to trust."
- fi
- trust_key "$@"
- ;;
- 'help')
- usage
- exit
- ;;
- *)
- failure "Unknown command: '$COMMAND'
-Type '$PGRM help' for usage."
- ;;
-esac
+++ /dev/null
-#!/bin/sh
-
-# rhesus: monkeysphere authorized_keys/known_hosts generating script
-#
-# Written by
-# Jameson Rollins <jrollins@fifthhorseman.net>
-#
-# Copyright 2008, released under the GPL, version 3 or later
-
-# all caps variables are meant to be user supplied (ie. from config
-# file) and are considered global
-
-PGRM=$(basename $0)
-
-# date in UTF format if needed
-DATE=$(date -u '+%FT%T')
-
-# unset some environment variables that could screw things up
-GREP_OPTIONS=
-
-########################################################################
-# FUNCTIONS
-########################################################################
-
-usage() {
-cat <<EOF
-usage: $PGRM k|known_hosts [host...]
- $PGRM a|authorized_keys [userid...]
-Monkeysphere update of known_hosts or authorized_keys file.
-If hosts/userids are specified, only those specified will be processed
-EOF
-}
-
-failure() {
- echo "$1" >&2
- exit ${2:-'1'}
-}
-
-# write output to stdout
-log() {
- echo -n "ms: "
- echo "$@"
-}
-
-# write output to stderr
-loge() {
- echo -n "ms: " 1>&2
- echo "$@" 1>&2
-}
-
-# cut out all comments(#) and blank lines from standard input
-meat() {
- grep -v -e "^[[:space:]]*#" -e '^$'
-}
-
-# cut a specified line from standard input
-cutline() {
- head --line="$1" | tail -1
-}
-
-# retrieve all keys with given user id from keyserver
-# FIXME: need to figure out how to retrieve all matching keys
-# (not just first 5)
-gpg_fetch_keys() {
- local id
- id="$1"
- echo 1,2,3,4,5 | \
- gpg --quiet --batch --command-fd 0 --with-colons \
- --keyserver "$KEYSERVER" \
- --search ="$id" >/dev/null 2>&1
-}
-
-# check that characters are in a string (in an AND fashion).
-# used for checking key capability
-# check_capability capability a [b...]
-check_capability() {
- local capability
- local capcheck
-
- capability="$1"
- shift 1
-
- for capcheck ; do
- if echo "$capability" | grep -q -v "$capcheck" ; then
- return 1
- fi
- done
- return 0
-}
-
-# convert escaped characters from gpg output back into original
-# character
-# FIXME: undo all escape character translation in with-colons gpg output
-unescape() {
- echo "$1" | sed 's/\\x3a/:/'
-}
-
-# stand in until we get dkg's gpg2ssh program
-gpg2ssh_tmp() {
- local mode
- local keyID
- local userID
- local host
-
- mode="$1"
- keyID="$2"
- userID="$3"
-
- if [ "$mode" = 'authorized_keys' ] ; then
- gpgkey2ssh "$keyID" | sed -e "s/COMMENT/${userID}/"
-
- # NOTE: it seems that ssh-keygen -R removes all comment fields from
- # all lines in the known_hosts file. why?
- # NOTE: just in case, the COMMENT can be matched with the
- # following regexp:
- # '^MonkeySphere[[:digit:]]{4}(-[[:digit:]]{2}){2}T[[:digit:]]{2}(:[[:digit:]]{2}){2}$'
- elif [ "$mode" = 'known_hosts' ] ; then
- host=$(echo "$userID" | sed -e "s|ssh://||")
- echo -n "$host "; gpgkey2ssh "$keyID" | sed -e "s/COMMENT/MonkeySphere${DATE}/"
- fi
-}
-
-# userid and key policy checking
-# the following checks policy on the returned keys
-# - checks that full key has appropriate valididy (u|f)
-# - checks key has specified capability (REQUIRED_KEY_CAPABILITY)
-# - checks that particular desired user id has appropriate validity
-# see /usr/share/doc/gnupg/DETAILS.gz
-# expects global variable: "mode"
-process_user_id() {
- local userID
- local cacheDir
- local requiredPubCapability
- local gpgOut
- local line
- local type
- local validity
- local keyid
- local uidfpr
- local capability
- local keyOK
- local pubKeyID
- local uidOK
- local keyIDs
- local userIDHash
- local keyID
-
- userID="$1"
- cacheDir="$2"
-
- requiredPubCapability=$(echo "$REQUIRED_KEY_CAPABILITY" | tr "[:lower:]" "[:upper:]")
-
- # fetch keys from keyserver, return 1 if none found
- gpg_fetch_keys "$userID" || return 1
-
- # output gpg info for (exact) userid and store
- gpgOut=$(gpg --fixed-list-mode --list-key --with-colons \
- ="$userID" 2> /dev/null)
-
- # return 1 if there only "tru" lines are output from gpg
- if [ -z "$(echo "$gpgOut" | grep -v '^tru:')" ] ; then
- return 1
- fi
-
- # loop over all lines in the gpg output and process.
- # need to do it this way (as opposed to "while read...") so that
- # variables set in loop will be visible outside of loop
- for line in $(seq 1 $(echo "$gpgOut" | wc -l)) ; do
-
- # read the contents of the line
- type=$(echo "$gpgOut" | cutline "$line" | cut -d: -f1)
- validity=$(echo "$gpgOut" | cutline "$line" | cut -d: -f2)
- keyid=$(echo "$gpgOut" | cutline "$line" | cut -d: -f5)
- uidfpr=$(echo "$gpgOut" | cutline "$line" | cut -d: -f10)
- capability=$(echo "$gpgOut" | cutline "$line" | cut -d: -f12)
-
- # process based on record type
- case $type in
- 'pub') # primary keys
- # new key, wipe the slate
- keyOK=
- pubKeyID=
- uidOK=
- keyIDs=
-
- pubKeyID="$keyid"
-
- # check primary key validity
- if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then
- loge " unacceptable primary key validity ($validity)."
- continue
- fi
- # check capability is not Disabled...
- if check_capability "$capability" 'D' ; then
- loge " key disabled."
- continue
- fi
- # check overall key capability
- # must be Encryption and Authentication
- if ! check_capability "$capability" $requiredPubCapability ; then
- loge " unacceptable primary key capability ($capability)."
- continue
- fi
-
- # mark if primary key is acceptable
- keyOK=true
-
- # add primary key ID to key list if it has required capability
- if check_capability "$capability" $REQUIRED_KEY_CAPABILITY ; then
- keyIDs[${#keyIDs[*]}]="$keyid"
- fi
- ;;
- 'uid') # user ids
- # check key ok and we have key fingerprint
- if [ -z "$keyOK" ] ; then
- continue
- fi
- # check key validity
- if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then
- continue
- fi
- # check the uid matches
- if [ "$(unescape "$uidfpr")" != "$userID" ] ; then
- continue
- fi
-
- # mark if uid acceptable
- uidOK=true
- ;;
- 'sub') # sub keys
- # add sub key ID to key list if it has required capability
- if check_capability "$capability" $REQUIRED_KEY_CAPABILITY ; then
- keyIDs[${#keyIDs[*]}]="$keyid"
- fi
- ;;
- esac
- done
-
- # hash userid for cache file name
- userIDHash=$(echo "$userID" | sha1sum | awk '{ print $1 }')
-
- # touch/clear key cache file
- # (will be left empty if there are noacceptable keys)
- > "$cacheDir"/"$userIDHash"."$pubKeyID"
-
- # for each acceptable key, write an ssh key line to the
- # key cache file
- if [ "$keyOK" -a "$uidOK" -a "${keyIDs[*]}" ] ; then
- for keyID in ${keyIDs[@]} ; do
- # export the key with gpg2ssh
- # FIXME: needs to apply extra options for authorized_keys
- # lines if specified
- gpg2ssh_tmp "$mode" "$keyID" "$userID" >> "$cacheDir"/"$userIDHash"."$pubKeyID"
-
- # hash the cache file if specified
- if [ "$mode" = 'known_hosts' -a "$HASH_KNOWN_HOSTS" ] ; then
- ssh-keygen -H -f "$cacheDir"/"$userIDHash"."$pubKeyID" > /dev/null 2>&1
- rm "$cacheDir"/"$userIDHash"."$pubKeyID".old
- fi
- done
- fi
-
- # echo the path to the key cache file
- echo "$cacheDir"/"$userIDHash"."$pubKeyID"
-}
-
-# process a host for addition to a known_host file
-process_host() {
- local host
- local cacheDir
- local hostKeyCachePath
-
- host="$1"
- cacheDir="$2"
-
- log "processing host: '$host'"
-
- hostKeyCachePath=$(process_user_id "ssh://${host}" "$cacheDir")
- if [ $? = 0 ] ; then
- ssh-keygen -R "$host" -f "$USER_KNOWN_HOSTS"
- cat "$hostKeyCachePath" >> "$USER_KNOWN_HOSTS"
- fi
-}
-
-# process known_hosts file
-# go through line-by-line, extract each host, and process with the
-# host processing function
-process_known_hosts() {
- local cacheDir
- local userID
-
- cacheDir="$1"
-
- # take all the hosts from the known_hosts file (first field),
- # grep out all the hashed hosts (lines starting with '|')
- cut -d ' ' -f 1 "$USER_KNOWN_HOSTS" | \
- grep -v '^|.*$' | \
- while IFS=, read -r -a hosts ; do
- # process each host
- for host in ${hosts[*]} ; do
- process_host "$host" "$cacheDir"
- done
- done
-}
-
-# process an authorized_*_ids file
-# go through line-by-line, extract each userid, and process
-process_authorized_ids() {
- local authorizedIDsFile
- local cacheDir
- local userID
- local userKeyCachePath
-
- authorizedIDsFile="$1"
- cacheDir="$2"
-
- # clean out keys file and remake keys directory
- rm -rf "$cacheDir"
- mkdir -p "$cacheDir"
-
- # loop through all user ids in file
- # FIXME: needs to handle extra options if necessary
- cat "$authorizedIDsFile" | meat | \
- while read -r userID ; do
- # process the userid
- log "processing userid: '$userID'"
- userKeyCachePath=$(process_user_id "$userID" "$cacheDir")
- if [ -s "$userKeyCachePath" ] ; then
- loge " acceptable key/uid found."
- fi
- done
-}
-
-########################################################################
-# MAIN
-########################################################################
-
-if [ -z "$1" ] ; then
- usage
- exit 1
-fi
-
-# mode given in first variable
-mode="$1"
-shift 1
-
-# check user
-if ! id -u "$USER" > /dev/null 2>&1 ; then
- failure "invalid user '$USER'."
-fi
-
-# set user home directory
-HOME=$(getent passwd "$USER" | cut -d: -f6)
-
-# set ms home directory
-MS_HOME=${MS_HOME:-"$HOME"/.config/monkeysphere}
-
-# load configuration file
-MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf}
-[ -e "$MS_CONF" ] && . "$MS_CONF"
-
-# set config variable defaults
-STAGING_AREA=${STAGING_AREA:-"$MS_HOME"}
-AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"$MS_HOME"/authorized_user_ids}
-GNUPGHOME=${GNUPGHOME:-"$HOME"/.gnupg}
-KEYSERVER=${KEYSERVER:-subkeys.pgp.net}
-REQUIRED_KEY_CAPABILITY=${REQUIRED_KEY_CAPABILITY:-"e a"}
-USER_CONTROLLED_AUTHORIZED_KEYS=${USER_CONTROLLED_AUTHORIZED_KEYS:-"$HOME"/.ssh/authorized_keys}
-USER_KNOWN_HOSTS=${USER_KNOWN_HOSTS:-"$HOME"/.ssh/known_hosts}
-HASH_KNOWN_HOSTS=${HASH_KNOWN_HOSTS:-}
-
-# export USER and GNUPGHOME variables, since they are used by gpg
-export USER
-export GNUPGHOME
-
-# stagging locations
-hostKeysCacheDir="$STAGING_AREA"/host_keys
-userKeysCacheDir="$STAGING_AREA"/user_keys
-msKnownHosts="$STAGING_AREA"/known_hosts
-msAuthorizedKeys="$STAGING_AREA"/authorized_keys
-
-# make sure gpg home exists with proper permissions
-mkdir -p -m 0700 "$GNUPGHOME"
-
-## KNOWN_HOST MODE
-if [ "$mode" = 'known_hosts' -o "$mode" = 'k' ] ; then
- mode='known_hosts'
-
- cacheDir="$hostKeysCacheDir"
-
- log "user '$USER': monkeysphere known_hosts processing"
-
- # touch the known_hosts file to make sure it exists
- touch "$USER_KNOWN_HOSTS"
-
- # if hosts are specified on the command line, process just
- # those hosts
- if [ "$1" ] ; then
- for host ; do
- process_host "$host" "$cacheDir"
- done
-
- # otherwise, if no hosts are specified, process the user
- # known_hosts file
- else
- if [ ! -s "$USER_KNOWN_HOSTS" ] ; then
- failure "known_hosts file '$USER_KNOWN_HOSTS' is empty."
- fi
- process_known_hosts "$cacheDir"
- fi
-
-## AUTHORIZED_KEYS MODE
-elif [ "$mode" = 'authorized_keys' -o "$mode" = 'a' ] ; then
- mode='authorized_keys'
-
- cacheDir="$userKeysCacheDir"
-
- # check auth ids file
- if [ ! -s "$AUTHORIZED_USER_IDS" ] ; then
- log "authorized_user_ids file is empty or does not exist."
- exit
- fi
-
- log "user '$USER': monkeysphere authorized_keys processing"
-
- # if userids are specified on the command line, process just
- # those userids
- if [ "$1" ] ; then
- for userID ; do
- if ! grep -q "$userID" "$AUTHORIZED_USER_IDS" ; then
- log "userid '$userID' not in authorized_user_ids file."
- continue
- fi
- log "processing user id: '$userID'"
- process_user_id "$userID" "$cacheDir" > /dev/null
- done
-
- # otherwise, if no userids are specified, process the entire
- # authorized_user_ids file
- else
- process_authorized_ids "$AUTHORIZED_USER_IDS" "$cacheDir"
- fi
-
- # write output key file
- log "writing monkeysphere authorized_keys file... "
- touch "$msAuthorizedKeys"
- if [ "$(ls "$cacheDir")" ] ; then
- log -n "adding gpg keys... "
- cat "$cacheDir"/* > "$msAuthorizedKeys"
- echo "done."
- else
- log "no gpg keys to add."
- fi
- if [ "$USER_CONTROLLED_AUTHORIZED_KEYS" ] ; then
- if [ -s "$USER_CONTROLLED_AUTHORIZED_KEYS" ] ; then
- log -n "adding user authorized_keys file... "
- cat "$USER_CONTROLLED_AUTHORIZED_KEYS" >> "$msAuthorizedKeys"
- echo "done."
- fi
- fi
- log "monkeysphere authorized_keys file generated:"
- log "$msAuthorizedKeys"
-
-else
- failure "unknown command '$mode'."
-fi