def _create_tuple_for_attribute(name, value): buf = ffi.new("char[]", X509_NAME_MAXLEN) length = lib.OBJ_obj2txt(buf, X509_NAME_MAXLEN, name, 0) if length < 0: raise ssl_error(None) name = _str_with_len(buf, length) buf_ptr = ffi.new("unsigned char**") length = lib.ASN1_STRING_to_UTF8(buf_ptr, value) if length < 0: raise ssl_error(None) try: value = _str_with_len(buf_ptr[0], length) finally: lib.OPENSSL_free(buf_ptr[0]) return (name, value)
def _get_peer_alt_names(certificate): # this code follows the procedure outlined in # OpenSSL's crypto/x509v3/v3_prn.c:X509v3_EXT_print() # function to extract the STACK_OF(GENERAL_NAME), # then iterates through the stack to add the # names. peer_alt_names = [] if certificate == ffi.NULL: return None # get a memory buffer biobuf = lib.BIO_new(lib.BIO_s_mem()) i = -1 while True: i = lib.X509_get_ext_by_NID(certificate, lib.NID_subject_alt_name, i) if i < 0: break # now decode the altName ext = lib.X509_get_ext(certificate, i) method = lib.X509V3_EXT_get(ext) if method is ffi.NULL: raise ssl_error("No method for internalizing subjectAltName!") ext_data = lib.X509_EXTENSION_get_data(ext) ext_data_len = ext_data.length ext_data_value = ffi.new("unsigned char**", ffi.NULL) ext_data_value[0] = ext_data.data if method.it != ffi.NULL: names = lib.ASN1_item_d2i(ffi.NULL, ext_data_value, ext_data_len, lib.ASN1_ITEM_ptr(method.it)) else: names = method.d2i(ffi.NULL, ext_data_value, ext_data_len) names = ffi.cast("GENERAL_NAMES*", names) count = lib.sk_GENERAL_NAME_num(names) for j in range(count): # get a rendering of each name in the set of names name = lib.sk_GENERAL_NAME_value(names, j) _type = name.type if _type == lib.GEN_DIRNAME: # we special-case DirName as a tuple of # tuples of attributes v = _create_tuple_for_X509_NAME(name.d.dirn) peer_alt_names.append(("DirName", v)) # GENERAL_NAME_print() doesn't handle NULL bytes in ASN1_string # correctly, CVE-2013-4238 elif _type == lib.GEN_EMAIL: v = _string_from_asn1(name.d.rfc822Name) peer_alt_names.append(("email", v)) elif _type == lib.GEN_DNS: v = _string_from_asn1(name.d.dNSName) peer_alt_names.append(("DNS", v)) elif _type == lib.GEN_URI: v = _string_from_asn1(name.d.uniformResourceIdentifier) peer_alt_names.append(("URI", v)) elif _type == lib.GEN_RID: v = "Registered ID" buf = ffi.new("char[2048]") length = lib.OBJ_obj2txt(buf, 2047, name.d.rid, 0) if length < 0: # TODO _setSSLError(NULL, 0, __FILE__, __LINE__); raise NotImplementedError elif length >= 2048: v = "<INVALID>" else: v = _str_with_len(buf, length) peer_alt_names.append(("Registered ID", v)) else: # for everything else, we use the OpenSSL print form if _type not in (lib.GEN_OTHERNAME, lib.GEN_X400, \ lib.GEN_EDIPARTY, lib.GEN_IPADD, lib.GEN_RID): warnings.warn("Unknown general type %d" % _type, RuntimeWarning) continue lib.BIO_reset(biobuf) lib.GENERAL_NAME_print(biobuf, name) v = _bio_get_str(biobuf) idx = v.find(":") if idx == -1: raise ValueError("Invalid value %s", v) peer_alt_names.append((v[:idx], v[idx + 1:])) free_func_addr = ffi.addressof(lib, "GENERAL_NAME_free") lib.sk_GENERAL_NAME_pop_free(names, free_func_addr) lib.BIO_free(biobuf) if peer_alt_names is not None: return tuple(peer_alt_names) return peer_alt_names