OpenVPN
extractcrl.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4'''
5Helper script for CRL (certificate revocation list) file extraction
6to a directory containing files named as decimal serial numbers of
7the revoked certificates, to be used with OpenVPN CRL directory
8verify mode. To enable this mode, directory and 'dir' flag needs to
9be specified as parameters of '--crl-verify' option.
10For more information refer OpenVPN tls-options.rst.
11
12Usage example:
13 extractcrl.py -f pem /path/to/crl.pem /path/to/outdir
14 extractcrl.py -f der /path/to/crl.crl /path/to/outdir
15 cat /path/to/crl.pem | extractcrl.py -f pem - /path/to/outdir
16 cat /path/to/crl.crl | extractcrl.py -f der - /path/to/outdir
17
18Output example:
19 Loaded: 309797 revoked certs in 4.136s
20 Scanned: 312006 files in 0.61s
21 Created: 475 files in 0.05s
22 Removed: 2684 files in 0.116s
23'''
24
25import argparse
26import os
27import sys
28import time
29from subprocess import check_output
30
31FILETYPE_PEM = 'PEM'
32FILETYPE_DER = 'DER'
33
34def measure_time(method):
35 def elapsed(*args, **kwargs):
36 start = time.time()
37 result = method(*args, **kwargs)
38 return result, round(time.time() - start, 3)
39 return elapsed
40
41@measure_time
42def load_crl(filename, format):
43
44 def try_openssl_module(filename, format):
45 from OpenSSL import crypto
46 types = {
47 FILETYPE_PEM: crypto.FILETYPE_PEM,
48 FILETYPE_DER: crypto.FILETYPE_ASN1
49 }
50 if filename == '-':
51 crl = crypto.load_crl(types[format], sys.stdin.buffer.read())
52 else:
53 with open(filename, 'rb') as f:
54 crl = crypto.load_crl(types[format], f.read())
55 return set(int(r.get_serial(), 16) for r in crl.get_revoked())
56
57 def try_openssl_exec(filename, format):
58 args = ['openssl', 'crl', '-inform', format, '-text']
59 if filename != '-':
60 args += ['-in', filename]
61 serials = set()
62 for line in check_output(args, universal_newlines=True).splitlines():
63 _, _, serial = line.partition('Serial Number:')
64 if serial:
65 serials.add(int(serial.strip(), 16))
66 return serials
67
68 try:
69 return try_openssl_module(filename, format)
70 except ImportError:
71 return try_openssl_exec(filename, format)
72
73@measure_time
74def scan_dir(dirname):
75 _, _, files = next(os.walk(dirname))
76 return set(int(f) for f in files if f.isdigit())
77
78@measure_time
79def create_new_files(dirname, newset, oldset):
80 addset = newset.difference(oldset)
81 for serial in addset:
82 try:
83 with open(os.path.join(dirname, str(serial)), 'xb'): pass
84 except FileExistsError:
85 pass
86 return addset
87
88@measure_time
89def remove_old_files(dirname, newset, oldset):
90 delset = oldset.difference(newset)
91 for serial in delset:
92 try:
93 os.remove(os.path.join(dirname, str(serial)))
94 except FileNotFoundError:
95 pass
96 return delset
97
99 if arg == '-' or os.path.isfile(arg):
100 return arg
101 raise argparse.ArgumentTypeError('No such file "{}"'.format(arg))
102
104 if os.path.isdir(arg):
105 return arg
106 raise argparse.ArgumentTypeError('No such directory: "{}"'.format(arg))
107
108def main():
109 parser = argparse.ArgumentParser(description='OpenVPN CRL extractor')
110 parser.add_argument('-f', '--format',
111 type=str.upper,
112 default=FILETYPE_PEM, choices=[FILETYPE_PEM, FILETYPE_DER],
113 help='input CRL format - default {}'.format(FILETYPE_PEM)
114 )
115 parser.add_argument('crlfile', metavar='CRLFILE|-',
116 type=lambda x: check_crlfile(x),
117 help='input CRL file or "-" for stdin'
118 )
119 parser.add_argument('outdir', metavar='OUTDIR',
120 type=lambda x: check_outdir(x),
121 help='output directory for serials numbers'
122 )
123 args = parser.parse_args()
124
125 certs, t = load_crl(args.crlfile, args.format)
126 print('Loaded: {} revoked certs in {}s'.format(len(certs), t))
127
128 files, t = scan_dir(args.outdir)
129 print('Scanned: {} files in {}s'.format(len(files), t))
130
131 created, t = create_new_files(args.outdir, certs, files)
132 print('Created: {} files in {}s'.format(len(created), t))
133
134 removed, t = remove_old_files(args.outdir, certs, files)
135 print('Removed: {} files in {}s'.format(len(removed), t))
136
137if __name__ == "__main__":
138 main()
remove_old_files(dirname, newset, oldset)
Definition extractcrl.py:89
create_new_files(dirname, newset, oldset)
Definition extractcrl.py:79
load_crl(filename, format)
Definition extractcrl.py:42
measure_time(method)
Definition extractcrl.py:34
check_crlfile(arg)
Definition extractcrl.py:98
check_outdir(arg)
scan_dir(dirname)
Definition extractcrl.py:74