Skip to content

Commit acb31fb

Browse files
authored
Fix various issues around X509_STORE_CTX reuse (#1272)
* Don't try to access X509_STORE_CTX after _cleanup Although OpenSSL happens to leave the errors in there on X509_STORE_CTX_cleanup, in no other OpenSSL API is accessing a cleaned up object meaningful. Do it in the other order. * Internal variables are internal The underscore-prefixed variables were not intended to be exposed as public API, so don't bother exposing it in the first place. * Don't reuse X509_STORE_CTXs There's a lot of history with X509_STORE_CTX's somewhat messy transition from a stack-allocated type to a heap-allocated type. (This is why a double X509_STORE_CTX_init used to leak memory.) We can avoid all this mess by just making a new X509_STORE_CTX each time.
1 parent bb4a60d commit acb31fb

File tree

1 file changed

+33
-62
lines changed

1 file changed

+33
-62
lines changed

src/OpenSSL/crypto.py

Lines changed: 33 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,12 +1881,6 @@ class X509StoreContext:
18811881
of a certificate in a described context. For describing such a context, see
18821882
:class:`X509Store`.
18831883
1884-
:ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
1885-
instance. It is dynamically allocated and automatically garbage
1886-
collected.
1887-
:ivar _store: See the ``store`` ``__init__`` parameter.
1888-
:ivar _cert: See the ``certificate`` ``__init__`` parameter.
1889-
:ivar _chain: See the ``chain`` ``__init__`` parameter.
18901884
:param X509Store store: The certificates which will be trusted for the
18911885
purposes of any verifications.
18921886
:param X509 certificate: The certificate to be verified.
@@ -1901,15 +1895,9 @@ def __init__(
19011895
certificate: X509,
19021896
chain: Optional[Sequence[X509]] = None,
19031897
) -> None:
1904-
store_ctx = _lib.X509_STORE_CTX_new()
1905-
self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
19061898
self._store = store
19071899
self._cert = certificate
19081900
self._chain = self._build_certificate_stack(chain)
1909-
# Make the store context available for use after instantiating this
1910-
# class by initializing it now. Per testing, subsequent calls to
1911-
# :meth:`_init` have no adverse affect.
1912-
self._init()
19131901

19141902
@staticmethod
19151903
def _build_certificate_stack(
@@ -1941,28 +1929,8 @@ def cleanup(s: Any) -> None:
19411929

19421930
return stack
19431931

1944-
def _init(self) -> None:
1945-
"""
1946-
Set up the store context for a subsequent verification operation.
1947-
1948-
Calling this method more than once without first calling
1949-
:meth:`_cleanup` will leak memory.
1950-
"""
1951-
ret = _lib.X509_STORE_CTX_init(
1952-
self._store_ctx, self._store._store, self._cert._x509, self._chain
1953-
)
1954-
if ret <= 0:
1955-
_raise_current_error()
1956-
1957-
def _cleanup(self) -> None:
1958-
"""
1959-
Internally cleans up the store context.
1960-
1961-
The store context can then be reused with a new call to :meth:`_init`.
1962-
"""
1963-
_lib.X509_STORE_CTX_cleanup(self._store_ctx)
1964-
1965-
def _exception_from_context(self) -> X509StoreContextError:
1932+
@staticmethod
1933+
def _exception_from_context(store_ctx: Any) -> X509StoreContextError:
19661934
"""
19671935
Convert an OpenSSL native context error failure into a Python
19681936
exception.
@@ -1972,21 +1940,45 @@ def _exception_from_context(self) -> X509StoreContextError:
19721940
"""
19731941
message = _ffi.string(
19741942
_lib.X509_verify_cert_error_string(
1975-
_lib.X509_STORE_CTX_get_error(self._store_ctx)
1943+
_lib.X509_STORE_CTX_get_error(store_ctx)
19761944
)
19771945
).decode("utf-8")
19781946
errors = [
1979-
_lib.X509_STORE_CTX_get_error(self._store_ctx),
1980-
_lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
1947+
_lib.X509_STORE_CTX_get_error(store_ctx),
1948+
_lib.X509_STORE_CTX_get_error_depth(store_ctx),
19811949
message,
19821950
]
19831951
# A context error should always be associated with a certificate, so we
19841952
# expect this call to never return :class:`None`.
1985-
_x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
1953+
_x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx)
19861954
_cert = _lib.X509_dup(_x509)
19871955
pycert = X509._from_raw_x509_ptr(_cert)
19881956
return X509StoreContextError(message, errors, pycert)
19891957

1958+
def _verify_certificate(self) -> Any:
1959+
"""
1960+
Verifies the certificate and runs an X509_STORE_CTX containing the
1961+
results.
1962+
1963+
:raises X509StoreContextError: If an error occurred when validating a
1964+
certificate in the context. Sets ``certificate`` attribute to
1965+
indicate which certificate caused the error.
1966+
"""
1967+
store_ctx = _lib.X509_STORE_CTX_new()
1968+
_openssl_assert(store_ctx != _ffi.NULL)
1969+
store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
1970+
1971+
ret = _lib.X509_STORE_CTX_init(
1972+
store_ctx, self._store._store, self._cert._x509, self._chain
1973+
)
1974+
_openssl_assert(ret == 1)
1975+
1976+
ret = _lib.X509_verify_cert(store_ctx)
1977+
if ret <= 0:
1978+
raise self._exception_from_context(store_ctx)
1979+
1980+
return store_ctx
1981+
19901982
def set_store(self, store: X509Store) -> None:
19911983
"""
19921984
Set the context's X.509 store.
@@ -2008,17 +2000,7 @@ def verify_certificate(self) -> None:
20082000
certificate in the context. Sets ``certificate`` attribute to
20092001
indicate which certificate caused the error.
20102002
"""
2011-
# Always re-initialize the store context in case
2012-
# :meth:`verify_certificate` is called multiple times.
2013-
#
2014-
# :meth:`_init` is called in :meth:`__init__` so _cleanup is called
2015-
# before _init to ensure memory is not leaked.
2016-
self._cleanup()
2017-
self._init()
2018-
ret = _lib.X509_verify_cert(self._store_ctx)
2019-
self._cleanup()
2020-
if ret <= 0:
2021-
raise self._exception_from_context()
2003+
self._verify_certificate()
20222004

20232005
def get_verified_chain(self) -> List[X509]:
20242006
"""
@@ -2031,20 +2013,10 @@ def get_verified_chain(self) -> List[X509]:
20312013
20322014
.. versionadded:: 20.0
20332015
"""
2034-
# Always re-initialize the store context in case
2035-
# :meth:`verify_certificate` is called multiple times.
2036-
#
2037-
# :meth:`_init` is called in :meth:`__init__` so _cleanup is called
2038-
# before _init to ensure memory is not leaked.
2039-
self._cleanup()
2040-
self._init()
2041-
ret = _lib.X509_verify_cert(self._store_ctx)
2042-
if ret <= 0:
2043-
self._cleanup()
2044-
raise self._exception_from_context()
2016+
store_ctx = self._verify_certificate()
20452017

20462018
# Note: X509_STORE_CTX_get1_chain returns a deep copy of the chain.
2047-
cert_stack = _lib.X509_STORE_CTX_get1_chain(self._store_ctx)
2019+
cert_stack = _lib.X509_STORE_CTX_get1_chain(store_ctx)
20482020
_openssl_assert(cert_stack != _ffi.NULL)
20492021

20502022
result = []
@@ -2056,7 +2028,6 @@ def get_verified_chain(self) -> List[X509]:
20562028

20572029
# Free the stack but not the members which are freed by the X509 class.
20582030
_lib.sk_X509_free(cert_stack)
2059-
self._cleanup()
20602031
return result
20612032

20622033

0 commit comments

Comments
 (0)