#!/bin/sh

# verify-cn.sh -- a sample OpenVPN tls-verify script
#
# Return 0 if cn matches the common name component of
# X509_NAME_oneline, 1 otherwise.
#
# For example in OpenVPN, you could use the directive:
#
#   tls-verify "./verify-cn.sh /etc/openvpn/allowed_clients"
#
# This would cause the connection to be dropped unless
# the client common name is listed on a line in the
# allowed_clients file.

if [ $# -ne 3 ]; then
	echo "usage: verify-cn.sh cnfile certificate_depth X509_NAME_oneline"
	exit 1
fi

# Parse out arguments:
#   cnfile -- The file containing the list of common names, one per
#             line, which the client is required to have,
#             taken from the argument to the tls-verify directive
#             in the OpenVPN config file.
#             The file can have blank lines and comment lines that begin
#             with the # character.
#   depth  -- The current certificate chain depth.  In a typical
#             bi-level chain, the root certificate will be at level
#             1 and the client certificate will be at level 0.
#             This script will be called separately for each level.
#   x509   -- the X509 subject string as extracted by OpenVPN from
#             the client's provided certificate.
cnfile="$1"
depth="$2"
x509="$3"

if [ $depth -eq 0 ]; then
	# If depth is zero, we know that this is the final
	# certificate in the chain (i.e. the client certificate),
	# and the one we are interested in examining.
	# If so, parse out the common name substring in
	# the X509 subject string.

	cn="${x509##*/CN=}"
	cn="${cn%%/*}"
	# if $x509 contains the CN, $cn will contain the CN ( "/CN=([^/])*/" )
	# if $x509 does not contain the CN, the first substitution will do nothing
	#   leaving the first char to be "/"
	if [ "${cn:0:1}" != "/" ]; then
		# Accept the connection if the X509 common name
		# string matches the passed cn argument.
		if [ ! -r $cnfile ]; then
			# can't open, nobody authenticates!
			exit 1
		fi

		cat $cnfile | while read line; do
			if [ "$cn" == "$line" ]; then
				exit 42; # This only exits the subshell running this pipe
			fi
			# default exit 0
		done
		if [ $? -eq 42 ]; then
			exit 0; # tell OpenVPN we're ok
		fi
	fi

# Authentication failed -- Either we could not parse
# the X509 subject string, or the common name in the
# subject string didn't match the passed cn argument.
	exit 1;
fi

# If depth is nonzero, tell OpenVPN to continue processing
# the certificate chain.
exit 0;

