#!/bin/bash

### DO NOT CHANGE THE LINE BELLOW
set -a

### CHANGE HERE

CA_DAYS=3650
COMPUTER_DAYS=1825
USER_DAYS=365
DOMAIN="troytech.eu"
CDP_URL="http://crp.$DOMAIN/$DOMAIN.crl"
X509OU="AD Smart Card Login"

### DO NOT CHANGE ANYTHING BELLOW THIS LINE

LDAP_BASE="dc=$(echo $DOMAIN | sed 's/\./,dc=/g')"
CA_BASE_DIR="/opt/ca"
CA_CONFIG="$CA_BASE_DIR/etc/openssl.cnf"
CA_SPOOL_DIR="$CA_BASE_DIR/var/spool"
CA_PUBLIC_DIR="$CA_SPOOL_DIR/public"
CA_DB_DIR="$CA_DB_DIR/var/db"
CA_KEY="$CA_DB_DIR/ca.key"
CA_CERT="$CA_DB_DIR/ca.crt"
CA_SERIAL="$CA_DB_DIR/serial.txt"
CA_DB="$CA_DB_DIR/db.txt"
CA_CRL_SEQ="$CA_DB_DIR/crl_seq.txt"
CA_CERTS_DIR="$CA_DB_DIR/certs"
CA_NEW_DIR="$CA_DB_DIR/new"
CA_CRL_DIR="$CA_DB_DIR/crl"
CA_CURRENT_CRL="$CA_CRL_DIR/$DOMAIN.crl"
CA_OUT_CRL="$CA_PUBLIC_DIR/$DOMAIN.crl"
HEXGUID=""
DNS=""
UPN=""

set +a

usage() {
    echo "Usage :" 1>&2
    echo "$0 setup => intial CA setup" 1>&2
    echo "$0 crl => generate CRL" 1>&2
    echo "$0 list => list issued certificates" 1>&2
    echo "$0 inspect <id> => inspect certificate" 1>&2
    echo "$0 revoke <id> => revoke certificate by ID" 1>&2
    echo "$0 [sign] <name> => sign computer/user certificate (type automatically detected from AD)" 1>&2
}

setup_root_ca() {
    umask 022
    mkdir -p "$CA_SPOOL_DIR/"{in,out}
    mkdir -p "$CA_PUBLIC_DIR"
    umask 077
    mkdir -p "$CA_DB_DIR/"{certs,crl,new}
    echo "!!! Your CA password is shown bellow, write it down, it won't be shown again !!!" > /dev/tty
    openssl rand -hex 64 | tee /dev/tty | openssl req -new -x509 -days "$CA_DAYS" -sha256 \
        -extensions v3_ca -keyout "$CA_KEY" \
        -subj "/O=$DOMAIN/OU=$X509OU/CN=AD Smart Card CA" \
        -out "$CA_CERT" -config "$CA_CONFIG" -passout stdin -quiet
    cp "$CA_CERT" "$CA_PUBLIC_DIR"
    chmod 644 "$CA_PUBLIC_DIR/ca.crt"
    echo -n "" > "$CA_DB"
    echo 00 > "$CA_SERIAL"
    echo 00 > "$CA_CRL_SEQ"
    echo 1000 > "$CA_OUT_CRL"
}

generate_crl() {
    openssl ca -config "$CA_CONFIG" -gencrl -out "$CA_OUT_CRL"
    chmod 644 "$CA_OUT_CRL"
}

revoke_cert() {
    openssl ca -config "$CA_CONFIG" -revoke "$CA_NEW_DIR/$1.pem"
}

sign_csr() {
    CSR_FILE="$CA_BASE_DIR/in/$1.csr"

    if ! [ -f "$CSR_FILE" ]
    then
        echo "CSR file $CSR_FILE missing" 1>&2
        exit 1
    fi

    LDAP_USER=$(op-remote-read "op://IT/ca_ldap_reader/username")
    LDAP_PASS=$(op-remote-read "op://IT/ca_ldap_reader/password")

    LDAP=$(LDAPTLS_REQCERT=never ldapsearch -H "ldaps://$DOMAIN" -b "$LDAP_BASE" -D "$LDAP_USER@$DOMAIN" -w "$LDAP_PASS" -x "(|(samaccountname=$1)(servicePrincipalName=host/$1))")

    RES=$(echo "$LDAP" | grep -Po '^# numEntries: .+$' | cut -d':' -f2 | tr -d ' ')

    if ! [ "$RES" = "1" ]
    then
        echo "LDAP error, check name" 1>&2
        exit 1
    fi

    B64GUID=$(echo "$LDAP" | grep -Po '^objectGUID:: \K.+$')
    RAWGUID=$(echo -n "$B64GUID" | base64 -d -i | hexdump -v -e '1/1 "%02X"')
    export HEXGUID="$RAWGUID"
    #export HEXGUID="${RAWGUID[3]}${RAWGUID[2]}${RAWGUID[1]}${RAWGUID[0]}${RAWGUID[5]}${RAWGUID[4]}${RAWGUID[7]}${RAWGUID[6]}${RAWGUID[8]}${RAWGUID[9]}${RAWGUID[10]}${RAWGUID[11]}${RAWGUID[12]}${RAWGUID[13]}${RAWGUID[14]}${RAWGUID[15]}"
    export CN=$(echo "$LDAP" | grep -Po '^cn: \K.+$')

    if echo "$LDAP" | grep -q "objectClass: computer"
    then
        echo "Generating computer certificate"
        export DNS=$(echo "$LDAP" | grep -Po '^dNSHostName: \K.+$')
        openssl ca -config "$CA_CONFIG" -subj "/O=$DOMAIN/OU=$X509OU/CN=$DNS" -days "$COMPUTER_DAYS" \
            -extensions usr_cert_mskdc -in "$CSR_FILE" -out "$CA_BASE_DIR/out/$1.crt"
    else
        echo "Generating user certificate"
        export UPN=$(echo "$LDAP" | grep -Po '^userPrincipalName: \K.+$')
        openssl ca -config "$CA_CONFIG" -subj "/O=$DOMAIN/OU=$X509OU/CN=$CN" -days "$USER_DAYS" \
            -extensions usr_cert_scarduser -in "$CSR_FILE" -out "$CA_BASE_DIR/out/$1.crt"
    fi


}

list_certs() {
    cat "$CA_DB"
}

inspect_cert() {
    CERT_FILE="$CA_NEW_DIR/$1.pem"
    if ! [ -f "$CERT_FILE" ]
    then
        echo "$0 No certificate with ID $1" 1>&2
        exit 1
    fi

    echo "DB:"
    grep -P "\t$1\t" "$CA_DB"
    echo ""
    echo "File: "
    openssl x509 -in "$CERT_FILE" -text
}

case "$1" in

    "setup")
        setup_root_ca
        ;;

    "crl")
        generate_crl
        ;;

    "revoke")
        revoke_cert "$2"
        echo "do not forget to run $0 crl"
        ;;

    "list")
        list_certs
        ;;

    "help")
        usage "$@"
        ;;

    "inspect")
        inspect_cert "$2"
        ;;

    "sign")
        sign_csr "$2"
        ;;
    *)
        if [ -z "$1" ]
        then
            echo "Missing argument" 1>&2
            usage "$@"
            exit 1
        fi
        sign_csr "$1"
        ;;

esac