#!/bin/bash
set -e

progname="$(basename "$0")"

error() {
    echo "$@" >&2
    exit 1
}

quiet_message() {
    $QUIET || echo "$@"
}

quiet_error() {
    quiet_message "$@" >&2
    exit 1
}

_usage() {
cat <<END
$progname [options] /path/to/certificate

This script will copy the public component of the kernel signing key
to /etc/uefi/certs and queue it for enrollment in the system MOK.

If the certificate is already enrolled, it will be skipped and the script will exit with success.
If the system does not support UEFI Secure Boot, it will be skipped and the script will exit with retval=2.

options:
    -q|--quiet: do not report errors for missing dependencies, if the
                certificate is already enrolled, or if it will be skipped
    -h|--help: display this message
END
}

help() {
    _usage
    exit 0
}

usage() {
    _usage >&2
    exit 1
}

check_commands() {
    for command in "$@"; do
        if ! command -v "$command" > /dev/null; then
            if ! $QUIET; then
                error "$command is missing"
            else
                exit 1
            fi
        fi
    done
}

filter_fingerprints() {
    grep 'SHA1 Fingerprint' | sed -e 's/SHA1 Fingerprint.//' | \
        tr -d ' :' | tr a-z A-Z
}

cert_fingerprint() {
    openssl x509 -inform PEM -fingerprint -noout -in "$1" | filter_fingerprints
}

enrolled_fingerprints() {
    mokutil --list-enrolled | filter_fingerprints
    mokutil --list-new | filter_fingerprints
}

options=$(getopt -o qh --long quiet,help -- "$@")
eval set -- $options

QUIET=false
while true; do
    case "$1" in
    -q|--quiet)
        QUIET=true ;;
    -h|--help)
        help ;;
    --)
        shift
        break
        ;;
    *)
        usage ;;
    esac
    shift
done

# It's typical for systems without UEFI to not have mokutil installed, so
# check for UEFI variable support manually.  The user shouldn't need to
# install mokutil only to discover it would fail anyway.
if ! test -d /sys/firmware/efi/efivars; then
    quiet_message "This system does not use UEFI Secure Boot functionality."
    quiet_message "Exiting."
    exit 2
fi

check_commands openssl mokutil

CERT=$1

if test -z "$CERT"; then
    error "error: No certificate specified."
fi

if test "$EUID" != 0; then
    error "This tool must be run as root."
fi

fingerprint=$(cert_fingerprint "$CERT")
fingerprint8=$(echo $fingerprint | cut -b 1-8)

if test -z "$fingerprint8"; then
    error "Failed to parse fingerprint from $CERT"
fi

for enrolled in $(enrolled_fingerprints); do
    if test "$enrolled" = "$fingerprint"; then
        quiet_message "Signing key $fingerprint8 already enrolled or pending."
        exit 0
    fi
done

if test "$EUID" -ne 0; then
    error "Root privileges are required to queue $CERT for enrollment on reboot."
fi

mkdir -p /etc/uefi/certs
# FIXME: Should handle collisions
if ! openssl x509 -inform PEM -in "$CERT" -outform DER \
                  -out "/etc/uefi/certs/$fingerprint8.crt"; then
    error "Failed to convert ${CERT} to DER format for import."
fi

if ! mokutil --import "/etc/uefi/certs/$fingerprint8.crt" --root-pw; then
    error "Failed to queue certificate for enrollment."
fi

quiet_message "Secure Boot certificate queued for enrollment on reboot."
