major overhaul of rhesus. it now handles creation of both
[monkeysphere.git] / rhesus / rhesus
1 #!/bin/sh -e
2
3 # rhesus: monkeysphere authorized_keys/known_hosts generating script
4 #
5 # When run as a normal user, no special configuration is needed.
6 #
7 # When run as an administrator to update users' authorized_keys files,
8 # the following environment variables should be defined first:
9 #
10 #  MS_CONF=/etc/monkeysphere/monkeysphere.conf
11 #  USER=foo
12 #
13 # ie:
14 #
15 #  for USER in $(ls -1 /home) ; do
16 #    MS_CONF=/etc/monkeysphere/monkeysphere.conf rhesus --authorized_keys
17 #  done
18 #
19 # Written by
20 # Jameson Rollins <jrollins@fifthhorseman.net>
21 #
22 # Copyright 2008, released under the GPL, version 3 or later
23
24 CMD=$(basename $0)
25
26 usage() {
27 cat <<EOF
28 usage: $CMD -k|--known_hosts
29        $CMD -a|--authorized_keys
30 EOF
31 }
32
33 failure() {
34     echo "$1" >&2
35     exit ${2:-'1'}
36 }
37
38 log() {
39     echo -n "ms: "
40     echo "$@"
41 }
42
43 meat() {
44     grep -v -e "^[[:space:]]*#" -e '^$' "$1"
45 }
46
47 cutline() {
48     head --line="$1" | tail -1
49 }
50
51 # stand in for dkg's gpg2ssh program
52 gpg2ssh() {
53     mode="$1"
54     keyid="$2"
55     if [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then
56         gpgkey2ssh "$keyid" | sed -e "s/COMMENT/$userid/"
57     elif [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then
58         echo -n "$userid "; gpgkey2ssh "$keyid" | sed -e 's/ COMMENT//'
59     fi
60 }
61
62 # expects global variables
63 # mode REQUIRED_KEY_CAPABILITY ids_file key_dir
64 process_keys() {
65     local nlines
66     local n
67     local userid
68     local userid_hash
69     local return
70     local pub_info
71     local key_trust
72     local key_capability
73     local gen_key
74     unset gen_key
75
76     # find number of user ids in auth_user_ids file
77     nlines=$(meat "$ids_file" | wc -l)
78
79     # make sure gpg home exists with proper permissions
80     mkdir -p -m 0700 "$GNUPGHOME"
81
82     # clean out keys file and remake keys directory
83     rm -rf "$key_dir"
84     mkdir -p "$key_dir"
85
86     # loop through all user ids, and generate ssh keys
87     for n in $(seq 1 $nlines) ; do
88
89         # get id
90         userid=$(meat "$ids_file" | cutline "$n" )
91         userid_hash=$(echo "$userid" | sha1sum | awk '{ print $1 }')
92
93         # search for key on keyserver
94         log "validating: '$userid'"
95         return=$(echo 1 | gpg --quiet --batch --command-fd 0 --with-colons --keyserver "$KEYSERVER" --search ="$userid")
96
97         # if the key was found...
98         if [ "$return" ] ; then
99             echo "    key found."
100         
101             # checking key attributes
102             # see /usr/share/doc/gnupg/DETAILS.gz
103             
104             pub_info=$(gpg --fixed-list-mode --with-colons --list-keys --with-fingerprint ="$userid" | grep '^pub:')
105             if [ -z "$pub_info" ] ; then
106                 echo "    error getting pub info -> SKIPPING"
107                 continue
108             fi
109
110             # extract needed fields
111             key_trust=$(echo "$pub_info" | cut -d: -f2)
112             keyid=$(echo "$pub_info" | cut -d: -f5)
113             key_capability=$(echo "$pub_info" | cut -d: -f12)
114             
115             # check if key disabled
116             if  echo "$key_capability" | grep -q '[D]' ; then
117                 echo "    key disabled -> SKIPPING"
118                 continue
119             fi
120
121             # check key capability
122             if  echo "$key_capability" | grep -q '[$REQUIRED_KEY_CAPABILITY]' ; then
123                 echo "    key capability verified ('$key_capability')."
124             else
125                 echo "    unacceptable key capability ('$key_capability') -> SKIPPING"
126                 continue
127             fi
128
129             # if key is not fully trusted exit
130             # (this includes not revoked or expired)
131             # determine trust
132             echo -n "    key "
133             case "$key_trust" in
134                 'i')
135                     echo -n "invalid" ;;
136                 'r')
137                     echo -n "revoked" ;;
138                 'e')
139                     echo -n "expired" ;;
140                 '-'|'q'|'n'|'m')
141                     echo -n "has unacceptable trust" ;;
142                 'f'|'u')
143                     echo -n "fully trusted"
144                     gen_key=true
145                     ;;
146                 *)
147                     echo -n "has unknown trust" ;;
148             esac
149
150             if [ "$gen_key" ] ; then
151                 # convert pgp key to ssh key, and write to cache file
152                 echo -n " -> generating ssh key... "
153                 gpg2ssh "$mode" "$keyid" > "$key_dir"/"$userid_hash"
154                 echo "done."
155             else
156                 echo ". -> SKIPPING"
157             fi
158
159         else
160             echo "    key not found."
161         fi
162     done
163 }
164
165 ########################################################################
166 # MAIN
167 ########################################################################
168
169 if [ -z "$1" ] ; then
170     usage
171     exit 1
172 fi
173
174 # check mode
175 mode="$1"
176 shift 1
177
178 # check user
179 if ! id -u "$USER" > /dev/null 2>&1 ; then
180     failure "invalid user '$USER'."
181 fi
182
183 HOME=$(getent passwd "$USER" | cut -d: -f6)
184
185 MS_HOME=${MS_HOME:-"$HOME"/.config/monkeysphere}
186
187 # load conf file
188 MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf}
189 [ -e "$MS_CONF" ] && . "$MS_CONF"
190
191 # set config variable defaults
192 STAGING_AREA=${STAGING_AREA:-"$MS_HOME"}
193 AUTH_HOST_FILE=${AUTH_HOST_FILE:-"$MS_HOME"/auth_host_ids}
194 AUTH_USER_FILE=${AUTH_USER_FILE:-"$MS_HOME"/auth_user_ids}
195 GNUPGHOME=${GNUPGHOME:-"$HOME"/.gnupg}
196 KEYSERVER=${KEYSERVER:-subkeys.pgp.net}
197 REQUIRED_KEY_CAPABILITY=${REQUIRED_KEY_CAPABILITY:-'a'}
198
199 export USER
200 export GNUPGHOME
201
202 host_keys_dir="$STAGING_AREA"/host_keys
203 user_keys_dir="$STAGING_AREA"/user_keys
204 known_hosts_stage_file="$STAGING_AREA"/known_hosts
205 authorized_keys_stage_file="$STAGING_AREA"/authorized_keys
206
207 # act on mode
208 if [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then
209
210     # set variables for process_keys command
211     ids_file="$AUTH_HOST_FILE"
212     log -n "[$USER] "
213     if [ ! -s "$ids_file" ] ; then
214         echo "auth_host_ids file is empty or does not exist."
215         exit
216     else
217         echo "updating known_hosts file..."
218     fi
219     key_dir="$host_keys_dir"
220
221     # process the keys
222     process_keys
223
224     # write known_hosts file
225     > "$known_hosts_stage_file"
226     if [ $(ls "$key_dir") ]  ; then
227         log -n "writing known_hosts stage file..."
228         cat "$key_dir"/* > "$known_hosts_stage_file"
229         echo "done."
230     else
231         log "no gpg keys to add to known_hosts file."
232     fi
233     if [ -s "$HOME"/.ssh/known_hosts ] ; then
234         log -n "adding user known_hosts file... "
235         cat "$HOME"/.ssh/known_hosts >> "$known_hosts_stage_file"
236         echo "done."
237     fi
238     log "known_hosts file updated: $known_hosts_stage_file"
239
240 elif [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then
241
242     # set variables for process_keys command
243     ids_file="$AUTH_USER_FILE"
244     log -n "[$USER] "
245     if [ ! -s "$ids_file" ] ; then
246         echo "auth_user_ids file is empty or does not exist."
247         exit
248     else
249         echo "updating authorized_keys file:"
250     fi
251     key_dir="$user_keys_dir"
252         
253     # process the keys
254     process_keys
255
256     # write authorized_keys file
257     > "$authorized_keys_stage_file"
258     if [ $(ls "$key_dir") ]  ; then
259         log -n "writing ms authorized_keys file... "
260         cat "$key_dir"/* > "$authorized_keys_stage_file"
261         echo "done."
262     else
263         log "no gpg keys to add to authorized_keys file."
264     fi
265     if [ -s "$HOME"/.ssh/authorized_keys ] ; then
266         log -n "adding user authorized_keys file... "
267         cat "$HOME"/.ssh/authorized_keys >> "$authorized_keys_stage_file"
268         echo "done."
269     fi
270     log "authorized_keys file updated: $authorized_keys_stage_file"
271 fi