X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=src%2Fshare%2Fcommon;h=c8d44f656c58136be011a0bd80c5af077adeaedb;hb=0fded72147e60d7193c393e6de68493258501e7a;hp=00a10080008571b4cd89b40f10fcc59015f26dfd;hpb=7548a859412f10e68f90ee68f330593d85b090fc;p=monkeysphere.git diff --git a/src/share/common b/src/share/common index 00a1008..c8d44f6 100644 --- a/src/share/common +++ b/src/share/common @@ -8,24 +8,19 @@ # Jamie McClelland # Daniel Kahn Gillmor # -# Copyright 2008, released under the GPL, version 3 or later +# Copyright 2008-2009, 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 -######################################################################## -### COMMON VARIABLES - -# managed directories -SYSCONFIGDIR=${MONKEYSPHERE_SYSCONFIGDIR:-"/etc/monkeysphere"} -export SYSCONFIGDIR - -# monkeysphere version -VERSION=__VERSION__ - ######################################################################## ### UTILITY FUNCTIONS +# output version info +version() { + cat "${SYSSHAREDIR}/VERSION" +} + # failure function. exits with code 255, unless specified otherwise. failure() { [ "$1" ] && echo "$1" >&2 @@ -81,15 +76,48 @@ log() { fi if [ "$priority" = "$level" -a "$output" = 'true' ] ; then if [ "$1" ] ; then - echo -n "ms: " >&2 - echo "$@" >&2 + echo "$@" else - cat >&2 - fi + cat + fi | sed 's/^/'"${LOG_PREFIX}"'/' >&2 fi done } +# run command as monkeysphere user +su_monkeysphere_user() { + # our main goal here is to run the given command as the the + # monkeysphere user, but without prompting for any sort of + # authentication. If this is not possible, we should just fail. + + # FIXME: our current implementation is overly restrictive, because + # there may be some su PAM configurations that would allow su + # "$MONKEYSPHERE_USER" -c "$@" to Just Work without prompting, + # allowing specific users to invoke commands which make use of + # this user. + + # chpst (from runit) would be nice to use, but we don't want to + # introduce an extra dependency just for this. This may be a + # candidate for re-factoring if we switch implementation languages. + + case $(id -un) in + # if monkeysphere user, run the command under bash + "$MONKEYSPHERE_USER") + bash -c "$@" + ;; + + # if root, su command as monkeysphere user + 'root') + su "$MONKEYSPHERE_USER" -c "$@" + ;; + + # otherwise, fail + *) + log error "non-privileged user." + ;; + esac +} + # cut out all comments(#) and blank lines from standard input meat() { grep -v -e "^[[:space:]]*#" -e '^$' "$1" @@ -100,6 +128,16 @@ cutline() { head --line="$1" "$2" | tail -1 } +# make a temporary directory +msmktempdir() { + mktemp -d ${TMPDIR:-/tmp}/monkeysphere.XXXXXXXXXX +} + +# make a temporary file +msmktempfile() { + mktemp ${TMPDIR:-/tmp}/monkeysphere.XXXXXXXXXX +} + # this is a wrapper for doing lock functions. # # it lets us depend on either lockfile-progs (preferred) or procmail's @@ -123,6 +161,7 @@ lock() { else lockfile -r 20 "${file}.lock" || failure "unable to lock '$file'" fi + log debug "lock created on '$file'." ;; touch) if [ -n "$use_lockfileprogs" ] ; then @@ -130,6 +169,7 @@ lock() { else : Nothing to do here fi + log debug "lock touched on '$file'." ;; remove) if [ -n "$use_lockfileprogs" ] ; then @@ -137,6 +177,7 @@ lock() { else rm -f "${file}.lock" fi + log debug "lock removed on '$file'." ;; *) failure "bad argument for lock subfunction '$action'" @@ -234,7 +275,7 @@ get_gpg_expiration() { keyExpire="$1" - if [ -z "$keyExpire" ]; then + if [ -z "$keyExpire" -a "$PROMPT" = 'true' ]; then cat >&2 </dev/null; then + printf 'Launching "%s"\n' "${SSH_ASKPASS:-ssh-askpass}" | log info + printf '(with prompt "%s")\n' "$prompt" | log debug "${SSH_ASKPASS:-ssh-askpass}" "$prompt" > "$fifo" else read -s -p "$prompt" PASS @@ -272,52 +315,6 @@ passphrase_prompt() { 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 @@ -358,12 +355,15 @@ remove_monkeysphere_lines() { file="$1" - if [ -z "$file" ] ; then + # return error if file does not exist + if [ ! -e "$file" ] ; then return 1 fi - if [ ! -e "$file" ] ; then - return 1 + # just return ok if the file is empty, since there aren't any + # lines to remove + if [ ! -s "$file" ] ; then + return 0 fi tempfile=$(mktemp "${file}.XXXXXXX") || \ @@ -417,6 +417,8 @@ check_key_file_permissions() { uname="$1" path="$2" + log debug "checking path permission '$path'..." + # return 255 if cannot stat file if ! stat=$(ls -ld "$path" 2>/dev/null) ; then log error "could not stat path '$path'." @@ -429,18 +431,21 @@ check_key_file_permissions() { # return 1 if path has invalid owner if [ "$owner" != "$uname" -a "$owner" != 'root' ] ; then - log error "improper ownership on path '$path'." + log error "improper ownership on path '$path':" + log error " $owner != ($uname|root)" return 1 fi # return 2 if path has group or other writability if is_write "$gAccess" || is_write "$oAccess" ; then - log error "improper group or other writability on path '$path'." + log error "improper group or other writability on path '$path':" + log error " group: $gAccess, other: $oAcess" return 2 fi # return zero if all clear, or go to next path if [ "$path" = '/' ] ; then + log debug "path ok." return 0 else check_key_file_permissions "$uname" $(dirname "$path") @@ -461,14 +466,23 @@ gpg2ssh() { # output known_hosts line from ssh key ssh2known_hosts() { local host + local port local key - host="$1" + # FIXME this does not properly deal with IPv6 hosts using the + # standard port (because it's unclear whether their final + # colon-delimited address section is a port number or an address + # string) + host=${1%:*} + port=${1##*:} key="$2" - echo -n "$host " - echo -n "$key" | tr -d '\n' - echo " MonkeySphere${DATE}" + # specify the host and port properly for new ssh known_hosts + # format + if [ "$port" != "$host" ] ; then + host="[${host}]:${port}" + fi + printf "%s %s MonkeySphere%s\n" "$host" "$key" "$DATE" } # output authorized_keys line from ssh key @@ -479,41 +493,43 @@ ssh2authorized_keys() { userID="$1" key="$2" - echo -n "$key" | tr -d '\n' - echo " MonkeySphere${DATE} ${userID}" + printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID" } # convert key from gpg to ssh known_hosts format gpg2known_hosts() { local host local keyID + local key host="$1" keyID="$2" + key=$(gpg2ssh "$keyID") + # 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}$' - echo -n "$host " - gpg2ssh "$keyID" | tr -d '\n' - echo " MonkeySphere${DATE}" + printf "%s %s MonkeySphere%s\n" "$host" "$key" "$DATE" } # convert key from gpg to ssh authorized_keys format gpg2authorized_keys() { local userID local keyID + local key userID="$1" keyID="$2" + key=$(gpg2ssh "$keyID") + # 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}$' - gpg2ssh "$keyID" | tr -d '\n' - echo " MonkeySphere${DATE} ${userID}" + printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID" } ### GPG UTILITIES @@ -522,8 +538,8 @@ gpg2authorized_keys() { # FIXME: need to figure out how to retrieve all matching keys # (not just first N (5 in this case)) gpg_fetch_userid() { + local returnCode=0 local userID - local returnCode if [ "$CHECK_KEYSERVER" != 'true' ] ; then return 0 @@ -562,6 +578,7 @@ gpg_fetch_userid() { # # expects global variable: "MODE" process_user_id() { + local returnCode=0 local userID local requiredCapability local requiredPubCapability @@ -593,10 +610,10 @@ process_user_id() { # output gpg info for (exact) userid and store gpgOut=$(gpg --list-key --fixed-list-mode --with-colon \ --with-fingerprint --with-fingerprint \ - ="$userID" 2>/dev/null) + ="$userID" 2>/dev/null) || returnCode="$?" # if the gpg query return code is not 0, return 1 - if [ "$?" -ne 0 ] ; then + if [ "$returnCode" -ne 0 ] ; then log verbose " no primary keys found." return 1 fi @@ -667,14 +684,14 @@ 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 or DSA?)." + log error " ! primary key could not be translated (not RSA?)." else echo "0:${sshKey}" fi else log debug " - unacceptable primary key." if [ -z "$sshKey" ] ; then - log debug " ! primary key could not be translated (not RSA or DSA?)." + log debug " ! primary key could not be translated (not RSA?)." else echo "1:${sshKey}" fi @@ -725,14 +742,14 @@ process_user_id() { if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then log verbose " * acceptable sub key." if [ -z "$sshKey" ] ; then - log error " ! sub key could not be translated (not RSA or DSA?)." + log error " ! sub key could not be translated (not RSA?)." else echo "0:${sshKey}" fi else log debug " - unacceptable sub key." if [ -z "$sshKey" ] ; then - log debug " ! sub key could not be translated (not RSA or DSA?)." + log debug " ! sub key could not be translated (not RSA?)." else echo "1:${sshKey}" fi @@ -756,6 +773,9 @@ process_host_known_hosts() { local sshKey local tmpfile + # set the key processing mode + export MODE='known_hosts' + host="$1" userID="ssh://${host}" @@ -823,6 +843,7 @@ process_host_known_hosts() { # update the known_hosts file for a set of hosts listed on command # line update_known_hosts() { + local returnCode=0 local nHosts local nHostsOK local nHostsBAD @@ -835,6 +856,13 @@ update_known_hosts() { nHostsOK=0 nHostsBAD=0 + # touch the known_hosts file so that the file permission check + # below won't fail upon not finding the file + (umask 0022 && touch "$KNOWN_HOSTS") + + # check permissions on the known_hosts file path + check_key_file_permissions $(whoami) "$KNOWN_HOSTS" || failure + # create a lockfile on known_hosts: lock create "$KNOWN_HOSTS" # FIXME: we're discarding any pre-existing EXIT trap; is this bad? @@ -845,9 +873,9 @@ update_known_hosts() { for host ; do # process the host - process_host_known_hosts "$host" + process_host_known_hosts "$host" || returnCode="$?" # note the result - case "$?" in + case "$returnCode" in 0) nHostsOK=$((nHostsOK+1)) ;; @@ -889,7 +917,13 @@ update_known_hosts() { process_known_hosts() { local hosts - log debug "processing known_hosts file..." + # exit if the known_hosts file does not exist + if [ ! -e "$KNOWN_HOSTS" ] ; then + failure "known_hosts file '$KNOWN_HOSTS' does not exist." + fi + + log debug "processing known_hosts file:" + log debug " $KNOWN_HOSTS" hosts=$(meat "$KNOWN_HOSTS" | cut -d ' ' -f 1 | grep -v '^|.*$' | tr , ' ' | tr '\n' ' ') @@ -912,6 +946,9 @@ process_uid_authorized_keys() { local ok local sshKey + # set the key processing mode + export MODE='authorized_keys' + userID="$1" log verbose "processing: $userID" @@ -961,6 +998,7 @@ process_uid_authorized_keys() { # update the authorized_keys files from a list of user IDs on command # line update_authorized_keys() { + local returnCode=0 local userID local nIDs local nIDsOK @@ -973,6 +1011,12 @@ update_authorized_keys() { nIDsOK=0 nIDsBAD=0 + log debug "updating authorized_keys file:" + log debug " $AUTHORIZED_KEYS" + + # check permissions on the authorized_keys file path + check_key_file_permissions $(whoami) "$AUTHORIZED_KEYS" || failure + # create a lockfile on authorized_keys lock create "$AUTHORIZED_KEYS" # FIXME: we're discarding any pre-existing EXIT trap; is this bad? @@ -987,10 +1031,10 @@ update_authorized_keys() { for userID ; do # process the user ID, change return code if key not found for # user ID - process_uid_authorized_keys "$userID" + process_uid_authorized_keys "$userID" || returnCode="$?" # note the result - case "$?" in + case "$returnCode" in 0) nIDsOK=$((nIDsOK+1)) ;; @@ -1005,6 +1049,8 @@ update_authorized_keys() { # remove the lockfile and the trap lock remove "$AUTHORIZED_KEYS" + + # remove the trap trap - EXIT # note if the authorized_keys file was updated @@ -1036,7 +1082,16 @@ process_authorized_user_ids() { authorizedUserIDs="$1" - log debug "processing authorized_user_ids file..." + # exit if the authorized_user_ids file is empty + if [ ! -e "$authorizedUserIDs" ] ; then + failure "authorized_user_ids file '$authorizedUserIDs' does not exist." + fi + + log debug "processing authorized_user_ids file:" + log debug " $authorizedUserIDs" + + # check permissions on the authorized_user_ids file path + check_key_file_permissions $(whoami) "$authorizedUserIDs" || failure if ! meat "$authorizedUserIDs" > /dev/null ; then log debug " no user IDs to process." @@ -1054,3 +1109,53 @@ process_authorized_user_ids() { update_authorized_keys "${userIDs[@]}" } + +# takes a gpg key or keys on stdin, and outputs a list of +# fingerprints, one per line: +list_primary_fingerprints() { + local fake=$(msmktempdir) + GNUPGHOME="$fake" gpg --no-tty --quiet --import + GNUPGHOME="$fake" gpg --with-colons --fingerprint --list-keys | \ + awk -F: '/^fpr:/{ print $10 }' + rm -rf "$fake" +} + + +check_cruft_file() { + local loc="$1" + local version="$2" + + if [ -e "$loc" ] ; then + printf "! The file '%s' is no longer used by\n monkeysphere (as of version %s), and can be removed.\n\n" "$loc" "$version" | log info + fi +} + +check_upgrade_dir() { + local loc="$1" + local version="$2" + + if [ -d "$loc" ] ; then + printf "The presence of directory '%s' indicates that you have\nnot yet completed a monkeysphere upgrade.\nYou should probably run the following script:\n %s/transitions/%s\n\n" "$loc" "$SYSSHAREDIR" "$version" | log info + fi +} + +## look for cruft from old versions of the monkeysphere, and notice if +## upgrades have not been run: +report_cruft() { + check_upgrade_dir "${SYSCONFIGDIR}/gnupg-host" 0.23 + check_upgrade_dir "${SYSCONFIGDIR}/gnupg-authentication" 0.23 + + check_cruft_file "${SYSCONFIGDIR}/gnupg-authentication.conf" 0.23 + check_cruft_file "${SYSCONFIGDIR}/gnupg-host.conf" 0.23 + + local found= + for foo in "${SYSDATADIR}/backup-from-"*"-transition" ; do + if [ -d "$foo" ] ; then + printf "! %s\n" "$foo" | log info + found=true + fi + done + if [ "$found" ] ; then + printf "The directories above are backups left over from a monkeysphere transition.\nThey may contain copies of sensitive data (host keys, certifier lists), but\nthey are no longer needed by monkeysphere.\nYou may remove them at any time.\n\n" | log info + fi +}