#!/bin/bash
#
# Discovery Usage: $0 [--prefix <prefix>] --discover <path-glob> ...
# Lookup Usage: $0  <path> <(startdate|enddate|serial|subject|issuer|dhlen|sigalgo|carootsubject|trustanchorissuer|trustanchorsubject|verify|chainlength|fingerprint)
#

function usage  {
	cat <<-EOD >&2

		Filesystem certificate file discovery

		    $0 [--prefix <prefix>] --discover <path-glob>...

		Certificate propery checks

		    $0 <path> <property>

		    Where <property> can be one of:

		      * startdate          - The certificate's notBefore date as unix epoch seconds
		      * enddate            - The certificate's notAfter date as unix epoch seconds
		      * serial             - The certificate's serial number
		      * subject            - The certificate's subject in the form /A=B/D=C/ (eg /C=AU/ST=...)
		      * fingerprint        - The certificate's fingerprint
		      * issuer             - The certificate's issuer in the form /A=B/D=C/ (eg /C=AU/ST=...)
		      * keylen             - The certificate's key length (in bits)
		      * sigalgo            - The certificate's signature algorithm (eg md5WithRSAEncryption, sha1With.., sha256With... etc)
		      * subject_hash       - The certificate's openssl subject hash value
		      * issuer_hash        - The certificate's openssl issuer hash value
		      * modulus            - The certificate's public key modulus of the certificate
		      * ocspid             - The certificate's OCSP responder ID(s)
		      * ocsp_uri           - The certificate's OCSP responder URI(s)
		      * time-to-expiry     - The number of seconds until the enddate (or 0 if already expired or not yet valid)
		      * verify             - Whether the local server would trust the service (openssl verify return code)

		Notes & Limitations

		    * Only PEM encoded certificate files are supported

		    * Only the first certificate in chain files is discovered or evaluated

	EOD
	exit 1
}


SELF="$(readlink -f "$0")"

if [[ "$1" == "--readcert" ]]; then

	# We got (re)called with --readcert (typically via sudo....)
	openssl x509 -in "$2"
	exit $?

elif [[ "$1" == "--prefix" || "$1" == "--discover" ]]; then

	PREFIX=
	if [[ "$1" == "--prefix" ]]; then
		PREFIX="$2"
		shift
		shift
	fi
	shift

	# Can't read straight into an array from null delimited using -a so.....
	CANDIDATES=()
	while IFS= read -r -d '' FILE ; do
		CANDIDATES+=( "${FILE}" )
	done < <(sudo -n "${SELF%/*}/glob" --files --realpath --null --braces -- "$@")

        echo "{ \"data\":["
	for FILE in "${CANDIDATES[@]}"; do

		# Try to extract the certificate to just a PEM string, but if it fails skip the file (probably not a cert)
		if ! PEM="$(sudo "${SELF}" --readcert "${FILE}" </dev/null 2>/dev/null)"; then
			continue
		fi

		# Extract the Fingerprint and CN from the PEM
		openssl x509 -noout -fingerprint -subject 2>/dev/null <<<"${PEM}" \
			| sed -re 's/^.+Fingerprint=(.*)$/\"{#FINGERPRINT}\":\"\1\",/g' -e 's/^subject=.*\/CN=([^/]+).*/\"{#CN}\":\"\1\"/g' -e 's/^/ /; 1 s/^ /  { /' \
			| cat - <(echo ", \"{#PATH}\":\"$FILE\", \"{#FILE}\":\"${FILE##*/}\" }, ") \
                        | tr -d '\n'
		echo
	done | sort -k 2,3 -u | sed -re '$ s/, *$//g' -e "s/\"\\{#/\"\\{#${PREFIX}/g"
	echo "]}"


elif [[ "$1" == keylen ]]; then

	if ! PEM="$(sudo "${SELF}" --readcert "$2" </dev/null 2>/dev/null)"; then
                echo "ZBX_NOTSUPPORTED"
                exit
    fi

	openssl x509 -noout -text <<<"${PEM}" | sed -nre 's/^[ \t]+Public-Key: \(([0-9]+) bit\)[ \t]*$/\1/  p'

elif [[ "$1" == "sigalgo" ]]; then

	if ! PEM="$(sudo "${SELF}" --readcert "$2" </dev/null 2>/dev/null)"; then
                echo "ZBX_NOTSUPPORTED"
                exit
    fi

        openssl x509 -noout -text <<<"${PEM}" | awk '/Signature Algorithm: /{print $3; exit;}'

elif [[ "$1" == 'time-to-expiry' ]]; then

    if ! PEM="$(sudo "${SELF}" --readcert "$2" </dev/null 2>/dev/null)"; then
                echo "ZBX_NOTSUPPORTED"
                exit
    fi

    openssl x509 -noout -dates <<<"${PEM}" \
      | perl -MDate::Parse -ne '
          s/^.*?= *//g; push(@ts, str2time($_));
          END {
              my $now=time();
              printf("%s\n", (($now < $ts[0] || $now > $ts[1]) ? 0 : ($ts[1] - $now)))
          }
        '

elif [[ "$1" =~ ^((start|end)date|serial|subject|issuer|fingerprint|(issuer|subject)_hash|modulus|ocsp(id|_uri))$ ]]; then

	if ! PEM="$(sudo "${SELF}" --readcert "$2" </dev/null 2>/dev/null)"; then
		echo "ZBX_NOTSUPPORTED"
		exit
	fi

	RESULT="$(openssl x509 -noout "-$1" <<<"${PEM}" | sed -re 's/^[^=]+= *//g')"

	if [[ "$1" =~ ^(start|end)date$ ]]; then
		RESULT="$(date --date="$RESULT" +%s)"
	elif [[ "$1" == 'modulus' ]]; then
		RESULT="$(sha256sum <<<"${RESULT}" | cut -d\  -f1)"
	fi

	echo "${RESULT}"

elif [[ "$1" == "verify" ]]; then

	echo TODO

else

	usage
	exit 1

fi
