Skip to content

Conversation

@MegaManSec
Copy link
Contributor

No description provided.

@yadij yadij changed the title ssl-bump: fix X509 ref leak on SSL_set_ex_data() failure Fix SSL-Bump X509 certificate leak on SSL_set_ex_data() failure Sep 10, 2025
@yadij yadij added S-waiting-for-more-reviewers needs a reviewer and/or a second opinion S-waiting-for-author author action is expected (and usually required) labels Sep 10, 2025
Co-authored-by: Amos Jeffries <yadij@users.noreply.github.com>
@MegaManSec MegaManSec requested a review from yadij September 10, 2025 15:51
@rousskov rousskov self-requested a review September 10, 2025 22:01
Copy link
Contributor

@rousskov rousskov left a comment

Choose a reason for hiding this comment

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

I agree that we should check OpenSSL function calls above for failures and that this X509_free() should be added to avoid leaking the certificate as stated in the PR title. Thank you for working on fixing this code!

The rest of these changes need more work as detailed in specific change requests.

X509_up_ref(peeked_cert);
SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);
if (!X509_up_ref(peeked_cert)) {
debugs(83, DBG_IMPORTANT, "WARNING: X509_up_ref(peeked_cert) failed on server certificate");
Copy link
Contributor

Choose a reason for hiding this comment

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

Rule of thumb: Do not inform admins about transaction-specific problems they can do nothing about. I hope that addressing the other change request will eliminate the two new level-1 messages. If it does not, then they need to be converted to level 3.

if (X509 *peeked_cert = serverBump->serverCert.get()) {
X509_up_ref(peeked_cert);
SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);
if (!X509_up_ref(peeked_cert)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wish we could just ignore these failures like current PR code does, but we cannot: If we cannot add this certificate, then we cannot check for SQUID_X509_V_ERR_CERT_CHANGE problems. If we cannot check for those problems, we must terminate the connection because it is unsafe to proceed.

There are several reasonable ways to fix this, but I will propose the simplest solution that I can think of: Agree that if X509_up_ref() fails, then there is a Squid bug somewhere. The only way that function can fail is if we overflow the lock or corrupt certificate metadata. Both are Squid bugs.

The corresponding solution would wrap X509_up_ref() into a Squid function called Ssl::Lock() that does something like this:

const auto locked = X509_up_ref(cert);
Assure(locked);

Ssl::Lock() will return void. We can place that function into generic Security namespace instead, but I would not do that (for now) because GnuTLS does not lock its certificates.

This code will then just call Ssl::Lock(peeked_cert). We should convert other callers as well, but that can wait for a dedicated PR if you prefer, especially if this PR provides a reusable function for that future conversion (rather than adding more technical debt by inlining that solution once).

Please test callers reaction on the thrown exception by injecting/faking an X509_up_ref() error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do not have the time to learn more about this unfortunately. Any chance this can be turned into a bug report?

} else if (!SSL_set_ex_data(serverSession.get(),
ssl_ex_index_ssl_peeked_cert,
peeked_cert)) {
debugs(83, DBG_IMPORTANT, "WARNING: SSL_set_ex_data(ssl_ex_index_ssl_peeked_cert) failed; dropping extra X509 ref");
Copy link
Contributor

Choose a reason for hiding this comment

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

I wish we could just ignore these failures like current PR code does, but we cannot: If we cannot add this certificate, then we cannot check for SQUID_X509_V_ERR_CERT_CHANGE problems. If we cannot check for those problems, we must terminate the connection because it is unsafe to proceed.

To quit, we should set an error object and return false. Something along these lines (untested but please do test by injecting this failure):

Suggested change
debugs(83, DBG_IMPORTANT, "WARNING: SSL_set_ex_data(ssl_ex_index_ssl_peeked_cert) failed; dropping extra X509 ref");
debugs(83, 3, "SSL_set_ex_data(ssl_ex_index_ssl_peeked_cert) failed" << Ssl::ReportAndForgetErrors);
const auto err = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request, al);
static const auto d = MakeNamedErrorDetail("TLS_SET_EX_PEEKED_CERT");
err->detailError(d);
noteNegotiationDone(err);
bail(err);
return false;

For Ssl::ReportAndForgetErrors() to work best, please call Ssl::ForgetErrors() before calling SSL_set_ex_data().

@rousskov rousskov removed the S-waiting-for-more-reviewers needs a reviewer and/or a second opinion label Sep 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-for-author author action is expected (and usually required)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants