#!/bin/bash

# monkeysphere-server: MonkeySphere server admin tool
#
# The monkeysphere scripts are written by:
# Jameson Rollins <jrollins@fifthhorseman.net>
#
# They are Copyright 2008, and are all released under the GPL, version 3
# or later.

########################################################################
PGRM=$(basename $0)

SHARE=${SHARE:-"/usr/share/monkeysphere"}
export SHARE
. "${SHARE}/common"

VARLIB="/var/lib/monkeysphere"
export VARLIB

# date in UTF format if needed
DATE=$(date -u '+%FT%T')

# unset some environment variables that could screw things up
GREP_OPTIONS=

# default return code
ERR=0

########################################################################
# FUNCTIONS
########################################################################

usage() {
cat <<EOF
usage: $PGRM <subcommand> [args]
MonkeySphere server admin tool.

subcommands:
  update-users (u) [USER]...            update users authorized_keys files
  gen-key (g) [HOSTNAME]                generate gpg key for the server
  show-fingerprint (f)                  show server's host key fingerprint
  publish-key (p)                       publish server's host key to keyserver
  trust-key (t) KEYID [LEVEL]           set owner trust for keyid
  help (h,?)                            this help

EOF
}

# generate server gpg key
gen_key() {
    local hostName

    hostName=${1:-$(hostname --fqdn)}

    SERVICE=${SERVICE:-"ssh"}
    userID="${SERVICE}://${hostName}"

    if gpg --list-key ="$userID" > /dev/null 2>&1 ; then
	failure "Key for '$userID' already exists"
    fi

    # set key defaults
    KEY_TYPE=${KEY_TYPE:-"RSA"}
    KEY_LENGTH=${KEY_LENGTH:-"2048"}
    KEY_USAGE=${KEY_USAGE:-"auth"}
    KEY_EXPIRE=${KEY_EXPIRE:-"0"}
    cat <<EOF
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
EOF
    read -p "Key is valid for? ($KEY_EXPIRE) " KEY_EXPIRE; KEY_EXPIRE=${KEY_EXPIRE:-"0"}

    # set key parameters
    keyParameters=$(cat <<EOF
Key-Type: $KEY_TYPE
Key-Length: $KEY_LENGTH
Key-Usage: $KEY_USAGE
Name-Real: $userID
Expire-Date: $KEY_EXPIRE
EOF
)

    # add the revoker field if requested
    # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key.  why?
    # FIXME: why is this marked "sensitive"?  how will this signature ever
    # be transmitted to the expected revoker?
    if [ "$REVOKER" ] ; then
	keyParameters="${keyParameters}"$(cat <<EOF

Revoker: 1:$REVOKER sensitive
EOF
)
    fi

    echo "The following key parameters will be used:"
    echo "$keyParameters"

    read -p "Generate key? [Y|n]: " OK; OK=${OK:=Y}
    if [ ${OK/y/Y} != 'Y' ] ; then
	failure "aborting."
    fi

    # add commit command
    keyParameters="${keyParameters}"$(cat <<EOF

%commit
%echo done
EOF
)

    log "generating server key... "
    echo "$keyParameters" | gpg --batch --gen-key

    # output the server fingerprint
    fingerprint_server_key "=${userID}"

    # find the key fingerprint of the server primary key
    keyID=$(gpg --list-key --with-colons --with-fingerprint "=${userID}" | \
	grep '^fpr:' | head -1 | cut -d: -f10)

    # write the key to the file
    # NOTE: assumes that the primary key is the proper key to use
    (umask 077 && gpgsecret2ssh "$keyID" > "${VARLIB}/ssh_host_rsa_key")
    log "Private SSH host key output to file: ${VARLIB}/ssh_host_rsa_key"
}

# gpg output key fingerprint
fingerprint_server_key() {
    local ID

    if [ -z "$1" ] ; then
	ID="$1"
    else
	ID="=ssh://$(hostname --fqdn)"
    fi

    gpg --fingerprint --list-secret-keys "$ID"
}

########################################################################
# MAIN
########################################################################

COMMAND="$1"
[ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
shift

# set ms home directory
MS_HOME=${MS_HOME:-"$ETC"}

# load configuration file
MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere-server.conf}
[ -e "$MS_CONF" ] && . "$MS_CONF"

# set empty config variable with defaults
MONKEYSPHERE_USER=${MONKEYSPHERE_USER:-"monkeysphere"}
KEYSERVER=${KEYSERVER:-"subkeys.pgp.net"}
CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"}
AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"%h/.config/monkeysphere/authorized_user_ids"}
RAW_AUTHORIZED_KEYS=${RAW_AUTHORIZED_KEYS:-"%h/.ssh/authorized_keys"}

# other variables
REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
GNUPGHOME_HOST=${GNUPGHOME_HOST:-"${VARLIB}/gnupg-host"}
GNUPGHOME_AUTHENTICATION=${GNUPGHOME_AUTHENTICATION:-"${VARLIB}/gnupg-authentication"}

# set default GNUPGHOME, and make sure the directory exists.  this is
# true for all functions expect user authentication
# (ie. update-users).
GNUPGHOME="$GNUPGHOME_HOST"
export GNUPGHOME
mkdir -p -m 0700 "$GNUPGHOME"

case $COMMAND in
    'update-users'|'update-user'|'u')
	if [ "$1" ] ; then
	    # get users from command line
	    unames="$@"
	else
	    # or just look at all users if none specified
	    unames=$(getent passwd | cut -d: -f1)
	fi

	# set mode
	MODE="authorized_keys"

        # make sure the authorized_keys directory exists
	mkdir -p "${VARLIB}/authorized_keys"

	# set GNUPGHOME, and make sure the directory exists
	GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
	export GNUPGHOME
	mkdir -p -m 0700 "$GNUPGHOME"

	# loop over users
	for uname in $unames ; do
	    # check all specified users exist
	    if ! getent passwd "$uname" >/dev/null ; then
		error "----- unknown user '$uname' -----"
		continue
	    fi

	    # set authorized_user_ids and raw authorized_keys variables,
	    # translating ssh-style path variables
	    authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS")
	    rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS")

	    # if neither is found, skip user
	    if [ ! -s "$authorizedUserIDs" -a ! -s "$rawAuthorizedKeys" ] ; then
		continue
	    fi

	    log "----- user: $uname -----"

	    # temporary authorized_keys file
	    AUTHORIZED_KEYS=$(mktemp)

	    # trap to delete file on exit
	    trap "rm -f $AUTHORIZE_KEYS" EXIT

	    # process authorized_user_ids file
	    if [ -s "$authorizedUserIDs" ] ; then
		log "processing authorized_user_ids file..."
		process_authorized_user_ids "$authorizedUserIDs"
	    fi

	    # add user-controlled authorized_keys file path if specified
	    if [ "$RAW_AUTHORIZED_KEYS" != '-' ] ; then
		if [ -s "$rawAuthorizedKeys" ] ; then
		    log -n "adding raw authorized_keys file... "
		    cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS"
		    loge "done."
		fi
	    fi

	    # if the resulting authorized_keys file is not empty, move
	    # the temp authorized_keys file into place
	    if [ -s "$AUTHORIZED_KEYS" ] ; then
		# openssh appears to check the contents of the
                # authorized_keys file as the user in question, so the
                # file must be readable by that user at least.
		# FIXME: is there a better way to do this?
		chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS"
		chmod g+r "$AUTHORIZED_KEYS"

		mv -f "$AUTHORIZED_KEYS" "${VARLIB}/authorized_keys/${uname}"

		log "authorized_keys file updated."

	    # else destroy it
	    else
		rm -f "$AUTHORIZED_KEYS"
	    fi
	done
	;;

    'gen-key'|'g')
	gen_key "$1"
	;;

    'show-fingerprint'|'f')
	fingerprint_server_key "$@"
	;;

    'publish-key'|'p')
	publish_server_key
	;;

    'trust-key'|'trust-key'|'t')
	trust_key "$@"
	;;

    'help'|'h'|'?')
        usage
        ;;

    *)
        failure "Unknown command: '$COMMAND'
Type '$PGRM help' for usage."
        ;;
esac

exit "$ERR"