From a2a7d940559cc1eac7457ffc0a0dcf0af08baa60 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 3 Oct 2021 16:53:22 +0200 Subject: [PATCH] Support cryptography 35.0.0 for all modules except openssl_pkcs12 (#294) * Add some workarounds for cryptography 35.0.0. * Make fix work with very old cryptography versions as well (which supported multiple backends). * [TEMP] Disable openssl_pkcs12 tests to see whether everything else works. * Revert "[TEMP] Disable openssl_pkcs12 tests to see whether everything else works." This reverts commit 3f905bc79520432bf33abf9ca36442cddab9ccda. * Add changelog fragment. * Remove unnecessary assignment. * Simplify code change. * [TEMP] Disable openssl_pkcs12 tests to see whether everything else works. * Revert "[TEMP] Disable openssl_pkcs12 tests to see whether everything else works." This reverts commit fdb210528e46c70248097bd2f195b563a529b090. --- .../fragments/294-cryptography-35.0.0.yml | 4 ++ plugins/module_utils/crypto/_obj2txt.py | 4 ++ .../crypto/cryptography_support.py | 51 ++++++++++++++++--- 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 changelogs/fragments/294-cryptography-35.0.0.yml diff --git a/changelogs/fragments/294-cryptography-35.0.0.yml b/changelogs/fragments/294-cryptography-35.0.0.yml new file mode 100644 index 00000000..6f99a4f7 --- /dev/null +++ b/changelogs/fragments/294-cryptography-35.0.0.yml @@ -0,0 +1,4 @@ +bugfixes: + - "get_certificate - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294)." + - "openssl_csr_info - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294)." + - "x509_certificate_info - fix compatibility with the cryptography 35.0.0 release (https://github.com/ansible-collections/community.crypto/pull/294)." diff --git a/plugins/module_utils/crypto/_obj2txt.py b/plugins/module_utils/crypto/_obj2txt.py index f84774cb..928b3c00 100644 --- a/plugins/module_utils/crypto/_obj2txt.py +++ b/plugins/module_utils/crypto/_obj2txt.py @@ -20,6 +20,10 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +# WARNING: this function no longer works with cryptography 35.0.0 and newer! +# It must **ONLY** be used in compatibility code for older +# cryptography versions! + def obj2txt(openssl_lib, openssl_ffi, obj): # Set to 80 on the recommendation of # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values diff --git a/plugins/module_utils/crypto/cryptography_support.py b/plugins/module_utils/crypto/cryptography_support.py index 6c576a76..82d3e7b8 100644 --- a/plugins/module_utils/crypto/cryptography_support.py +++ b/plugins/module_utils/crypto/cryptography_support.py @@ -30,6 +30,7 @@ from ._asn1 import serialize_asn1_string_as_der try: import cryptography from cryptography import x509 + from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization import ipaddress except ImportError: @@ -68,9 +69,19 @@ def cryptography_get_extensions_from_cert(cert): # Since cryptography won't give us the DER value for an extension # (that is only stored for unrecognized extensions), we have to re-do # the extension parsing outselves. + backend = default_backend() + try: + # For certain old versions of cryptography, backend is a MultiBackend object, + # which has no _lib attribute. In that case, revert to the old approach. + backend._lib + except AttributeError: + backend = cert._backend + result = dict() - backend = cert._backend x509_obj = cert._x509 + # With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does + # not allow to get the raw value of an extension, so we have to use this ugly hack: + exts = list(cert.extensions) for i in range(backend._lib.X509_get_ext_count(x509_obj)): ext = backend._lib.X509_get_ext(x509_obj, i) @@ -84,8 +95,12 @@ def cryptography_get_extensions_from_cert(cert): critical=(crit == 1), value=base64.b64encode(der), ) - oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext)) + try: + oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext)) + except AttributeError: + oid = exts[i].oid.dotted_string result[oid] = entry + return result @@ -94,7 +109,13 @@ def cryptography_get_extensions_from_csr(csr): # (that is only stored for unrecognized extensions), we have to re-do # the extension parsing outselves. result = dict() - backend = csr._backend + backend = default_backend() + try: + # For certain old versions of cryptography, backend is a MultiBackend object, + # which has no _lib attribute. In that case, revert to the old approach. + backend._lib + except AttributeError: + backend = csr._backend extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req) extensions = backend._ffi.gc( @@ -105,6 +126,10 @@ def cryptography_get_extensions_from_csr(csr): ) ) + # With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does + # not allow to get the raw value of an extension, so we have to use this ugly hack: + exts = list(csr.extensions) + for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)): ext = backend._lib.sk_X509_EXTENSION_value(extensions, i) if ext == backend._ffi.NULL: @@ -117,8 +142,12 @@ def cryptography_get_extensions_from_csr(csr): critical=(crit == 1), value=base64.b64encode(der), ) - oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext)) + try: + oid = obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext)) + except AttributeError: + oid = exts[i].oid.dotted_string result[oid] = entry + return result @@ -489,9 +518,15 @@ def parse_pkcs12(pkcs12_bytes, passphrase=None): friendly_name = None if certificate: # See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238 - maybe_name = certificate._backend._lib.X509_alias_get0( - certificate._x509, certificate._backend._ffi.NULL) - if maybe_name != certificate._backend._ffi.NULL: - friendly_name = certificate._backend._ffi.string(maybe_name) + backend = default_backend() + try: + # For certain old versions of cryptography, backend is a MultiBackend object, + # which has no _lib attribute. In that case, revert to the old approach. + backend._lib + except AttributeError: + backend = certificate._backend + maybe_name = backend._lib.X509_alias_get0(certificate._x509, backend._ffi.NULL) + if maybe_name != backend._ffi.NULL: + friendly_name = backend._ffi.string(maybe_name) return private_key, certificate, additional_certificates, friendly_name