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

impl SSL_CTX_set_default_verify_paths and friends #2

Merged
merged 7 commits into from
Apr 5, 2024

Conversation

cpu
Copy link
Member

@cpu cpu commented Apr 3, 2024

Adds:

  • SSL_CTX_set_default_verify_paths
  • SSL_CTX_set_default_verify_dir
  • SSL_CTX_set_default_verify_file

Stubs:

  • SSL_CTX_set_default_verify_store

We take a dep on openssl-probe in order to get convenient handling of the SSL_CERT_DIR and SSL_CERT_FILE env vars, and default locations for the verify paths.

There is likely some fine-tuning to do (e.g. with respect to the X509_LOOKUP API surface), but it's a step in the right direction for the simple cases. See man SSL_CTX_set_default_verify_paths for more information.

With this branch in-place I'm able to use a build of curl 7.81.0 that previously complained about the lack of a SSL_CTX_set_default_verify_paths symbol to do a HTTPS connection to example.com without explicit specification of CA cert file/dir paths.

With tip of main:
$ LD_LIBRARY_PATH=target/debug curl -o /dev/null -s -w "%{http_code}" https://example.com
curl: symbol lookup error: /nix/store/xcrd8llsff08w9dygz7cpgpcipgamhr0-curl-7.81.0/lib/libcurl.so.4: undefined symbol: SSL_CTX_set_default_verify_paths, version OPENSSL_3.0.0
With this branch:
$ LD_LIBRARY_PATH=target/debug curl -o /dev/null -s -w "%{http_code}" https://example.com
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_CTX_set_next_proto_select_cb") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_CTX_set_post_handshake_auth") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_CTX_sess_set_new_cb") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: User, reason: OperationFailed, string: Some("operation would block") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: User, reason: OperationFailed, string: Some("operation would block") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: User, reason: OperationFailed, string: Some("operation would block") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: Ssl, reason: Unsupported, string: Some("_SSL_set_ex_data") }
[2024-04-03T17:24:26Z ERROR ssl::error] raising Error { lib: User, reason: OperationFailed, string: Some("operation would block") }
200

@cpu cpu self-assigned this Apr 3, 2024
@cpu cpu force-pushed the cpu-set-default-paths branch 2 times, most recently from c7b4bd9 to e122674 Compare April 3, 2024 17:31
@cpu
Copy link
Member Author

cpu commented Apr 3, 2024

@ctz This probably needs a bit more work but seemed like a good spot to check in for feedback.

In terms of test coverage: do you have any thoughts? I was thinking of updating client.c to use SSL_CTX_set_default_verify_paths and then adding something like the Rustls connect-tests to use it to hit a public server using the default system CAs.

Comment on lines +237 to +249
entry! {
pub fn _SSL_CTX_set_default_verify_paths(ctx: *mut SSL_CTX) -> c_int {
let ctx = try_clone_arc!(ctx);
match ctx
.lock()
.map_err(|_| Error::cannot_lock())
.map(|mut ctx| ctx.set_default_verify_paths())
{
Err(e) => e.raise().into(),
Ok(()) => C_INT_SUCCESS,
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This pattern of casting an arc from the pointer, juggling the lock, invoking a fn with the unlocked ctx, and then juggling a c_int error return might be worth refactoring into something shared 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, agreed. Should be possible to take a closure that gets a &mut SslContext. Seems like a good refactoring for later.

rustls-libssl/build.rs Outdated Show resolved Hide resolved
Comment on lines +237 to +249
entry! {
pub fn _SSL_CTX_set_default_verify_paths(ctx: *mut SSL_CTX) -> c_int {
let ctx = try_clone_arc!(ctx);
match ctx
.lock()
.map_err(|_| Error::cannot_lock())
.map(|mut ctx| ctx.set_default_verify_paths())
{
Err(e) => e.raise().into(),
Ok(()) => C_INT_SUCCESS,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, agreed. Should be possible to take a closure that gets a &mut SslContext. Seems like a good refactoring for later.

@ctz
Copy link
Member

ctz commented Apr 3, 2024

In terms of test coverage: do you have any thoughts? I was thinking of updating client.c to use SSL_CTX_set_default_verify_paths and then adding something like the Rustls connect-tests to use it to hit a public server using the default system CAs.

Yeah adding things to client.c is a good idea. I suggest adding another "magic" option for cacert (like insecure currently works). However the later assumptions in client.c might not be suitable for hitting servers on the internet (it sends "hello" and asserts that "olleh" comes back).

@cpu cpu force-pushed the cpu-set-default-paths branch 3 times, most recently from 24b6627 to ca1dcd5 Compare April 4, 2024 15:43
@cpu cpu changed the base branch from cpu-set-default-paths to main April 4, 2024 15:43
@cpu
Copy link
Member Author

cpu commented Apr 4, 2024

Yeah adding things to client.c is a good idea. I suggest adding another "magic" option for cacert (like insecure currently works). However the later assumptions in client.c might not be suitable for hitting servers on the internet (it sends "hello" and asserts that "olleh" comes back).

I sorted the first part of this by adding a "default" magic option for the ca cert. The second part is addressed by setting a NO_ECHO env var and then bailing before the echo test parts in the client code.

@cpu cpu force-pushed the cpu-set-default-paths branch 2 times, most recently from 6e305d3 to da431da Compare April 4, 2024 15:57
.github/workflows/libssl.yaml Outdated Show resolved Hide resolved
rustls-libssl/tests/runner.rs Outdated Show resolved Hide resolved
cpu added 7 commits April 5, 2024 10:29
The `load_verify_files` fn in `entry.rs` had two jobs:

1. Converting a `impl Iterator<Item = PathBuf>` to
   a `Vec<CertificateDer<'a>>`.
2. Unlocking a `SSL_CTX` mutex and calling `add_trusted_certs` with the
   `Vec`.

The first of these jobs is useful in other contexts, and so this commit
lifts it into `x509.rs` where it can be shared.
The `SSL_new` entry point is described as returning `NULL` for error
conditions. Prior to this commit the only possible error was from the
`SSL_CTX` Mutex being poisoned - the `SSL::new` constructor was
infallible.

To support loading certs on-demand from default locations when
constructing a `SSL` from a `SSL_CTX` this commit updates the `SSL::new`
constructor fn to be fallible. We convert any error to a `NULL`
return in the entry-point wrapper.
Adds:

* SSL_CTX_set_default_verify_paths
* SSL_CTX_set_default_verify_dir
* SSL_CTX_set_default_verify_file

Stubs:

* SSL_CTX_set_default_verify_store

We take a dep on `openssl-probe` in order to get convenient handling of
the `SSL_CERT_DIR` and `SSL_CERT_FILE` env vars, and default locations
for the verify paths.

There is likely some fine-tuning to do (e.g. with respect to the
`X509_LOOKUP` API surface), but is a step in the right direction for the
simple cases.
Keeps CLion/Rust-Rover users from checking in IDE settings by mistake.
Previously the CA cert positional arg to the client could be either
a path to a PEM bundle, or "insecure" to disable cert. validation.

This commit updates this handling to add "default" as an option that
will invoke `SSL_CTX_set_default_verify_paths` to load default system CA
certs.
Previously the client test wrote "hello" to the socket and expected to
receive an echoed response. This matches what our test server
infrastructure does, but precludes using the test client against
a real HTTPS server. We want to be able to do that to test using the
system CA bundle to verify a server with a cert from the web PKI.

This commit adds a check for a `NO_ECHO` env var. When set, we clean up
the connection without trying to write a message or expect a response.
In the future we may choose to do something more complex like writing
a HTTP request and asserting a 200 response.

By default when `NO_ECHO` is not set the test client operates as it did
before.
This commit adds a unit test that uses the client test binary to
connect to https://example.com, validating the presented certificate
chain using the system default CA bundle. We do this with `NO_ECHO=1` to
avoid trying to write a non-HTTP request and asserting on an echoed
response.
@cpu cpu merged commit ba7144b into rustls:main Apr 5, 2024
10 checks passed
@cpu cpu deleted the cpu-set-default-paths branch April 5, 2024 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants