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