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