add some more informative debug output to key processing.
[monkeysphere.git] / src / monkeysphere-ssh-proxycommand
1 #!/usr/bin/env bash
2
3 # monkeysphere-ssh-proxycommand: MonkeySphere ssh ProxyCommand hook
4 #
5 # The monkeysphere scripts are written by:
6 # Jameson Rollins <jrollins@fifthhorseman.net>
7 #
8 # They are Copyright 2008, and are all released under the GPL, version 3
9 # or later.
10
11 # This is meant to be run as an ssh ProxyCommand to initiate a
12 # monkeysphere known_hosts update before an ssh connection to host is
13 # established.  Can be added to ~/.ssh/config as follows:
14 #  ProxyCommand monkeysphere-ssh-proxycommand %h %p
15
16 ########################################################################
17 PGRM=$(basename $0)
18
19 SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"/usr/share/monkeysphere"}
20 export SYSSHAREDIR
21 . "${SYSSHAREDIR}/common" || exit 1
22
23 ########################################################################
24 # FUNCTIONS
25 ########################################################################
26
27 usage() {
28     cat <<EOF >&2
29 usage: ssh -o ProxyCommand="$(basename $0) %h %p" ...
30 EOF
31 }
32
33 log() {
34     echo "$@" >&2
35 }
36
37 output_no_valid_key() {
38     local sshKeyOffered
39     local userID
40     local type
41     local validity
42     local keyid
43     local uidfpr
44     local usage
45     local sshKeyGPG
46     local sshFingerprint
47
48     userID="ssh://${HOSTP}"
49
50     log "Monkeysphere found only OpenPGP keys for this host with*out* full validity."
51     log "host:                $userID"
52     log
53
54     # retrieve the actual ssh key
55     sshKeyOffered=$(ssh-keyscan -t rsa -p "$PORT" "$HOST" 2>/dev/null | awk '{ print $2, $3 }')
56     # FIXME: should we do any checks for failed keyscans, eg host not
57     # found?
58
59     # output gpg info for userid and store
60     gpgOut=$(gpg --list-key --fixed-list-mode --with-colon \
61         --with-fingerprint --with-fingerprint \
62         ="$userID" 2>/dev/null)
63
64     # find all 'pub' and 'sub' lines in the gpg output, which each
65     # represent a retrieved key for the user ID
66     echo "$gpgOut" | cut -d: -f1,2,5,10,12 | \
67     while IFS=: read -r type validity keyid uidfpr usage ; do
68         case $type in
69             'pub'|'sub')
70                 # get the ssh key of the gpg key
71                 sshKeyGPG=$(gpg2ssh "$keyid")
72
73                 # if one of keys found matches the one offered by the
74                 # host, then output info
75                 if [ "$sshKeyGPG" = "$sshKeyOffered" ] ; then
76
77                     # get the fingerprint of the ssh key
78                     tmpkey=$(mktemp ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX)
79                     echo "$sshKeyGPG" > "$tmpkey"
80                     sshFingerprint=$(ssh-keygen -l -f "$tmpkey" | awk '{ print $2 }')
81                     rm -rf "$tmpkey"
82
83                     # output gpg info
84                     gpg --check-sigs \
85                         --list-options show-uid-validity \
86                         "$keyid" >&2
87
88                     # output ssh fingerprint
89                     log "RSA key fingerprint is ${sshFingerprint}."
90                     log "Falling through to standard ssh host checking."
91                     log
92                 fi
93                 ;;
94         esac
95     done
96 }
97
98 ########################################################################
99
100 # export the monkeysphere log level
101 export MONKEYSPHERE_LOG_LEVEL
102
103 if [ "$1" = '--no-connect' ] ; then
104     NO_CONNECT='true'
105     shift 1
106 fi
107
108 HOST="$1"
109 PORT="$2"
110
111 if [ -z "$HOST" ] ; then
112     log "Host not specified."
113     usage
114     exit 255
115 fi
116 if [ -z "$PORT" ] ; then
117     PORT=22
118 fi
119
120 # set the host URI
121 if [ "$PORT" != '22' ] ; then
122     HOSTP="${HOST}:${PORT}"
123 else
124     HOSTP="${HOST}"
125 fi
126 URI="ssh://${HOSTP}"
127
128 # specify keyserver checking.  the behavior of this proxy command is
129 # intentionally different than that of running monkeyesphere normally,
130 # and keyserver checking is intentionally done under certain
131 # circumstances.  This can be overridden by setting the
132 # MONKEYSPHERE_CHECK_KEYSERVER environment variable.
133
134 # if the host is in the gpg keyring...
135 if gpg --list-key ="${URI}" 2>&1 >/dev/null ; then
136     # do not check the keyserver
137     CHECK_KEYSERVER="false"
138
139 # if the host is NOT in the keyring...
140 else
141     # if the host key is found in the known_hosts file...
142     # FIXME: this only works for default known_hosts location
143     hostKey=$(ssh-keygen -F "$HOST" 2>/dev/null)
144
145     if [ "$hostKey" ] ; then
146         # do not check the keyserver
147         # FIXME: more nuanced checking should be done here to properly
148         # take into consideration hosts that join monkeysphere by
149         # converting an existing and known ssh key
150         CHECK_KEYSERVER="false"
151
152     # if the host key is not found in the known_hosts file...
153     else
154         # check the keyserver
155         CHECK_KEYSERVER="true"
156     fi
157 fi
158 # set and export the variable for use by monkeysphere
159 MONKEYSPHERE_CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:="$CHECK_KEYSERVER"}
160 export MONKEYSPHERE_CHECK_KEYSERVER
161
162 # update the known_hosts file for the host
163 monkeysphere update-known_hosts "$HOSTP"
164
165 # output on depending on the return of the update-known_hosts
166 # subcommand, which is (ultimately) the return code of the
167 # update_known_hosts function in common
168 case $? in
169     0)
170         # acceptable host key found so continue to ssh
171         true
172         ;;
173     1)
174         # no hosts at all found so also continue (drop through to
175         # regular ssh host verification)
176         true
177         ;;
178     2)
179         # at least one *bad* host key (and no good host keys) was
180         # found, so output some usefull information
181         output_no_valid_key
182         ;;
183     *)
184         # anything else drop through
185         true
186         ;;
187 esac
188
189 # exec a netcat passthrough to host for the ssh connection
190 if [ -z "$NO_CONNECT" ] ; then
191     if (which nc 2>/dev/null >/dev/null); then
192         exec nc "$HOST" "$PORT"
193     elif (which socat 2>/dev/null >/dev/null); then
194         exec socat STDIO "TCP:$HOST:$PORT"
195     else
196         echo "Neither netcat nor socat found -- could not complete monkeysphere-ssh-proxycommand connection to $HOST:$PORT" >&2
197         exit 255
198     fi
199 fi