OpenVPN
extractcrl.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 '''
5 Helper script for CRL (certificate revocation list) file extraction
6 to a directory containing files named as decimal serial numbers of
7 the revoked certificates, to be used with OpenVPN CRL directory
8 verify mode. To enable this mode, directory and 'dir' flag needs to
9 be specified as parameters of '--crl-verify' option.
10 For more information refer OpenVPN tls-options.rst.
11 
12 Usage 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 
18 Output 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 
25 import argparse
26 import os
27 import sys
28 import time
29 from subprocess import check_output
30 
31 FILETYPE_PEM = 'PEM'
32 FILETYPE_DER = 'DER'
33 
34 def 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
42 def 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
74 def 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
79 def 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
89 def 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 
98 def check_crlfile(arg):
99  if arg == '-' or os.path.isfile(arg):
100  return arg
101  raise argparse.ArgumentTypeError('No such file "{}"'.format(arg))
102 
103 def check_outdir(arg):
104  if os.path.isdir(arg):
105  return arg
106  raise argparse.ArgumentTypeError('No such directory: "{}"'.format(arg))
107 
108 def 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 
137 if __name__ == "__main__":
138  main()
def create_new_files(dirname, newset, oldset)
Definition: extractcrl.py:79
def main()
Definition: extractcrl.py:108
def check_crlfile(arg)
Definition: extractcrl.py:98
def scan_dir(dirname)
Definition: extractcrl.py:74
def measure_time(method)
Definition: extractcrl.py:34
def load_crl(filename, format)
Definition: extractcrl.py:42
def remove_old_files(dirname, newset, oldset)
Definition: extractcrl.py:89
def check_outdir(arg)
Definition: extractcrl.py:103