X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=rhesus%2Frhesus;h=7a43fca0ac19f456f95c6daa4c746feb05878c7b;hb=b75cb97e42dd3327942d8b32cac2d9ee97e9aa4a;hp=7979e418c9e3e1a367adc2cab00e90c02467d360;hpb=7ba522ae456c76c6d131ecac990815f7d55695d5;p=monkeysphere.git diff --git a/rhesus/rhesus b/rhesus/rhesus index 7979e41..7a43fca 100755 --- a/rhesus/rhesus +++ b/rhesus/rhesus @@ -1,88 +1,323 @@ -#!/bin/sh +#!/bin/sh -e -# rhesus: monkeysphere authorized_keys update script +# rhesus: monkeysphere authorized_keys/known_hosts generating script # # Written by # Jameson Rollins # # Copyright 2008, released under the GPL, version 3 or later -################################################## -# load conf file -#. /etc/monkeysphere/monkeysphere.conf -. ~/ms/monkeysphere.conf +PGRM=$(basename $0) -# user name of user to update -USERNAME="$1" +######################################################################## +# FUNCTIONS +######################################################################## -#AUTH_KEYS_DIR_BASE=/var/lib/monkeysphere/authorized_keys/ -AUTH_KEYS_DIR_BASE=~/ms/authorized_keys - -AUTH_KEYS_DIR="$AUTH_KEYS_DIR_BASE"/"$USERNAME" -AUTH_KEYS_FILE="$AUTH_KEYS_DIR"/authorized_keys - -AUTH_USER_IDS="$AUTH_USER_IDS_DIR"/"$USERNAME" - -export GNUPGHOME -################################################## - -### FUNCTIONS +usage() { +cat <&2 exit ${2:-'1'} } +log() { + echo -n "ms: " + echo "$@" +} + +# cut out all comments(#) and blank lines from standard input meat() { - grep -v -e "^[[:space:]]*#" -e '^$' "$1" + grep -v -e "^[[:space:]]*#" -e '^$' } +# cut a specified line from standard input cutline() { head --line="$1" | tail -1 } -### MAIN - -# make sure the gnupg home exists with proper permissions -mkdir -p "$GNUPGHOME" -chmod 0700 "$GNUPGHOME" +# 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 +} -# find number of user ids in auth_user_ids file -NLINES=$(meat "$AUTH_USER_IDS" | wc -l) +# 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/:/' +} -# clean out keys file and remake keys directory -rm -rf "$AUTH_KEYS_DIR"/keys -mkdir -p "$AUTH_KEYS_DIR"/keys +# stand in until we get dkg's gpg2ssh program +gpg2ssh_tmp() { + local mode + local keyID -# loop through all user ids, and generate ssh keys -for (( N=1; N<=$NLINES; N=N+1 )) ; do - # get user id - USERID=$(meat "$AUTH_USER_IDS" | head --line="$N" | tail -1) - USERID_HASH=$(echo "$USERID" | sha1sum | awk '{ print $1 }') + mode="$1" + keyID="$2" + userID="$3" - # get key id from user id - #KEYID=$(gpguser2key "$USERID") - KEYID="$USERID" + if [ "$mode" = 'authorized_keys' -o "$mode" = 'a' ] ; then + gpgkey2ssh "$keyID" | sed -e "s/COMMENT/$userID/" + elif [ "$mode" = 'known_hosts' -o "$mode" = 'k' ] ; then + echo -n "$userID "; gpgkey2ssh "$keyID" | sed -e 's/ COMMENT//' + fi +} - echo "Receiving keys for: $USERID ($KEYID)..." +# 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 appropriate capability (E|A) +# - checks that particular desired user id has appropriate validity +# see /usr/share/doc/gnupg/DETAILS.gz +# FIXME: add some more status output +# expects global variable: "mode" +process_user_id() { + local userID + local cacheDir + local keyOK + local keyCapability + local keyFingerprint + local userIDHash - # is primary key revoked && kill - # for all associated keys (primary and sub) - # - type "A" - # - not revoked - # - signed by trusted user - # output ssh key + userID="$1" + cacheDir="$2" - # Receive keys into key ring - if gpg --recv-keys --keyserver "$KEYSERVER" "$KEYID" ; then - # convert pgp key to ssh key, and write to cache file - KEYFILE="$AUTH_KEYS_DIR"/keys/"$USERID_HASH" - gpgkey2ssh "$KEYID" | sed -e "s/COMMENT/$USERID/" > "$KEYFILE" + # fetch all keys from keyserver + # if none found, break + if ! gpg_fetch_keys "$userID" ; then + echo " no keys found." + return fi -done -echo "Writing authorized_keys file '$AUTH_KEYS_FILE'..." -cat "$AUTH_KEYS_DIR"/keys/* > "$AUTH_KEYS_FILE" || > "$AUTH_KEYS_FILE" -if [ -s ~"$USERNAME"/.ssh/authorized_keys ] ; then - cat ~"$USERNAME"/.ssh/authorized_keys >> "$AUTH_KEYS_FILE" + # some crazy piping here that takes the output of gpg and + # pipes it into a "while read" loop that reads each line + # of standard input one-by-one. + gpg --fixed-list-mode --list-key --with-colons \ + --with-fingerprint ="$userID" 2> /dev/null | \ + cut -d : -f 1,2,5,10,12 | \ + while IFS=: read -r type validity keyid uidfpr capability ; do + # process based on record type + case $type in + 'pub') + # new key, wipe the slate + keyOK= + keyCapability= + keyFingerprint= + # check primary key validity + if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then + continue + fi + # check capability is not Disabled... + if echo "$capability" | grep -q 'D' ; then + continue + fi + # check capability is Encryption and Authentication + # FIXME: make more flexible capability specification + # (ie. in conf file) + if echo "$capability" | grep -q -v 'E' ; then + if echo "$capability" | grep -q -v 'A' ; then + continue + fi + fi + keyCapability="$capability" + keyOK=true + keyID="$keyid" + ;; + 'fpr') + # if key ok, get fingerprint + if [ "$keyOK" ] ; then + keyFingerprint="$uidfpr" + fi + ;; + 'uid') + # check key ok and we have key fingerprint + if [ -z "$keyOK" -o -z "$keyFingerprint" ] ; 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 + # convert the key + # FIXME: needs to apply extra options if specified + echo -n " valid key found; generating ssh key(s)... " + userIDHash=$(echo "$userID" | sha1sum | awk '{ print $1 }') + # export the key with gpg2ssh + #gpg --export "$keyFingerprint" | gpg2ssh "$mode" > "$cacheDir"/"$userIDHash"."$keyFingerprint" + # stand in until we get dkg's gpg2ssh program + gpg2ssh_tmp "$mode" "$keyID" "$userID" > "$cacheDir"/"$userIDHash"."$keyFingerprint" + if [ "$?" = 0 ] ; then + echo "done." + else + echo "error." + fi + ;; + esac + done +} + +# process the auth_*_ids file +# go through line-by-line, extracting and processing each user id +# expects global variable: "mode" +process_auth_file() { + local authIDsFile + local cacheDir + local nLines + local line + local userID + + authIDsFile="$1" + cacheDir="$2" + + # find number of user ids in auth_user_ids file + nLines=$(meat <"$authIDsFile" | wc -l) + + # clean out keys file and remake keys directory + rm -rf "$cacheDir" + mkdir -p "$cacheDir" + + # loop through all user ids + for line in $(seq 1 $nLines) ; do + # get user id + # FIXME: needs to handle extra options if necessary + userID=$(meat <"$authIDsFile" | cutline "$line" ) + + # process the user id and extract keys + log "processing user id: '$userID'" + process_user_id "$userID" "$cacheDir" + done +} + +######################################################################## +# MAIN +######################################################################## + +if [ -z "$1" ] ; then + usage + exit 1 +fi + +# check mode +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"} +AUTH_HOST_FILE=${AUTH_HOST_FILE:-"$MS_HOME"/auth_host_ids} +AUTH_USER_FILE=${AUTH_USER_FILE:-"$MS_HOME"/auth_user_ids} +GNUPGHOME=${GNUPGHOME:-"$HOME"/.gnupg} +KEYSERVER=${KEYSERVER:-subkeys.pgp.net} + +USER_KNOW_HOSTS="$HOME"/.ssh/known_hosts +USER_AUTHORIZED_KEYS="$HOME"/.ssh/authorized_keys + +# 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 + +# set mode variables +if [ "$mode" = 'known_hosts' -o "$mode" = 'k' ] ; then + fileType=known_hosts + authFileType=auth_host_ids + authIDsFile="$AUTH_HOST_FILE" + outFile="$msKnownHosts" + cacheDir="$hostKeysCacheDir" + userFile="$USER_KNOWN_HOSTS" +elif [ "$mode" = 'authorized_keys' -o "$mode" = 'a' ] ; then + fileType=authorized_keys + authFileType=auth_user_ids + authIDsFile="$AUTH_USER_FILE" + outFile="$msAuthorizedKeys" + cacheDir="$userKeysCacheDir" + userFile="$USER_AUTHORIZED_KEYS" +else + failure "unknown command '$mode'." +fi + +# check auth ids file +if [ ! -s "$authIDsFile" ] ; then + echo "'$authFileType' file is empty or does not exist." + exit +fi + +log "user '$USER': monkeysphere $fileType generation" + +# make sure gpg home exists with proper permissions +mkdir -p -m 0700 "$GNUPGHOME" + +# if users are specified on the command line, process just +# those users +if [ "$1" ] ; then + # process userids given on the command line + for userID ; do + if ! grep -q "$userID" "$authIDsFile" ; then + log "userid '$userID' not in $authFileType file." + continue + fi + log "processing user id: '$userID'" + process_user_id "$userID" "$cacheDir" + done +# otherwise if no users are specified, process the entire +# auth_*_ids file +else + # process the auth file + process_auth_file "$authIDsFile" "$cacheDir" +fi + +# write output key file +log "writing ms $fileType file... " +> "$outFile" +if [ "$(ls "$cacheDir")" ] ; then + log -n "adding gpg keys... " + cat "$cacheDir"/* > "$outFile" + echo "done." +else + log "no gpg keys to add." +fi +if [ -s "$userFile" ] ; then + log -n "adding user $fileType file... " + cat "$userFile" >> "$outFile" + echo "done." fi +log "ms $fileType file generated:" +log "$outFile"