#!/usr/bin/perl # verify-cn -- a sample OpenVPN tls-verify script # # Return 0 if cn matches the common name component of # subject, 1 otherwise. # # For example in OpenVPN, you could use the directive: # # tls-verify "./verify-cn /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. die "usage: verify-cn cnfile certificate_depth subject" if (@ARGV != 3); # 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, $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=([^,]+)/) { $cn = $1; # Accept the connection if the X509 common name # string matches the passed cn argument. open(FH, '<', $cnfile) or exit 1; # can't open, nobody authenticates! while (defined($line = )) { if ($line !~ /^[[:space:]]*(#|$)/o) { chop($line); if ($line eq $cn) { exit 0; } } } close(FH); } # 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;