OpenVPN

Useful links:

#!/usr/bin/env python

'''
    ovpnCNcheck -- an OpenVPN tls-verify script
    """""""""""""""""""""""""""""""""""""""""""
    
    This script checks if the peer is in the allowed
    user list by checking the CN (common name) of the 
    X509 certificate against a provided text file.
    
    For example in OpenVPN, you could use the directive
    (as one line):
    
    tls-verify "/usr/local/sbin/ovpnCNcheck.py
                /etc/openvpn/userlist.txt"

    This would cause the connection to be dropped unless
    the client common name is within the userlist.txt.
    Every line should hold one regular expression which
    can also be just one common name (don't forget to escape
    stuff like .?^()[]\ with a \).
    Empty or lines which start with a # are ignored.
    
    Written by Robert Penz <[email protected]> under the GPL 2
    Parts are copied from the verify-cn sample OpenVPN
    tls-verify script.
'''

# Version 0.1
# Initial Release


import sys
import re


def matchCommonName(userListFile, cn):
    """ reads the user list file and tries to match every regex """
    for rawLine in open(userListFile,"r").readlines():
        line = rawLine.strip().rstrip("\n")
        if not line or line[0] == "#":
            continue
        
        if re.compile(line).match(cn):
            return True
    return False

def main():
    # we only work with 3 parameters
    if len(sys.argv) != 4:
        print __doc__
        sys.exit(-1)

    # Parse out arguments:
    # userListFile -- Path of the file with the allowed users
    #                 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.
    (userListFile, depth, x509) = sys.argv[1:]

    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.

        found = re.compile(r"/CN=(?P<cn>[^/]+)").search(x509)
        if found:
            # Accept the connection if the X509 common name
            # string matches a regex
            if matchCommonName(userListFile, found.group("cn")):
                sys.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 user list regexes
        sys.exit(1)

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

if __name__ == '__main__':
    main()