1#!/usr/bin/env bash 2 3# monkeysphere: Monkeysphere client tool 4# 5# The monkeysphere scripts are written by: 6# Jameson Rollins <jrollins@finestructure.net> 7# Jamie McClelland <jm@mayfirst.org> 8# Daniel Kahn Gillmor <dkg@fifthhorseman.net> 9# Micah Anderson <micah@riseup.net> 10# 11# They are Copyright 2008-2009, and are all released under the GPL, version 3 12# or later. 13 14######################################################################## 15set -e 16 17PGRM=$(basename $0) 18 19SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} 20export SYSSHAREDIR 21. "${SYSSHAREDIR}/defaultenv" 22. "${SYSSHAREDIR}/common" 23 24# sharedir for host functions 25MSHAREDIR="${SYSSHAREDIR}/m" 26 27# UTC date in ISO 8601 format if needed 28DATE=$(date -u '+%FT%T') 29 30# unset some environment variables that could screw things up 31unset GREP_OPTIONS 32 33# set the file creation mask to be only owner rw 34umask 077 35 36######################################################################## 37# FUNCTIONS 38######################################################################## 39 40usage() { 41 cat <<EOF >&2 42usage: $PGRM <subcommand> [options] [args] 43Monkeysphere client tool. 44 45subcommands: 46 update-known_hosts (k) [HOST]... update known_hosts file 47 update-authorized_keys (a) update authorized_keys file 48 ssh-proxycommand HOST [PORT] monkeysphere ssh ProxyCommand 49 --no-connect do not make TCP connection to host 50 subkey-to-ssh-agent (s) store authentication subkey in ssh-agent 51 52 keys-for-userid (u) USERID output valid ssh keys for given user id 53 sshfprs-for-userid USERID output ssh fingerprints for given user id 54 gen-subkey (g) [KEYID] generate an authentication subkey 55 --length (-l) BITS key length in bits (2048) 56 57 version (v) show version number 58 help (h,?) this help 59 60EOF 61} 62 63# user gpg command to define common options 64gpg_user() { 65 LC_ALL=C "${GPG:-gpg2}" --fixed-list-mode --no-greeting --quiet --no-tty "$@" 66} 67 68# output the ssh fingerprint of a gpg key 69gpg_ssh_fingerprint() { 70 keyid="$1" 71 gpg_user --export "$keyid" --no-armor | "$SYSSHAREDIR/keytrans" openpgp2sshfpr "$keyid" 72} 73 74# take a secret key ID and check that only zero or one ID is provided, 75# and that it corresponds to only a single secret key ID 76check_gpg_sec_key_id() { 77 local gpgSecOut 78 79 case "$#" in 80 0) 81 gpgSecOut=$(gpg_user --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:') 82 ;; 83 1) 84 gpgSecOut=$(gpg_user --list-secret-keys --with-colons "$1" | egrep '^sec:') || failure 85 ;; 86 *) 87 failure "You must specify only a single primary key ID." 88 ;; 89 esac 90 91 # check that only a single secret key was found 92 case $(echo "$gpgSecOut" | grep -c '^sec:') in 93 0) 94 failure "No secret keys found. Create an OpenPGP key with the following command: 95 gpg2 --gen-key" 96 ;; 97 1) 98 echo "$gpgSecOut" | cut -d: -f5 99 ;; 100 *) 101 local seckeys=$(echo "$gpgSecOut" | cut -d: -f5) 102 failure "Multiple primary secret keys found: 103$seckeys 104Please specify which primary key to use." 105 ;; 106 esac 107} 108 109# check that a valid authentication subkey does not already exist 110check_gpg_authentication_subkey() { 111 local keyID 112 local IFS 113 local line 114 local type 115 local validity 116 local usage 117 118 keyID="$1" 119 120 # check that a valid authentication key does not already exist 121 IFS=$'\n' 122 for line in $(gpg_user --list-keys --with-colons "$keyID") ; do 123 type=$(echo "$line" | cut -d: -f1) 124 validity=$(echo "$line" | cut -d: -f2) 125 usage=$(echo "$line" | cut -d: -f12) 126 127 # look at keys only 128 if [ "$type" != 'pub' -a "$type" != 'sub' ] ; then 129 continue 130 fi 131 # check for authentication capability 132 if ! check_capability "$usage" 'a' ; then 133 continue 134 fi 135 # if authentication key is valid, prompt to continue 136 if [ "$validity" = 'u' ] ; then 137 echo "A valid authentication key already exists for primary key '$keyID'." 1>&2 138 if [ "$PROMPT" != "false" ] ; then 139 printf "Are you sure you would like to generate another one? (y/N) " >&2 140 read OK; OK=${OK:N} 141 if [ "${OK/y/Y}" != 'Y' ] ; then 142 failure "aborting." 143 fi 144 break 145 else 146 failure "aborting." 147 fi 148 fi 149 done 150} 151 152######################################################################## 153# MAIN 154######################################################################## 155 156# set unset default variables 157GNUPGHOME=${GNUPGHOME:="${HOME}/.gnupg"} 158KNOWN_HOSTS="${HOME}/.ssh/known_hosts" 159HASH_KNOWN_HOSTS="false" 160AUTHORIZED_KEYS="${HOME}/.ssh/authorized_keys" 161 162# unset the check keyserver variable, since that needs to have 163# different defaults for the different functions 164unset CHECK_KEYSERVER 165 166# load global config 167[ -r "${SYSCONFIGDIR}/monkeysphere.conf" ] \ 168 && . "${SYSCONFIGDIR}/monkeysphere.conf" 169 170# set monkeysphere home directory 171MONKEYSPHERE_HOME=${MONKEYSPHERE_HOME:="${HOME}/.monkeysphere"} 172mkdir -p -m 0700 "$MONKEYSPHERE_HOME" 173 174# load local config 175[ -e ${MONKEYSPHERE_CONFIG:="${MONKEYSPHERE_HOME}/monkeysphere.conf"} ] \ 176 && . "$MONKEYSPHERE_CONFIG" 177 178# set empty config variables with ones from the environment 179GNUPGHOME=${MONKEYSPHERE_GNUPGHOME:=$GNUPGHOME} 180LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL} 181KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER} 182# if keyserver not specified in env or conf, then look in gpg.conf 183if [ -z "$KEYSERVER" ] ; then 184 if [ -f "${GNUPGHOME}/gpg.conf" ] ; then 185 KEYSERVER=$(grep -e "^[[:space:]]*keyserver " "${GNUPGHOME}/gpg.conf" | tail -1 | awk '{ print $2 }') 186 fi 187fi 188PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT} 189KNOWN_HOSTS=${MONKEYSPHERE_KNOWN_HOSTS:=$KNOWN_HOSTS} 190HASH_KNOWN_HOSTS=${MONKEYSPHERE_HASH_KNOWN_HOSTS:=$HASH_KNOWN_HOSTS} 191AUTHORIZED_KEYS=${MONKEYSPHERE_AUTHORIZED_KEYS:=$AUTHORIZED_KEYS} 192STRICT_MODES=${MONKEYSPHERE_STRICT_MODES:=$STRICT_MODES} 193 194# other variables not in config file 195AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:="${MONKEYSPHERE_HOME}/authorized_user_ids"} 196REQUIRED_HOST_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_HOST_KEY_CAPABILITY:="a"} 197REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"} 198# note that only using '=' instead of ':=' tests only if the variable 199# in unset, not if it's "null" 200LOG_PREFIX=${MONKEYSPHERE_LOG_PREFIX='ms: '} 201 202# export GNUPGHOME and make sure gpg home exists with proper 203# permissions 204export GNUPGHOME 205mkdir -p -m 0700 "$GNUPGHOME" 206export LOG_LEVEL 207export LOG_PREFIX 208 209if [ "$#" -eq 0 ] ; then 210 usage 211 failure "Please supply a subcommand." 212fi 213 214# get subcommand 215COMMAND="$1" 216shift 217 218case $COMMAND in 219 'update-known_hosts'|'update-known-hosts'|'k') 220 # whether or not to check keyservers 221 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} 222 223 source "${MSHAREDIR}/update_known_hosts" 224 225 # if hosts are specified on the command line, process just 226 # those hosts 227 if [ "$1" ] ; then 228 update_known_hosts "$@" 229 230 # otherwise, if no hosts are specified, process every host 231 # in the user's known_hosts file 232 else 233 process_known_hosts 234 fi 235 ;; 236 237 'update-authorized_keys'|'update-authorized-keys'|'a') 238 # whether or not to check keyservers 239 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} 240 source "${MSHAREDIR}/update_authorized_keys" 241 update_authorized_keys 242 ;; 243 244 'import-subkey'|'import'|'i') 245 source "${MSHAREDIR}/import_subkey" 246 import_subkey "$@" 247 ;; 248 249 'gen-subkey'|'g') 250 source "${MSHAREDIR}/gen_subkey" 251 gen_subkey "$@" 252 ;; 253 254 'ssh-proxycommand'|'p') 255 source "${MSHAREDIR}/ssh_proxycommand" 256 ssh_proxycommand "$@" 257 ;; 258 259 'subkey-to-ssh-agent'|'s') 260 source "${MSHAREDIR}/subkey_to_ssh_agent" 261 subkey_to_ssh_agent "$@" 262 ;; 263 264 'sshfpr') 265 echo "Warning: 'sshfpr' is deprecated. Please use 'sshfprs-for-userid' instead." >&2 266 gpg_ssh_fingerprint "$@" 267 ;; 268 269 'keys-for-userid'|'u') 270 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} 271 source "${MSHAREDIR}/keys_for_userid" 272 keys_for_userid "$@" 273 ;; 274 275 'sshfprs-for-userid') 276 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} 277 source "${MSHAREDIR}/keys_for_userid" 278 keys_for_userid "$@" | "$SYSSHAREDIR/keytrans" sshfpr 279 ;; 280 281 'keys-from-userid') 282 echo "Warning: 'keys-from-userid' is deprecated. Please use 'keys-for-userid' instead." >&2 283 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} 284 source "${MSHAREDIR}/keys_for_userid" 285 keys_for_userid "$@" 286 ;; 287 288 'version'|'--version'|'v') 289 version 290 ;; 291 292 'help'|'--help'|'-h'|'h'|'?') 293 usage 294 ;; 295 296 *) 297 failure "Unknown command: '$COMMAND' 298Try '$PGRM help' for usage." 299 ;; 300esac 301