X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=src%2Fcommon;h=25f7e4efc245fa7b95cb39ca946a1e50ab8fef23;hb=74bd3557fe340555629fd8615c31fe4b6a8b8174;hp=bb988f785e983ad5bcb90492f3390b618ce2c737;hpb=46586fc0f24e24166a52c2a0efb3e2ab838eea81;p=monkeysphere.git diff --git a/src/common b/src/common index bb988f7..25f7e4e 100644 --- a/src/common +++ b/src/common @@ -4,6 +4,8 @@ # # Written by # Jameson Rollins +# Jamie McClelland +# Daniel Kahn Gillmor # # Copyright 2008, released under the GPL, version 3 or later @@ -26,14 +28,57 @@ failure() { exit ${2:-'255'} } -# write output to stderr +# write output to stderr based on specified LOG_LEVEL the first +# parameter is the priority of the output, and everything else is what +# is echoed to stderr log() { - echo -n "ms: " >&2 - echo "$@" >&2 -} + local priority + local level + local output + local alllevels + local found= + + # don't include SILENT in alllevels: it's handled separately + # list in decreasing verbosity (all caps). + # separate with $IFS explicitly, since we do some fancy footwork + # elsewhere. + alllevels="DEBUG${IFS}VERBOSE${IFS}INFO${IFS}ERROR" + + # translate lowers to uppers in global log level + LOG_LEVEL=$(echo "$LOG_LEVEL" | tr "[:lower:]" "[:upper:]") + + # just go ahead and return if the log level is silent + if [ "$LOG_LEVEL" = 'SILENT' ] ; then + return + fi + + for level in $alllevels ; do + if [ "$LOG_LEVEL" = "$level" ] ; then + found=true + fi + done + if [ -z "$found" ] ; then + # default to INFO: + LOG_LEVEL=INFO + fi -loge() { - echo "$@" >&2 + # get priority from first parameter, translating all lower to + # uppers + priority=$(echo "$1" | tr "[:lower:]" "[:upper:]") + shift + + # scan over available levels + for level in $alllevels ; do + # output if the log level matches, set output to true + # this will output for all subsequent loops as well. + if [ "$LOG_LEVEL" = "$level" ] ; then + output=true + fi + if [ "$priority" = "$level" -a "$output" = 'true' ] ; then + echo -n "ms: " >&2 + echo "$@" >&2 + fi + done } # cut out all comments(#) and blank lines from standard input @@ -46,6 +91,49 @@ cutline() { head --line="$1" "$2" | tail -1 } +# this is a wrapper for doing lock functions. +# +# it lets us depend on either lockfile-progs (preferred) or procmail's +# lockfile, and should +lock() { + local use_lockfileprogs=true + local action="$1" + local file="$file" + + if ! ( which lockfile-create >/dev/null 2>/dev/null ) ; then + if ! ( which lockfile >/dev/null ); then + failure "Neither lockfile-create nor lockfile are in the path!" + fi + use_lockfileprogs= + fi + + case "$action" in + create) + if [ -n "$use_lockfileprogs" ] ; then + lockfile-create "$file" || failure "unable to lock '$file'" + else + lockfile -r 20 "${file}.lock" || failure "unable to lock '$file'" + fi + ;; + touch) + if [ -n "$use_lockfileprogs" ] ; then + lockfile-touch --oneshot "$file" + else + : Nothing to do here + fi + ;; + remove) + if [ -n "$use_lockfileprogs" ] ; then + lockfile-remove "$file" + else + rm -f "${file}.lock" + fi + ;; + *) + failure "bad argument for lock subfunction '$action'" + esac +} + # check that characters are in a string (in an AND fashion). # used for checking key capability # check_capability capability a [b...] @@ -83,10 +171,101 @@ gpg_escape() { sed 's/:/\\x3a/g' } +# prompt for GPG-formatted expiration, and emit result on stdout +get_gpg_expiration() { + local keyExpire + + keyExpire="$1" + + if [ -z "$keyExpire" ]; then + cat >&2 < = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years +EOF + while [ -z "$keyExpire" ] ; do + read -p "Key is valid for? (0) " keyExpire + if ! test_gpg_expire ${keyExpire:=0} ; then + echo "invalid value" >&2 + unset keyExpire + fi + done + elif ! test_gpg_expire "$keyExpire" ; then + failure "invalid key expiration value '$keyExpire'." + fi + + 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 local string + local tempfile file="$1" string="$2" @@ -101,8 +280,13 @@ remove_line() { # if the string is in the file... if grep -q -F "$string" "$file" 2> /dev/null ; then + tempfile=$(mktemp "${file}.XXXXXXX") || \ + failure "Unable to make temp file '${file}.XXXXXXX'" + # remove the line with the string, and return 0 - grep -v -F "$string" "$file" | sponge "$file" + grep -v -F "$string" "$file" >"$tempfile" + cat "$tempfile" > "$file" + rm "$tempfile" return 0 # otherwise return 1 else @@ -113,6 +297,7 @@ remove_line() { # remove all lines with MonkeySphere strings in file remove_monkeysphere_lines() { local file + local tempfile file="$1" @@ -124,8 +309,13 @@ remove_monkeysphere_lines() { return 1 fi + tempfile=$(mktemp "${file}.XXXXXXX") || \ + failure "Could not make temporary file '${file}.XXXXXXX'." + egrep -v '^MonkeySphere[[:digit:]]{4}(-[[:digit:]]{2}){2}T[[:digit:]]{2}(:[[:digit:]]{2}){2}$' \ - "$file" | sponge "$file" + "$file" >"$tempfile" + cat "$tempfile" > "$file" + rm "$tempfile" } # translate ssh-style path variables %h and %u @@ -278,13 +468,12 @@ gpg_fetch_userid() { userID="$1" - log -n " checking keyserver $KEYSERVER... " + log info " checking keyserver $KEYSERVER... " echo 1,2,3,4,5 | \ gpg --quiet --batch --with-colons \ --command-fd 0 --keyserver "$KEYSERVER" \ --search ="$userID" > /dev/null 2>&1 returnCode="$?" - loge "done." # if the user is the monkeysphere user, then update the # monkeysphere user's trustdb @@ -306,10 +495,13 @@ gpg_fetch_userid() { # (see /usr/share/doc/gnupg/DETAILS.gz) # output is one line for every found key, in the following format: # -# flag:fingerprint +# flag:sshKey # # "flag" is an acceptability flag, 0 = ok, 1 = bad -# "fingerprint" is the fingerprint of the key +# "sshKey" is the translated gpg key +# +# all log output must go to stderr, as stdout is used to pass the +# flag:sshKey to the calling function. # # expects global variable: "MODE" process_user_id() { @@ -348,7 +540,7 @@ process_user_id() { # if the gpg query return code is not 0, return 1 if [ "$?" -ne 0 ] ; then - log " no primary keys found." + log verbose " no primary keys found." return 1 fi @@ -365,21 +557,21 @@ process_user_id() { lastKeyOK= fingerprint= - log " primary key found: $keyid" + log verbose " primary key found: $keyid" # if overall key is not valid, skip if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then - log " - unacceptable primary key validity ($validity)." + log error " - unacceptable primary key validity ($validity)." continue fi # if overall key is disabled, skip if check_capability "$usage" 'D' ; then - log " - key disabled." + log error " - key disabled." continue fi # if overall key capability is not ok, skip if ! check_capability "$usage" $requiredPubCapability ; then - log " - unacceptable primary key capability ($usage)." + log error " - unacceptable primary key capability ($usage)." continue fi @@ -393,42 +585,37 @@ process_user_id() { ;; 'uid') # user ids if [ "$lastKey" != pub ] ; then - log " - got a user ID after a sub key! user IDs should only follow primary keys!" - continue - fi - # don't bother with a uid if there is no valid or reasonable primary key. - if [ "$keyOK" != true ] ; then + log error " - got a user ID after a sub key?! user IDs should only follow primary keys!" continue fi # if an acceptable user ID was already found, skip - if [ "$uidOK" ] ; then + if [ "$uidOK" = 'true' ] ; then continue fi - # if the user ID does not match, skip - if [ "$(echo "$uidfpr" | gpg_unescape)" != "$userID" ] ; then - continue - fi - # if the user ID validity is not ok, skip - if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then + # if the user ID does matches... + if [ "$(echo "$uidfpr" | gpg_unescape)" = "$userID" ] ; then + # and the user ID validity is ok + if [ "$validity" = 'u' -o "$validity" = 'f' ] ; then + # mark user ID acceptable + uidOK=true + fi + else continue fi - # mark user ID acceptable - uidOK=true - # output a line for the primary key # 0 = ok, 1 = bad if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then - log " * acceptable primary key." + log verbose " * acceptable primary key." if [ -z "$sshKey" ] ; then - log " ! primary key could not be translated (not RSA or DSA?)." + log error " ! primary key could not be translated (not RSA or DSA?)." else echo "0:${sshKey}" fi else - log " - unacceptable primary key." + log error " - unacceptable primary key." if [ -z "$sshKey" ] ; then - log " ! primary key could not be translated (not RSA or DSA?)." + log error " ! primary key could not be translated (not RSA or DSA?)." else echo "1:${sshKey}" fi @@ -475,16 +662,16 @@ process_user_id() { # output a line for the sub key # 0 = ok, 1 = bad if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then - log " * acceptable sub key." + log verbose " * acceptable sub key." if [ -z "$sshKey" ] ; then - log " ! sub key could not be translated (not RSA or DSA?)." + log error " ! sub key could not be translated (not RSA or DSA?)." else echo "0:${sshKey}" fi else - log " - unacceptable sub key." + log error " - unacceptable sub key." if [ -z "$sshKey" ] ; then - log " ! sub key could not be translated (not RSA or DSA?)." + log error " ! sub key could not be translated (not RSA or DSA?)." else echo "1:${sshKey}" fi @@ -510,7 +697,7 @@ process_host_known_hosts() { host="$1" userID="ssh://${host}" - log "processing: $host" + log verbose "processing: $host" nKeys=0 nKeysOK=0 @@ -580,11 +767,10 @@ update_known_hosts() { nHostsOK=0 nHostsBAD=0 - # set the trap to remove any lockfiles on exit - trap "lockfile-remove $KNOWN_HOSTS" EXIT - - # create a lockfile on known_hosts - lockfile-create "$KNOWN_HOSTS" + # create a lockfile on known_hosts: + lock create "$KNOWN_HOSTS" + # FIXME: we're discarding any pre-existing EXIT trap; is this bad? + trap "lock remove $KNOWN_HOSTS" EXIT # note pre update file checksum fileCheck="$(file_hash "$KNOWN_HOSTS")" @@ -603,15 +789,16 @@ update_known_hosts() { esac # touch the lockfile, for good measure. - lockfile-touch --oneshot "$KNOWN_HOSTS" + lock touch "$KNOWN_HOSTS" done - # remove the lockfile - lockfile-remove "$KNOWN_HOSTS" + # remove the lockfile and the trap + lock remove "$KNOWN_HOSTS" + trap - EXIT # note if the known_hosts file was updated if [ "$(file_hash "$KNOWN_HOSTS")" != "$fileCheck" ] ; then - log "known_hosts file updated." + log verbose "known_hosts file updated." fi # if an acceptable host was found, return 0 @@ -634,12 +821,12 @@ update_known_hosts() { process_known_hosts() { local hosts - log "processing known_hosts file..." + log verbose "processing known_hosts file..." hosts=$(meat "$KNOWN_HOSTS" | cut -d ' ' -f 1 | grep -v '^|.*$' | tr , ' ' | tr '\n' ' ') if [ -z "$hosts" ] ; then - log "no hosts to process." + log error "no hosts to process." return fi @@ -659,7 +846,7 @@ process_uid_authorized_keys() { userID="$1" - log "processing: $userID" + log verbose "processing: $userID" nKeys=0 nKeysOK=0 @@ -718,11 +905,10 @@ update_authorized_keys() { nIDsOK=0 nIDsBAD=0 - # set the trap to remove any lockfiles on exit - trap "lockfile-remove $AUTHORIZED_KEYS" EXIT - # create a lockfile on authorized_keys - lockfile-create "$AUTHORIZED_KEYS" + lock create "$AUTHORIZED_KEYS" + # FIXME: we're discarding any pre-existing EXIT trap; is this bad? + trap "lock remove $AUTHORIZED_KEYS" EXIT # note pre update file checksum fileCheck="$(file_hash "$AUTHORIZED_KEYS")" @@ -746,15 +932,16 @@ update_authorized_keys() { esac # touch the lockfile, for good measure. - lockfile-touch --oneshot "$AUTHORIZED_KEYS" + lock touch "$AUTHORIZED_KEYS" done - # remove the lockfile - lockfile-remove "$AUTHORIZED_KEYS" + # remove the lockfile and the trap + lock remove "$AUTHORIZED_KEYS" + trap - EXIT # note if the authorized_keys file was updated if [ "$(file_hash "$AUTHORIZED_KEYS")" != "$fileCheck" ] ; then - log "authorized_keys file updated." + log verbose "authorized_keys file updated." fi # if an acceptable id was found, return 0 @@ -781,10 +968,10 @@ process_authorized_user_ids() { authorizedUserIDs="$1" - log "processing authorized_user_ids file..." + log verbose "processing authorized_user_ids file..." if ! meat "$authorizedUserIDs" > /dev/null ; then - log "no user IDs to process." + log error "no user IDs to process." return fi