Home | History | Annotate | Download | only in ssl
      1 #! /usr/bin/env python3
      2 
      3 """
      4 This script should be called *manually* when we want to upgrade SSLError
      5 `library` and `reason` mnemnonics to a more recent OpenSSL version.
      6 
      7 It takes two arguments:
      8 - the path to the OpenSSL source tree (e.g. git checkout)
      9 - the path to the C file to be generated
     10   (probably Modules/_ssl_data.h)
     11 """
     12 
     13 import datetime
     14 import os
     15 import re
     16 import sys
     17 import _ssl
     18 
     19 
     20 def parse_error_codes(h_file, prefix, libcode):
     21     pat = re.compile(r"#define\W+(%s([\w]+))\W+(\d+)\b" % re.escape(prefix))
     22     codes = []
     23     with open(h_file, "r", encoding="latin1") as f:
     24         for line in f:
     25             match = pat.search(line)
     26             if match:
     27                 code, name, num = match.groups()
     28                 num = int(num)
     29                 # e.g. ("SSL_R_BAD_DATA", ("ERR_LIB_SSL", "BAD_DATA", 390))
     30                 codes.append((code, (libcode, name, num)))
     31     return codes
     32 
     33 if __name__ == "__main__":
     34     openssl_inc = sys.argv[1]
     35     outfile = sys.argv[2]
     36     use_stdout = outfile == '-'
     37     f = sys.stdout if use_stdout else open(outfile, "w")
     38     error_libraries = {
     39         # mnemonic -> (library code, error prefix, header file)
     40         'PEM': ('ERR_LIB_PEM', 'PEM_R_', 'crypto/pem/pem.h'),
     41         'SSL': ('ERR_LIB_SSL', 'SSL_R_', 'ssl/ssl.h'),
     42         'X509': ('ERR_LIB_X509', 'X509_R_', 'crypto/x509/x509.h'),
     43         }
     44 
     45     # Read codes from libraries
     46     new_codes = []
     47     for libcode, prefix, h_file in sorted(error_libraries.values()):
     48         new_codes += parse_error_codes(os.path.join(openssl_inc, h_file),
     49                                        prefix, libcode)
     50     new_code_nums = set((libcode, num)
     51                         for (code, (libcode, name, num)) in new_codes)
     52 
     53     # Merge with existing codes (in case some old codes disappeared).
     54     codes = {}
     55     for errname, (libnum, errnum) in _ssl.err_names_to_codes.items():
     56         lib = error_libraries[_ssl.lib_codes_to_names[libnum]]
     57         libcode = lib[0]              # e.g. ERR_LIB_PEM
     58         errcode = lib[1] + errname    # e.g. SSL_R_BAD_SSL_SESSION_ID_LENGTH
     59         # Only keep it if the numeric codes weren't reused
     60         if (libcode, errnum) not in new_code_nums:
     61             codes[errcode] = libcode, errname, errnum
     62     codes.update(dict(new_codes))
     63 
     64     def w(l):
     65         f.write(l + "\n")
     66     w("/* File generated by Tools/ssl/make_ssl_data.py */")
     67     w("/* Generated on %s */" % datetime.datetime.now().isoformat())
     68     w("")
     69 
     70     w("static struct py_ssl_library_code library_codes[] = {")
     71     for mnemo, (libcode, _, _) in sorted(error_libraries.items()):
     72         w('    {"%s", %s},' % (mnemo, libcode))
     73     w('    { NULL }')
     74     w('};')
     75     w("")
     76 
     77     w("static struct py_ssl_error_code error_codes[] = {")
     78     for errcode, (libcode, name, num) in sorted(codes.items()):
     79         w('  #ifdef %s' % (errcode))
     80         w('    {"%s", %s, %s},' % (name, libcode, errcode))
     81         w('  #else')
     82         w('    {"%s", %s, %d},' % (name, libcode, num))
     83         w('  #endif')
     84     w('    { NULL }')
     85     w('};')
     86     if not use_stdout:
     87         f.close()
     88