- break out monkeysphere-server command into
[monkeysphere.git] / src / monkeysphere-host
1 #!/usr/bin/env bash
2
3 # monkeysphere-host: Monkeysphere host admin tool
4 #
5 # The monkeysphere scripts are written by:
6 # Jameson Rollins <jrollins@fifthhorseman.net>
7 # Jamie McClelland <jm@mayfirst.org>
8 # Daniel Kahn Gillmor <dkg@fifthhorseman.net>
9 #
10 # They are Copyright 2008, and are all released under the GPL, version 3
11 # or later.
12
13 ########################################################################
14 PGRM=$(basename $0)
15
16 SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"/usr/share/monkeysphere"}
17 export SYSSHAREDIR
18 . "${SYSSHAREDIR}/common" || exit 1
19
20 SYSDATADIR=${MONKEYSPHERE_SYSDATADIR:-"/var/lib/monkeysphere/host"}
21 export SYSDATADIR
22
23 # monkeysphere temp directory, in sysdatadir to enable atomic moves of
24 # authorized_keys files
25 MSTMPDIR="${SYSDATADIR}/tmp"
26 export MSTMPDIR
27
28 # UTC date in ISO 8601 format if needed
29 DATE=$(date -u '+%FT%T')
30
31 # unset some environment variables that could screw things up
32 unset GREP_OPTIONS
33
34 # default return code
35 RETURN=0
36
37 ########################################################################
38 # FUNCTIONS
39 ########################################################################
40
41 usage() {
42     cat <<EOF >&2
43 usage: $PGRM <subcommand> [options] [args]
44 Monkeysphere host admin tool.
45
46 subcommands:
47  show-key (s)                        output all host key information
48  extend-key (e) EXPIRE               extend host key expiration
49  add-hostname (n+) NAME[:PORT]       add hostname user ID to host key
50  revoke-hostname (n-) NAME[:PORT]    revoke hostname user ID
51  add-revoker (o) FINGERPRINT         add a revoker to the host key
52  revoke-key (r)                      revoke host key
53  publish-key (p)                     publish server host key to keyserver
54
55  expert
56   import-key (i)                     import existing ssh key to gpg
57    --hostname (-h) NAME[:PORT]         hostname for key user ID
58    --keyfile (-f) FILE                 key file to import
59    --expire (-e) EXPIRE                date to expire
60   gen-key (g)                        generate gpg key for the host
61    --hostname (-h) NAME[:PORT]         hostname for key user ID
62    --length (-l) BITS                  key length in bits (2048)
63    --expire (-e) EXPIRE                date to expire
64    --revoker (-r) FINGERPRINT          add a revoker
65   diagnostics (d)                    monkeysphere host status
66
67  version (v)                         show version number
68  help (h,?)                          this help
69
70 EOF
71 }
72
73 # function to run command as monkeysphere user
74 su_monkeysphere_user() {
75     # if the current user is the monkeysphere user, then just eval
76     # command
77     if [ $(id -un) = "$MONKEYSPHERE_USER" ] ; then
78         eval "$@"
79
80     # otherwise su command as monkeysphere user
81     else
82         su "$MONKEYSPHERE_USER" -c "$@"
83     fi
84 }
85
86 # function to interact with the host gnupg keyring
87 gpg_host() {
88     local returnCode
89
90     GNUPGHOME="$GNUPGHOME_HOST"
91     export GNUPGHOME
92
93     # NOTE: we supress this warning because we need the monkeysphere
94     # user to be able to read the host pubring.  we realize this might
95     # be problematic, but it's the simplest solution, without too much
96     # loss of security.
97     gpg --no-permission-warning "$@"
98     returnCode="$?"
99
100     # always reset the permissions on the host pubring so that the
101     # monkeysphere user can read the trust signatures
102     chgrp "$MONKEYSPHERE_USER" "${GNUPGHOME_HOST}/pubring.gpg"
103     chmod g+r "${GNUPGHOME_HOST}/pubring.gpg"
104     
105     return "$returnCode"
106 }
107
108 # check if user is root
109 is_root() {
110     [ $(id -u 2>/dev/null) = '0' ]
111 }
112
113 # check that user is root, for functions that require root access
114 check_user() {
115     is_root || failure "You must be root to run this command."
116 }
117
118 # output just key fingerprint
119 fingerprint_server_key() {
120     # set the pipefail option so functions fails if can't read sec key
121     set -o pipefail
122
123     gpg_host --list-secret-keys --fingerprint \
124         --with-colons --fixed-list-mode 2> /dev/null | \
125         grep '^fpr:' | head -1 | cut -d: -f10 2>/dev/null
126 }
127
128 # function to check for host secret key
129 check_host_keyring() {
130     fingerprint_server_key >/dev/null \
131         || failure "You don't appear to have a Monkeysphere host key on this server.  Please run 'monkeysphere-server gen-key' first."
132 }
133
134 # output key information
135 show_server_key() {
136     local fingerprintPGP
137     local fingerprintSSH
138     local ret=0
139
140     # FIXME: you shouldn't have to be root to see the host key fingerprint
141     if is_root ; then
142         check_host_keyring
143         fingerprintPGP=$(fingerprint_server_key)
144         gpg_authentication "--fingerprint --list-key --list-options show-unusable-uids $fingerprintPGP" 2>/dev/null
145         echo "OpenPGP fingerprint: $fingerprintPGP"
146     else
147         log info "You must be root to see host OpenPGP fingerprint."
148         ret='1'
149     fi
150
151     if [ -f "${SYSDATADIR}/ssh_host_rsa_key.pub" ] ; then
152         fingerprintSSH=$(ssh-keygen -l -f "${SYSDATADIR}/ssh_host_rsa_key.pub" | \
153             awk '{ print $1, $2, $4 }')
154         echo "ssh fingerprint: $fingerprintSSH"
155     else
156         log info "SSH host key not found."
157         ret='1'
158     fi
159
160     return $ret
161 }
162
163 # import an existing ssh key to a gpg key
164 import_key() {
165     local hostName=$(hostname -f)
166     local keyFile="/etc/ssh/ssh_host_rsa_key"
167     local keyExpire
168     local userID
169
170     # check for presense of secret key
171     # FIXME: is this the proper test to be doing here?
172     fingerprint_server_key >/dev/null \
173         && failure "An OpenPGP host key already exists."
174
175     # get options
176     while true ; do
177         case "$1" in
178             -h|--hostname)
179                 hostName="$2"
180                 shift 2
181                 ;;
182             -f|--keyfile)
183                 keyFile="$2"
184                 shift 2
185                 ;;
186             -e|--expire)
187                 keyExpire="$2"
188                 shift 2
189                 ;;
190             *)
191                 if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then
192                     failure "Unknown option '$1'.
193 Type '$PGRM help' for usage."
194                 fi
195                 break
196                 ;;
197         esac
198     done
199
200     if [ ! -f "$keyFile" ] ; then
201         failure "SSH secret key file '$keyFile' not found."
202     fi
203
204     userID="ssh://${hostName}"
205
206     # prompt about key expiration if not specified
207     keyExpire=$(get_gpg_expiration "$keyExpire")
208
209     echo "The following key parameters will be used for the host private key:"
210     echo "Import: $keyFile"
211     echo "Name-Real: $userID"
212     echo "Expire-Date: $keyExpire"
213
214     read -p "Import key? (Y/n) " OK; OK=${OK:=Y}
215     if [ ${OK/y/Y} != 'Y' ] ; then
216         failure "aborting."
217     fi
218
219     log verbose "importing ssh key..."
220     # translate ssh key to a private key
221     (umask 077 && \
222         pem2openpgp "$userID" "$keyExpire" < "$sshKey" | gpg_host --import)
223
224     # find the key fingerprint of the newly converted key
225     fingerprint=$(fingerprint_server_key)
226
227     # export host ownertrust to authentication keyring
228     log verbose "setting ultimate owner trust for host key..."
229     echo "${fingerprint}:6:" | gpg_host "--import-ownertrust"
230     echo "${fingerprint}:6:" | gpg_authentication "--import-ownertrust"
231
232     # export public key to file
233     gpg_authentication "--export-options export-minimal --armor --export 0x${fingerprint}\!" > "${SYSDATADIR}/ssh_host_rsa_key.pub.gpg"
234     log info "SSH host public key in OpenPGP form: ${SYSDATADIR}/ssh_host_rsa_key.pub.gpg"
235
236     # show info about new key
237     show_server_key
238 }
239
240 # generate server gpg key
241 gen_key() {
242     local keyType="RSA"
243     local keyLength="2048"
244     local keyUsage="auth"
245     local keyExpire
246     local revoker
247     local hostName=$(hostname -f)
248     local userID
249     local keyParameters
250     local fingerprint
251
252     # check for presense of secret key
253     # FIXME: is this the proper test to be doing here?
254     fingerprint_server_key >/dev/null \
255         && failure "An OpenPGP host key already exists."
256
257     # get options
258     while true ; do
259         case "$1" in
260             -h|--hostname)
261                 hostName="$2"
262                 shift 2
263                 ;;
264             -l|--length)
265                 keyLength="$2"
266                 shift 2
267                 ;;
268             -e|--expire)
269                 keyExpire="$2"
270                 shift 2
271                 ;;
272             -r|--revoker)
273                 revoker="$2"
274                 shift 2
275                 ;;
276             *)
277                 if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then
278                     failure "Unknown option '$1'.
279 Type '$PGRM help' for usage."
280                 fi
281                 break
282                 ;;
283         esac
284     done
285
286     userID="ssh://${hostName}"
287
288     # prompt about key expiration if not specified
289     keyExpire=$(get_gpg_expiration "$keyExpire")
290
291     # set key parameters
292     keyParameters=\
293 "Key-Type: $keyType
294 Key-Length: $keyLength
295 Key-Usage: $keyUsage
296 Name-Real: $userID
297 Expire-Date: $keyExpire"
298
299     # add the revoker field if specified
300     # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key.
301     # FIXME: key is marked "sensitive"?  is this appropriate?
302     if [ "$revoker" ] ; then
303         keyParameters=\
304 "${keyParameters}
305 Revoker: 1:${revoker} sensitive"
306     fi
307
308     echo "The following key parameters will be used for the host private key:"
309     echo "$keyParameters"
310
311     read -p "Generate key? (Y/n) " OK; OK=${OK:=Y}
312     if [ ${OK/y/Y} != 'Y' ] ; then
313         failure "aborting."
314     fi
315
316     # add commit command
317     # must include blank line!
318     keyParameters=\
319 "${keyParameters}
320
321 %commit
322 %echo done"
323
324     log verbose "generating host key..."
325     echo "$keyParameters" | gpg_host --batch --gen-key
326
327     # find the key fingerprint of the newly generated key
328     fingerprint=$(fingerprint_server_key)
329
330     # export host ownertrust to authentication keyring
331     log verbose "setting ultimate owner trust for host key..."
332     echo "${fingerprint}:6:" | gpg_authentication "--import-ownertrust"
333
334     # translate the private key to ssh format, and export to a file
335     # for sshs usage.
336     # NOTE: assumes that the primary key is the proper key to use
337     (umask 077 && \
338         gpg_host --export-secret-key "$fingerprint" | \
339         openpgp2ssh "$fingerprint" > "${SYSDATADIR}/ssh_host_rsa_key")
340     log info "SSH host private key output to file: ${SYSDATADIR}/ssh_host_rsa_key"
341     ssh-keygen -y -f "${SYSDATADIR}/ssh_host_rsa_key" > "${SYSDATADIR}/ssh_host_rsa_key.pub"
342     log info "SSH host public key output to file: ${SYSDATADIR}/ssh_host_rsa_key.pub"
343     gpg_authentication "--export-options export-minimal --armor --export 0x${fingerprint}\!" > "${SYSDATADIR}/ssh_host_rsa_key.pub.gpg"
344     log info "SSH host public key in OpenPGP form: ${SYSDATADIR}/ssh_host_rsa_key.pub.gpg"
345
346     # show info about new key
347     show_server_key
348 }
349
350 # extend the lifetime of a host key:
351 extend_key() {
352     local fpr=$(fingerprint_server_key)
353     local extendTo="$1"
354
355     # get the new expiration date
356     extendTo=$(get_gpg_expiration "$extendTo")
357
358     gpg_host --quiet --command-fd 0 --edit-key "$fpr" <<EOF 
359 expire
360 $extendTo
361 save
362 EOF
363
364     echo
365     echo "NOTE: Host key expiration date adjusted, but not yet published."
366     echo "Run '$PGRM publish-key' to publish the new expiration date."
367 }
368
369 # add hostname user ID to server key
370 add_hostname() {
371     local userID
372     local fingerprint
373     local tmpuidMatch
374     local line
375     local adduidCommand
376
377     if [ -z "$1" ] ; then
378         failure "You must specify a hostname to add."
379     fi
380
381     userID="ssh://${1}"
382
383     fingerprint=$(fingerprint_server_key)
384
385     # match to only ultimately trusted user IDs
386     tmpuidMatch="u:$(echo $userID | gpg_escape)"
387
388     # find the index of the requsted user ID
389     # NOTE: this is based on circumstantial evidence that the order of
390     # this output is the appropriate index
391     if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \
392         | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then
393         failure "Host userID '$userID' already exists."
394     fi
395
396     echo "The following user ID will be added to the host key:"
397     echo "  $userID"
398     read -p "Are you sure you would like to add this user ID? (y/N) " OK; OK=${OK:=N}
399     if [ ${OK/y/Y} != 'Y' ] ; then
400         failure "User ID not added."
401     fi
402
403     # edit-key script command to add user ID
404     adduidCommand=$(cat <<EOF
405 adduid
406 $userID
407
408
409 save
410 EOF
411 )
412
413     # execute edit-key script
414     if echo "$adduidCommand" | \
415         gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then
416
417         # update the trustdb for the authentication keyring
418         gpg_authentication "--check-trustdb"
419
420         show_server_key
421
422         echo
423         echo "NOTE: User ID added to key, but key not published."
424         echo "Run '$PGRM publish-key' to publish the new user ID."
425     else
426         failure "Problem adding user ID."
427     fi
428 }
429
430 # revoke hostname user ID to server key
431 revoke_hostname() {
432     local userID
433     local fingerprint
434     local tmpuidMatch
435     local line
436     local uidIndex
437     local message
438     local revuidCommand
439
440     if [ -z "$1" ] ; then
441         failure "You must specify a hostname to revoke."
442     fi
443
444     echo "WARNING: There is a known bug in this function."
445     echo "This function has been known to occasionally revoke the wrong user ID."
446     echo "Please see the following bug report for more information:"
447     echo "http://web.monkeysphere.info/bugs/revoke-hostname-revoking-wrong-userid/"
448     read -p "Are you sure you would like to proceed? (y/N) " OK; OK=${OK:=N}
449     if [ ${OK/y/Y} != 'Y' ] ; then
450         failure "aborting."
451     fi
452
453     userID="ssh://${1}"
454
455     fingerprint=$(fingerprint_server_key)
456
457     # match to only ultimately trusted user IDs
458     tmpuidMatch="u:$(echo $userID | gpg_escape)"
459
460     # find the index of the requsted user ID
461     # NOTE: this is based on circumstantial evidence that the order of
462     # this output is the appropriate index
463     if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \
464         | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then
465         uidIndex=${line%%:*}
466     else
467         failure "No non-revoked user ID '$userID' is found."
468     fi
469
470     echo "The following host key user ID will be revoked:"
471     echo "  $userID"
472     read -p "Are you sure you would like to revoke this user ID? (y/N) " OK; OK=${OK:=N}
473     if [ ${OK/y/Y} != 'Y' ] ; then
474         failure "User ID not revoked."
475     fi
476
477     message="Hostname removed by monkeysphere-server $DATE"
478
479     # edit-key script command to revoke user ID
480     revuidCommand=$(cat <<EOF
481 $uidIndex
482 revuid
483 y
484 4
485 $message
486
487 y
488 save
489 EOF
490         )       
491
492     # execute edit-key script
493     if echo "$revuidCommand" | \
494         gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then
495
496         # update the trustdb for the authentication keyring
497         gpg_authentication "--check-trustdb"
498
499         show_server_key
500
501         echo
502         echo "NOTE: User ID revoked, but revocation not published."
503         echo "Run '$PGRM publish-key' to publish the revocation."
504     else
505         failure "Problem revoking user ID."
506     fi
507 }
508
509 # add a revoker to the host key
510 add_revoker() {
511     # FIXME: implement!
512     failure "not implemented yet!"
513 }
514
515 # revoke the host key
516 revoke_key() {
517     # FIXME: implement!
518     failure "not implemented yet!"
519 }
520
521 # publish server key to keyserver
522 publish_server_key() {
523     read -p "Really publish host key to $KEYSERVER? (y/N) " OK; OK=${OK:=N}
524     if [ ${OK/y/Y} != 'Y' ] ; then
525         failure "key not published."
526     fi
527
528     # find the key fingerprint
529     fingerprint=$(fingerprint_server_key)
530
531     # publish host key
532     gpg_authentication "--keyserver $KEYSERVER --send-keys '0x${fingerprint}!'"
533 }
534
535 diagnostics() {
536 #  * check on the status and validity of the key and public certificates
537     local seckey
538     local keysfound
539     local curdate
540     local warnwindow
541     local warndate
542     local create
543     local expire
544     local uid
545     local fingerprint
546     local badhostkeys
547     local sshd_config
548     local problemsfound=0
549
550     # FIXME: what's the correct, cross-platform answer?
551     sshd_config=/etc/ssh/sshd_config
552     seckey=$(gpg_host --list-secret-keys --fingerprint --with-colons --fixed-list-mode)
553     keysfound=$(echo "$seckey" | grep -c ^sec:)
554     curdate=$(date +%s)
555     # warn when anything is 2 months away from expiration
556     warnwindow='2 months'
557     warndate=$(advance_date $warnwindow +%s)
558
559     if ! id monkeysphere >/dev/null ; then
560         echo "! No monkeysphere user found!  Please create a monkeysphere system user with bash as its shell."
561         problemsfound=$(($problemsfound+1))
562     fi
563
564     if ! [ -d "$SYSDATADIR" ] ; then
565         echo "! no $SYSDATADIR directory found.  Please create it."
566         problemsfound=$(($problemsfound+1))
567     fi
568
569     echo "Checking host GPG key..."
570     if (( "$keysfound" < 1 )); then
571         echo "! No host key found."
572         echo " - Recommendation: run 'monkeysphere-server gen-key'"
573         problemsfound=$(($problemsfound+1))
574     elif (( "$keysfound" > 1 )); then
575         echo "! More than one host key found?"
576         # FIXME: recommend a way to resolve this
577         problemsfound=$(($problemsfound+1))
578     else
579         create=$(echo "$seckey" | grep ^sec: | cut -f6 -d:)
580         expire=$(echo "$seckey" | grep ^sec: | cut -f7 -d:)
581         fingerprint=$(echo "$seckey" | grep ^fpr: | head -n1 | cut -f10 -d:)
582         # check for key expiration:
583         if [ "$expire" ]; then
584             if (( "$expire"  < "$curdate" )); then
585                 echo "! Host key is expired."
586                 echo " - Recommendation: extend lifetime of key with 'monkeysphere-server extend-key'"
587                 problemsfound=$(($problemsfound+1))
588             elif (( "$expire" < "$warndate" )); then
589                 echo "! Host key expires in less than $warnwindow:" $(advance_date $(( $expire - $curdate )) seconds +%F)
590                 echo " - Recommendation: extend lifetime of key with 'monkeysphere-server extend-key'"
591                 problemsfound=$(($problemsfound+1))
592             fi
593         fi
594
595         # and weirdnesses:
596         if [ "$create" ] && (( "$create" > "$curdate" )); then
597             echo "! Host key was created in the future(?!). Is your clock correct?"
598             echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?"
599             problemsfound=$(($problemsfound+1))
600         fi
601
602         # check for UserID expiration:
603         echo "$seckey" | grep ^uid: | cut -d: -f6,7,10 | \
604         while IFS=: read create expire uid ; do
605             # FIXME: should we be doing any checking on the form
606             # of the User ID?  Should we be unmangling it somehow?
607
608             if [ "$create" ] && (( "$create" > "$curdate" )); then
609                 echo "! User ID '$uid' was created in the future(?!).  Is your clock correct?"
610                 echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?"
611                 problemsfound=$(($problemsfound+1))
612             fi
613             if [ "$expire" ] ; then
614                 if (( "$expire" < "$curdate" )); then
615                     echo "! User ID '$uid' is expired."
616                     # FIXME: recommend a way to resolve this
617                     problemsfound=$(($problemsfound+1))
618                 elif (( "$expire" < "$warndate" )); then
619                     echo "! User ID '$uid' expires in less than $warnwindow:" $(advance_date $(( $expire - $curdate )) seconds +%F)             
620                     # FIXME: recommend a way to resolve this
621                     problemsfound=$(($problemsfound+1))
622                 fi
623             fi
624         done
625             
626 # FIXME: verify that the host key is properly published to the
627 #   keyservers (do this with the non-privileged user)
628
629 # FIXME: check that there are valid, non-expired certifying signatures
630 #   attached to the host key after fetching from the public keyserver
631 #   (do this with the non-privileged user as well)
632
633 # FIXME: propose adding a revoker to the host key if none exist (do we
634 #   have a way to do that after key generation?)
635
636         # Ensure that the ssh_host_rsa_key file is present and non-empty:
637         echo
638         echo "Checking host SSH key..."
639         if [ ! -s "${SYSDATADIR}/ssh_host_rsa_key" ] ; then
640             echo "! The host key as prepared for SSH (${SYSDATADIR}/ssh_host_rsa_key) is missing or empty."
641             problemsfound=$(($problemsfound+1))
642         else
643             if [ $(ls -l "${SYSDATADIR}/ssh_host_rsa_key" | cut -f1 -d\ ) != '-rw-------' ] ; then
644                 echo "! Permissions seem wrong for ${SYSDATADIR}/ssh_host_rsa_key -- should be 0600."
645                 problemsfound=$(($problemsfound+1))
646             fi
647
648             # propose changes needed for sshd_config (if any)
649             if ! grep -q "^HostKey[[:space:]]\+${SYSDATADIR}/ssh_host_rsa_key$" "$sshd_config"; then
650                 echo "! $sshd_config does not point to the monkeysphere host key (${SYSDATADIR}/ssh_host_rsa_key)."
651                 echo " - Recommendation: add a line to $sshd_config: 'HostKey ${SYSDATADIR}/ssh_host_rsa_key'"
652                 problemsfound=$(($problemsfound+1))
653             fi
654             if badhostkeys=$(grep -i '^HostKey' "$sshd_config" | grep -v "^HostKey[[:space:]]\+${SYSDATADIR}/ssh_host_rsa_key$") ; then
655                 echo "! $sshd_config refers to some non-monkeysphere host keys:"
656                 echo "$badhostkeys"
657                 echo " - Recommendation: remove the above HostKey lines from $sshd_config"
658                 problemsfound=$(($problemsfound+1))
659             fi
660
661         # FIXME: test (with ssh-keyscan?) that the running ssh
662         # daemon is actually offering the monkeysphere host key.
663
664         fi
665     fi
666
667 # FIXME: look at the ownership/privileges of the various keyrings,
668 #    directories housing them, etc (what should those values be?  can
669 #    we make them as minimal as possible?)
670
671 # FIXME: look to see that the ownertrust rules are set properly on the
672 #    authentication keyring
673
674 # FIXME: make sure that at least one identity certifier exists
675
676 # FIXME: look at the timestamps on the monkeysphere-generated
677 # authorized_keys files -- warn if they seem out-of-date.
678
679 # FIXME: check for a cronjob that updates monkeysphere-generated
680 # authorized_keys?
681
682     echo
683     echo "Checking for MonkeySphere-enabled public-key authentication for users ..."
684     # Ensure that User ID authentication is enabled:
685     if ! grep -q "^AuthorizedKeysFile[[:space:]]\+${SYSDATADIR}/authorized_keys/%u$" "$sshd_config"; then
686         echo "! $sshd_config does not point to monkeysphere authorized keys."
687         echo " - Recommendation: add a line to $sshd_config: 'AuthorizedKeysFile ${SYSDATADIR}/authorized_keys/%u'"
688         problemsfound=$(($problemsfound+1))
689     fi
690     if badauthorizedkeys=$(grep -i '^AuthorizedKeysFile' "$sshd_config" | grep -v "^AuthorizedKeysFile[[:space:]]\+${SYSDATADIR}/authorized_keys/%u$") ; then
691         echo "! $sshd_config refers to non-monkeysphere authorized_keys files:"
692         echo "$badauthorizedkeys"
693         echo " - Recommendation: remove the above AuthorizedKeysFile lines from $sshd_config"
694         problemsfound=$(($problemsfound+1))
695     fi
696
697     if [ "$problemsfound" -gt 0 ]; then
698         echo "When the above $problemsfound issue"$(if [ "$problemsfound" -eq 1 ] ; then echo " is" ; else echo "s are" ; fi)" resolved, please re-run:"
699         echo "  monkeysphere-server diagnostics"
700     else
701         echo "Everything seems to be in order!"
702     fi
703 }
704
705 ########################################################################
706 # MAIN
707 ########################################################################
708
709 # unset variables that should be defined only in config file
710 unset KEYSERVER
711 unset AUTHORIZED_USER_IDS
712 unset RAW_AUTHORIZED_KEYS
713 unset MONKEYSPHERE_USER
714
715 # load configuration file
716 [ -e ${MONKEYSPHERE_SERVER_CONFIG:="${SYSCONFIGDIR}/monkeysphere-server.conf"} ] && . "$MONKEYSPHERE_SERVER_CONFIG"
717
718 # set empty config variable with ones from the environment, or with
719 # defaults
720 LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=${LOG_LEVEL:="INFO"}}
721 KEYSERVER=${MONKEYSPHERE_KEYSERVER:=${KEYSERVER:="pool.sks-keyservers.net"}}
722 AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:=${AUTHORIZED_USER_IDS:="%h/.monkeysphere/authorized_user_ids"}}
723 RAW_AUTHORIZED_KEYS=${MONKEYSPHERE_RAW_AUTHORIZED_KEYS:=${RAW_AUTHORIZED_KEYS:="%h/.ssh/authorized_keys"}}
724 MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=${MONKEYSPHERE_USER:="monkeysphere"}}
725
726 # other variables
727 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:="true"}
728 REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"}
729 GNUPGHOME_HOST=${MONKEYSPHERE_GNUPGHOME_HOST:="${SYSDATADIR}/gnupg-host"}
730 GNUPGHOME_AUTHENTICATION=${MONKEYSPHERE_GNUPGHOME_AUTHENTICATION:="${SYSDATADIR}/gnupg-authentication"}
731
732 # export variables needed in su invocation
733 export DATE
734 export MODE
735 export MONKEYSPHERE_USER
736 export LOG_LEVEL
737 export KEYSERVER
738 export CHECK_KEYSERVER
739 export REQUIRED_USER_KEY_CAPABILITY
740 export GNUPGHOME_HOST
741 export GNUPGHOME_AUTHENTICATION
742 export GNUPGHOME
743
744 # get subcommand
745 COMMAND="$1"
746 [ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
747 shift
748
749 case $COMMAND in
750     'show-key'|'show'|'s')
751         show_server_key
752         ;;
753
754     'extend-key'|'e')
755         check_user
756         check_host_keyring
757         extend_key "$@"
758         ;;
759
760     'add-hostname'|'add-name'|'n+')
761         check_user
762         check_host_keyring
763         add_hostname "$@"
764         ;;
765
766     'revoke-hostname'|'revoke-name'|'n-')
767         check_user
768         check_host_keyring
769         revoke_hostname "$@"
770         ;;
771
772     'add-revoker'|'o')
773         check_user
774         check_host_keyring
775         add_revoker "$@"
776         ;;
777
778     'revoke-key'|'r')
779         check_user
780         check_host_keyring
781         revoke_key "$@"
782         ;;
783
784     'publish-key'|'publish'|'p')
785         check_user
786         check_host_keyring
787         publish_server_key
788         ;;
789
790     'expert'|'e')
791         check_user
792         SUBCOMMAND="$1"
793         shift
794         case "$SUBCOMMAND" in
795             'import-key'|'i')
796                 import_key "$@"
797                 ;;
798
799             'gen-key'|'g')
800                 gen_key "$@"
801                 ;;
802
803             'diagnostics'|'d')
804                 diagnostics
805                 ;;
806
807             *)
808                 failure "Unknown expert subcommand: '$COMMAND'
809 Type '$PGRM help' for usage."
810                 ;;
811         esac
812         ;;
813
814     'version'|'v')
815         echo "$VERSION"
816         ;;
817
818     '--help'|'help'|'-h'|'h'|'?')
819         usage
820         ;;
821
822     *)
823         failure "Unknown command: '$COMMAND'
824 Type '$PGRM help' for usage."
825         ;;
826 esac
827
828 exit "$RETURN"