Skip to content

Fix URL scheme parsing and TLS Server Name Indication (SNI) and its use in apps/ - #27357

Closed
DDvO wants to merge 7 commits into
openssl:masterfrom
siemens:SSL_set_tlsext_host_name-doc_and_use_fixes
Closed

DDvO wants to merge 7 commits into
openssl:masterfrom
siemens:SSL_set_tlsext_host_name-doc_and_use_fixes

Conversation

@DDvO

@DDvO DDvO commented Apr 11, 2025

Copy link
Copy Markdown
Contributor

Add recommendation that TLS clients should use SSL_set_tlsext_host_name() for SNI
and that this should be done jointly with using {SSL,X509_VERIFY_PARAM}_{set1,add1}_host().
On this occasion fix nits and omissions in the respective .pod files.
Doc changes moved to #29150.

  • Fix OSSL_parse_url() going too far when scanning for '@' to detect the end of the userinfo component.,
    going astray when an @ symbol occurs after the host component, for instance in the query component.
    meanwhile superseded by fix OSSL_parse_url userinfo scan to respect authority boundary #30319

  • Fix OSSL_parse_url() to correctly parse scheme (where only certain chars are allowed)

  • Make sure that app_http_tls_cb() calls SSL_set_tlsext_host_name() for setting the SNI only if a server name (rather than an IP address) is given.

  • Also fix the server name being set in the SNI: it must always be the server actually connecting to,
    while for the CMP CLI app so far this is wrongly tied to the -tls_host option.

  • Add to OSSL_HTTP_REQ_CTX_nbio support for partial content-type string matching

  • Extend related doc, e.g., of SSL_set_tlsext_host_name() and OSSL_HTTP_REQ_CTX_set_expected().

Checklist
  • documentation is added or updated
  • tests are added or updated

@DDvO DDvO added branch: master Applies to master branch approval: review pending This pull request needs review by a committer triaged: bug The issue/pr is/fixes a bug triaged: documentation The issue/pr deals with documentation (errors) branch: 3.0 Applies to openssl-3.0 branch (EOL) tests: present The PR has suitable tests present branch: 3.3 Applies to openssl-3.3 (EOL) branch: 3.4 Applies to openssl-3.4 branch: 3.5 Applies to openssl-3.5 labels Apr 11, 2025
@DDvO
DDvO requested a review from Copilot April 11, 2025 19:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot reviewed 6 out of 9 changed files in this pull request and generated 2 comments.

Files not reviewed (3)
  • doc/man1/openssl-cmp.pod.in: Language not supported
  • doc/man3/SSL_CTX_set_tlsext_servername_callback.pod: Language not supported
  • doc/man3/X509_VERIFY_PARAM_set_flags.pod: Language not supported
Comments suppressed due to low confidence (1)

apps/cmp.c:1520

  • According to the PR description, the '-tls_host' option should take precedence over the URL hostname; however, this call uses 'host' instead of checking if '-tls_host' is provided. Consider revisiting this logic to ensure the precedence is properly enforced.
if (!truststore_set_host_etc(trust_store, host))

Comment thread apps/include/apps.h Outdated
Comment thread apps/cmp.c Outdated
@DDvO
DDvO force-pushed the SSL_set_tlsext_host_name-doc_and_use_fixes branch from 6b70f7a to 2013643 Compare April 11, 2025 19:15
@DDvO

DDvO commented Apr 11, 2025

Copy link
Copy Markdown
Contributor Author

BTW, since doing the SNI and hostname/address checks is important for correct and secure TLS connections,
how about calling on client side SSL_set_tlsext_host_name() and X509_VERIFY_PARAM_set1_host() by default?

Comment thread doc/man3/X509_VERIFY_PARAM_set_flags.pod Outdated
Comment thread doc/man3/SSL_CTX_set_tlsext_servername_callback.pod Outdated
Comment thread doc/man3/X509_VERIFY_PARAM_set_flags.pod Outdated
Comment thread apps/cmp.c
mattcaswell
mattcaswell previously approved these changes Apr 21, 2025
@t8m

t8m commented May 2, 2025

Copy link
Copy Markdown
Member

This needs a rebase due to conflicts.

@DDvO
DDvO force-pushed the SSL_set_tlsext_host_name-doc_and_use_fixes branch from 0e1d7c0 to cdd9fb1 Compare May 5, 2025 16:54
@DDvO

DDvO commented May 5, 2025

Copy link
Copy Markdown
Contributor Author

This needs a rebase due to conflicts.

Done.
Also squashed all fixup commits due to review comments.

@DDvO
DDvO force-pushed the SSL_set_tlsext_host_name-doc_and_use_fixes branch from cdd9fb1 to d25895d Compare May 5, 2025 18:42
@DDvO
DDvO requested a review from mattcaswell May 15, 2025 07:43
@openssl-machine

Copy link
Copy Markdown
Collaborator

This PR is in a state where it requires action by @openssl/committers but the last update was 30 days ago

Comment thread apps/include/apps.h Outdated
@openssl-machine

Copy link
Copy Markdown
Collaborator

This PR is in a state where it requires action by @openssl/committers but the last update was 30 days ago

@vdukhovni vdukhovni left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please fix at least the "is IP address" test.

@DDvO

DDvO commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

@DDvO Can you please create the OSSL_parse_url() change backport PR for 4.0 and below?

I am fine with the changes but I am hesitant backporting all of them to stable branches.

No problem, done here: #31267

@DDvO

DDvO commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

When aligning this PR with a related one in a different project,
I got an interesting Copilot review comment pointing out a problem with the new host_is_ip_address() helper function:
it does not cope with IPv6 addresses enclosed in [ and ].

In retrospect, it would have been better if OSSL_*parse_url() returned the host component without these surrinding chars, which are needed only at URL level.
Yet this has been implemented and documented otherwise, so little chance to rectify this now.

So I had to push an extension of host_is_ip_address() here that glosses over those brackets if present.
and ask @t8m to reconfirm approval after this extra fix.

@DDvO
DDvO force-pushed the SSL_set_tlsext_host_name-doc_and_use_fixes branch 3 times, most recently from 035d24c to 466239e Compare May 21, 2026 15:28

@esyr esyr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally looks good, the only blocker is UB.

Comment thread doc/man3/SSL_CTX_set_tlsext_servername_callback.pod Outdated
Comment thread doc/man3/SSL_CTX_set_tlsext_servername_callback.pod Outdated
Comment thread doc/man3/SSL_CTX_set_tlsext_servername_callback.pod Outdated
Comment thread crypto/http/http_lib.c Outdated
Comment thread apps/cmp.c Outdated
Comment thread apps/lib/apps.c Outdated
Comment thread doc/man1/openssl-cmp.pod.in
Comment thread crypto/http/http_lib.c
Comment thread apps/lib/apps.c Outdated
Comment thread apps/cmp.c
@DDvO
DDvO force-pushed the SSL_set_tlsext_host_name-doc_and_use_fixes branch from 466239e to 6bb0219 Compare May 24, 2026 15:06

@esyr esyr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM now, thanks, the only nit is that the added test should probably rather use test_http_url_invalid().

Comment thread test/http_test.c Outdated
esyr
esyr previously approved these changes May 24, 2026

@esyr esyr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

Comment thread apps/lib/apps.c
return bio;

err:
BIO_free(sbio);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it safe to free the BIO here if the SSL object has it set above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'd say yes, it should be safe because

  • the BIO_set_ssl(sbio, ssl, BIO_CLOSE) call transfers the ownership of ssl to sbio, and
  • in the err branch, BIO_free(sbio) frees ssl along with sbio.

Both of which (as often, unfortunately) is not documented.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

wait, I had this backwards (I had thought the call to BIO_set_ssl was actuall a call to SSL_set_bio). But, given that, if BIO_set_ssl fails/returns 0, then don't we leak the ssl we just created? I grant thats going to be an unlikely event (it can happen if ssl_new or BIO_up_ref fails).

It looks like thats actually a pretty consistent pattern in uses of BIO_set_ssl.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@nhorman can you then also approve?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sorry, I forgot to submit my last round of reviews. As noted above, I had the problem backwards. You're right in that BIO_free will free the SSL, but if BIO_set_ssl fails, we will leak the allocated SSL. not sure how big a deal that is (it seems to be a fairly common pattern), but it definately looks like a leak problem.

@DDvO DDvO Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Now I see what you mean: what happens if the BIO_set_ssl() call fails?

I just had a closer look at BIO_set_ssl() and found that there is one case where ssl_ctrl() in ssl/bio_ssl.c returns 0 after transferring ownership (via bs->ssl = ssl), namely if BIO_up_ref(bio)) fails, which is very unlikely but possible.
Still, so far one cannot fully rely on the ownership transfer to happen if and only if BIO_set_ssl() succeeds,
so calling it, one either risks a mem leak (if ignoring its negative result) or a double free (if deallocating ssl on its failure).

To fully solve this, I now deallocate ssl in case BIO_set_ssl() fails
while adding a little bugfix commit that

  • makes sure that BIO_set_ssl() transfers ownership of ssl only on success, and
  • documents this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I guess you and Neil have just opened a can of worms: if I'm not mistaken, failing ssl_new() (due to failing OPENSSL_zalloc) after ssl_free would lead to BIO_SSL double-free, as b->ptr is not reset to NULL after OPENSSL_free call in ssl_free, and the latter would be called the second time in [1].

@esyr which b->ptr in which function context do you mean here?
Since I've changed ssl_ctrl() to set bs->ssl only at the end on success,
and as I call SSL_free(ssl) only on failure, I do not see that I introduced potential for double-free.
Which does not preclude that the wonderful code in bio_lib.c bears other potential for double-free,
but that's unrelated to this PR.

@nhorman nhorman Jun 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think what @esyr is getting at is the following set of conditions:

  1. BIO_set_ssl is called
  2. In ssl_ctrl, we fall into the BIO_C_SET_SSL case
  3. ssl_free is called on the passed bio b
  4. ssl_free(b) is called
  5. ssl_free calls BIO_get_data to get the data bio (bs)
  6. SSL_free(bs->ssl) is called
  7. OPENSSL_free(bs) is called. Critically the BIO data pointer remains pointed to bs (from (5)) which is now no longer valid
  8. back in ssl_ctrl, ssl_new is called, which we assume fails on a malloc failure, meaning a new bs is not allocated
  9. ssl_ctrl returns with an error code and the BIOS bs/data pointer still pointing to invalid memory (due to the free in (7)
  10. your code above detects the error, frees the previously leaked SSL (so you've fixed that and jumps to err:
  11. at the err label BIO_free is called
  12. BIO_free calls ssl_free, which calls BIO_get_data to get bs again, which is non-null but invalid.
  13. we try to call OPENSSL_free again on bs, which results in a double free.

If thats the case it seems like the best solution here is to call BIO_set_data(b, NULL) in ssl_free to set the freed pointer to NULL, and then check it at the top of ssl_free to not use it if it is NULL.

That all said, given the proliferation of the call sites for this, and the fact that no one currently checks the return code of the BIO_set_ssl call anywhere, this might be creeping out of scope for this PR. Maybe just let it be and open an issue to fix this separately along with all the call sites.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, that what I was trying to tell. In general, I concur that having broken BIO_set_ssl() is out of scope for this PR. Created [1] to track this.

[1] #31388

Comment thread ssl/bio_ssl.c
@@ -299,7 +299,6 @@ static long ssl_ctrl(BIO *b, int cmd, long num, void *ptr)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess you and Neil have just opened a can of worms: if I'm not mistaken, failing ssl_new() (due to failing OPENSSL_zalloc) after ssl_free would lead to BIO_SSL double-free, as b->ptr is not reset to NULL after OPENSSL_free call in ssl_free, and the latter would be called the second time in [1].

[1] https://github.com/openssl/openssl/pull/27357/files#diff-22af71ddb18c32051f7e626ab7089fb69c056a419dd209f6c0c85a873e30c2f5R2983

Comment thread ssl/bio_ssl.c
BIO_set_next(b, bio);
}
BIO_set_init(b, 1);
bs->ssl = ssl;

@esyr esyr Jun 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The change looks innocuous enough (famous last words), however it's slowly creeping out of scope of the PR.

@mattcaswell might also want to take a look at this.

@openssl-machine

Copy link
Copy Markdown
Collaborator

This pull request is ready to merge

@DDvO

DDvO commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Merged - thanks @nhorman, @esyr, and @t8m for your approvals
and @InfoHunter, @mattcaswell, and @vdukhovni for your reviews!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch tests: present The PR has suitable tests present triaged: bug The issue/pr is/fixes a bug triaged: documentation The issue/pr deals with documentation (errors) triaged: refactor The issue/pr requests/implements refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants