#!/usr/bin/perl

# verify-cn -- 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 Test-Client"
#
# This would cause the connection to be dropped unless
# the client common name is "Test-Client"

die "usage: verify-cn cn certificate_depth X509_NAME_oneline" if (@ARGV != 3);

# Parse out arguments:
#   cn    -- The common name which the client is required to have,
#            taken from the argument to the tls-verify directive
#            in the OpenVPN config file.
#   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.
($cn, $depth, $x509) = @ARGV;

if ($depth == 0) {
    # 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.

    if ($x509 =~ /\/CN=([^\/]+)/) {
	# Accept the connection if the X509 common name
	# string matches the passed cn argument.
	if ($cn eq $1) {
	    exit 0;
	}
    }

    # 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;
}

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