Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix a memory leak and a potential UAF and also #722 #723

Merged
merged 3 commits into from Nov 30, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/OpenSSL/SSL.py
Expand Up @@ -309,8 +309,10 @@ def __init__(self, callback):

@wraps(callback)
def wrapper(ok, store_ctx):
cert = X509.__new__(X509)
cert._x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx)
x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx)
res = _lib.X509_up_ref(x509)
_openssl_assert(res == 1)
cert = X509._from_raw_x509_ptr(x509)
error_number = _lib.X509_STORE_CTX_get_error(store_ctx)
error_depth = _lib.X509_STORE_CTX_get_error_depth(store_ctx)

Expand Down
7 changes: 3 additions & 4 deletions src/OpenSSL/crypto.py
Expand Up @@ -3058,8 +3058,7 @@ def load_pkcs12(buffer, passphrase=None):
pycert = None
friendlyname = None
else:
pycert = X509.__new__(X509)
pycert._x509 = _ffi.gc(cert[0], _lib.X509_free)
pycert = X509._from_raw_x509_ptr(cert[0])

friendlyname_length = _ffi.new("int*")
friendlyname_buffer = _lib.X509_alias_get0(
Expand All @@ -3073,8 +3072,8 @@ def load_pkcs12(buffer, passphrase=None):

pycacerts = []
for i in range(_lib.sk_X509_num(cacerts)):
pycacert = X509.__new__(X509)
pycacert._x509 = _lib.sk_X509_value(cacerts, i)
x509 = _lib.sk_X509_value(cacerts, i)
pycacert = X509._from_raw_x509_ptr(x509)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like a double check here. I'm not confident this is right. Does sk_X509_free free the underlying X509 * in the stack? If so then this is wrong, but that would also mean the current code in master is just a giant UAF.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sk_x509_free only frees the stack itself, not any of the things it contains, afaik

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, if that's true (which is what I thought but it's amazing how quickly you start second guessing yourself when fixing memory bugs) then should be right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the memory approach here is that each X509 object is responsible for hte X509* and the cacerts var is only responsible for freeing the stack?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

pycacerts.append(pycacert)
if not pycacerts:
pycacerts = None
Expand Down
25 changes: 25 additions & 0 deletions tests/test_ssl.py
Expand Up @@ -1279,6 +1279,31 @@ def callback(self, connection, *args):

assert verify.connection is clientConnection

def test_x509_in_verify_works(self):
"""
We had a bug where the X509 cert instantiated in the callback wrapper
didn't __init__ so it was missing objects needed when calling
get_subject. This test sets up a handshake where we call get_subject
on the cert provided to the verify callback.
"""
serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverConnection = Connection(serverContext, None)

def verify_cb_get_subject(conn, cert, errnum, depth, ok):
assert cert.get_subject()
return 1

clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(VERIFY_PEER, verify_cb_get_subject)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()

handshake_in_memory(clientConnection, serverConnection)

def test_set_verify_callback_exception(self):
"""
If the verify callback passed to `Context.set_verify` raises an
Expand Down