Merge commit 'dkg/master'
[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     log "OpenPGP keys with*out* full validity found for this host:"
49     log
50
51     # retrieve the actual ssh key
52     sshKeyOffered=$(ssh-keyscan -t rsa -p "$PORT" "$HOST" 2>/dev/null | awk '{ print $2, $3 }')
53
54     userID="ssh://${HOSTP}"
55
56     # output gpg info for (exact) userid and store
57     gpgOut=$(gpg --list-key --fixed-list-mode --with-colon \
58         --with-fingerprint --with-fingerprint \
59         ="$userID" 2>/dev/null)
60
61     # loop over all lines in the gpg output and process.
62     echo "$gpgOut" | cut -d: -f1,2,5,10,12 | \
63     while IFS=: read -r type validity keyid uidfpr usage ; do
64         case $type in
65             'pub'|'sub')
66                 # get the ssh key of the gpg key
67                 sshKeyGPG=$(gpg2ssh "$keyid")
68
69                 # if one of keys found matches the one offered by the
70                 # host, then output info
71                 if [ "$sshKeyGPG" = "$sshKeyOffered" ] ; then
72
73                     # get the fingerprint of the ssh key
74                     tmpkey=$(mktemp ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX)
75                     echo "$sshKeyGPG" > "$tmpkey"
76                     sshFingerprint=$(ssh-keygen -l -f "$tmpkey" | awk '{ print $2 }')
77                     rm -rf "$tmpkey"
78
79                     # output gpg info
80                     gpg --check-sigs \
81                         --list-options show-uid-validity \
82                         "$keyid" >&2
83
84                     # output ssh fingerprint
85                     log "RSA key fingerprint is ${sshFingerprint}."
86                     log "Falling through to standard ssh host checking."
87                     log
88                 fi
89                 ;;
90         esac
91     done
92 }
93
94 ########################################################################
95
96 # export the monkeysphere log level
97 export MONKEYSPHERE_LOG_LEVEL
98
99 if [ "$1" = '--no-connect' ] ; then
100     NO_CONNECT='true'
101     shift 1
102 fi
103
104 HOST="$1"
105 PORT="$2"
106
107 if [ -z "$HOST" ] ; then
108     log "Host not specified."
109     usage
110     exit 255
111 fi
112 if [ -z "$PORT" ] ; then
113     PORT=22
114 fi
115
116 # set the host URI
117 if [ "$PORT" != '22' ] ; then
118     HOSTP="${HOST}:${PORT}"
119 else
120     HOSTP="${HOST}"
121 fi
122 URI="ssh://${HOSTP}"
123
124 # specify keyserver checking.  the behavior of this proxy command is
125 # intentionally different than that of running monkeyesphere normally,
126 # and keyserver checking is intentionally done under certain
127 # circumstances.  This can be overridden by setting the
128 # MONKEYSPHERE_CHECK_KEYSERVER environment variable.
129
130 # if the host is in the gpg keyring...
131 if gpg --list-key ="${URI}" 2>&1 >/dev/null ; then
132     # do not check the keyserver
133     CHECK_KEYSERVER="false"
134
135 # if the host is NOT in the keyring...
136 else
137     # if the host key is found in the known_hosts file...
138     # FIXME: this only works for default known_hosts location
139     hostKey=$(ssh-keygen -F "$HOST" 2>/dev/null)
140
141     if [ "$hostKey" ] ; then
142         # do not check the keyserver
143         # FIXME: more nuanced checking should be done here to properly
144         # take into consideration hosts that join monkeysphere by
145         # converting an existing and known ssh key
146         CHECK_KEYSERVER="false"
147
148     # if the host key is not found in the known_hosts file...
149     else
150         # check the keyserver
151         CHECK_KEYSERVER="true"
152     fi
153 fi
154 # set and export the variable for use by monkeysphere
155 MONKEYSPHERE_CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:="$CHECK_KEYSERVER"}
156 export MONKEYSPHERE_CHECK_KEYSERVER
157
158 # update the known_hosts file for the host
159 monkeysphere update-known_hosts "$HOSTP"
160
161 # output on depending on the return of the update-known_hosts
162 # subcommand, which is (ultimately) the return code of the
163 # update_known_hosts function in common
164 case $? in
165     0)
166         # acceptable host key found so continue to ssh
167         true
168         ;;
169     1)
170         # no hosts at all found so also continue (drop through to
171         # regular ssh host verification)
172         true
173         ;;
174     2)
175         # at least one *bad* host key (and no good host keys) was
176         # found, so output some usefull information
177         output_no_valid_key
178         ;;
179     *)
180         # anything else drop through
181         true
182         ;;
183 esac
184
185 # exec a netcat passthrough to host for the ssh connection
186 if [ -z "$NO_CONNECT" ] ; then
187     if (which nc 2>/dev/null >/dev/null); then
188         exec nc "$HOST" "$PORT"
189     elif (which socat 2>/dev/null >/dev/null); then
190         exec socat STDIO "TCP:$HOST:$PORT"
191     else
192         echo "Neither netcat nor socat found -- could not complete monkeysphere-ssh-proxycommand connection to $HOST:$PORT" >&2
193         exit 255
194     fi
195 fi