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

Expose mTLS related APIs, introduce ForeignTypeExt + ForeignTypeRefExt, add corresponds macro #262

Merged
merged 6 commits into from
Aug 15, 2024

Conversation

rushilmehra
Copy link
Collaborator

@rushilmehra rushilmehra commented Aug 9, 2024

ForeignTypeExt and ForeignTypeRefExt are inspired by sfackler/rust-openssl#1345, which make dealing with FFI safer and more ergonomic. The new APIs also allow for gracefully handling instances where the initial API call results in NULL

@rushilmehra rushilmehra force-pushed the add-mtls-apis branch 2 times, most recently from c2993c6 to c88f335 Compare August 9, 2024 11:29
@rushilmehra rushilmehra force-pushed the add-mtls-apis branch 3 times, most recently from 5419a22 to df2386d Compare August 13, 2024 05:25
Copy link

Choose a reason for hiding this comment

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

It would probably be easier overall to review if the "corresponds" macro change was split in to a separate PR. It looks like github now allows you to review each commit individually (at least that's new to me) which is helpful though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah given how difficult it is to get reviews, I highly prefer leaving everything in one PR and relying on the per-commit diff viewer. I can update the title to be more sensible

@rushilmehra rushilmehra changed the title Expose mTLS related APIs Expose mTLS related APIs, introduce ForeignTypeExt + ForeignTypeRefExt, add corresponds macro Aug 13, 2024
unsafe {
let ptr = cert_store.as_ptr();
cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?;
mem::forget(cert_store);
Copy link

Choose a reason for hiding this comment

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

I took a brief look at the manpage and it's not clear to me why we need to pass cert_store by value and then forget it, rather than just passing a reference. Is it just that this only happens once on startup and it's okay to leak? It might be nice to have a // SAFETY: block

Copy link

Choose a reason for hiding this comment

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

Also for some reason when I inspect the first commit individually it says the function as a whole is new, but when I look at master it's already there and looking at the second commit it only shows the actual cert_store.as_ptr() change. weird.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

On master you may be seeing the SSL_CTX_set0_verify_cert_store variant

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There are generally two variants for each API - one that operates on SSL_CTX (applies to the server generally), and another that operates on SSL (connection specific)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Re: calling mem::forget manually, it's a good question, but I'm mainly following prior art: https://github.com/sfackler/rust-openssl/blob/master/openssl/src/ssl/mod.rs#L3408-L3414

Copy link

@hcstern hcstern Aug 13, 2024

Choose a reason for hiding this comment

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

To summarize: boringssl takes ownership of the X509Store (as Rushil mentioned above) and gets freed by boringssl, which is why we use the 0 variant and forget the Rust struct. Thanks for taking the time to help me understand!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For posterity, this works by virtue of refcounted objects in c++. When initialized, a X509Store will have a refcount of 1. Any time the destructor is called, the refcount will be decremented and checked against == 0 before the object is fully freed. By using the set0 variant of the API, we "hand off" the object at refcount == 1 to c++ and leave the memory's lifetime at mercy of the next destructor call

Copy link
Contributor

Choose a reason for hiding this comment

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

Given that this is a common pattern I still think it would be nice to document somewhere

Copy link
Collaborator

Choose a reason for hiding this comment

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

The intent should be conveyed by using <X509Store as ForeignType>::into_ptr

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Opened #267

///
/// This function will return `None` if the underlying string contains invalid utf-8.
#[corresponds(X509_NAME_oneline)]
pub fn oneline(&self) -> Option<String> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This API is technically deprecated by openssl, and its use is strongly discouraged. I'm probably going to remove it from this PR and call some unsafe code directly where I need to, until we're able to stop using this API entirely

let out = CStr::from_ptr(ptr).to_str().map(|s| s.to_string());
// SAFETY: `X509_NAME_oneline` dynamically allocates memory for the string that's
// ultimately returned. As such, it's the caller's responsibility to free that memory
// after allocating an owned String in rust land.
Copy link
Contributor

@evanrittenhouse evanrittenhouse Aug 14, 2024

Choose a reason for hiding this comment

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

Should this be in the doc comment if user action is required?

E: oh I see, you mean the bindings, nvm

`ForeignTypeExt` and `ForeignTypeRefExt` are inspired by
sfackler/rust-openssl#1345, which make dealing
with FFI safer and more ergonomic. The new APIs (e.g.
from_const_ptr_opt`) also allow for gracefully handling instances where
the initial API call results in `NULL`. Instead of crashing the program,
`None` will be returned.
Our rustdocs are miserably broken. We manually link to openssl docs in
most binding definitions, and openssl keeps changing their documentation
URL, so in order to fix everything I'd have to touch every single
binding definition in every single file. Instead, we should use the
`corresponds` macro from the openssl-macros crate which nicely adds a
link to the openssl documentation on our behalf. If the openssl
documentation url ever changes again in the future, a simple dependency
bump should solve the issue.
);

unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) }
mem::forget(list);
Copy link
Member

Choose a reason for hiding this comment

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

Why is this legitimate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

SSL_set_client_CA_list takes ownership of the list: https://github.com/google/boringssl/blob/master/include/openssl/ssl.h#L2966

Constructing the Stack<X509Name> requires the application to own the Stack, and the owned type will free the underlying memory if drop is called. The general pattern here is to pass ownership to boringssl, and call mem::forget to release that ownership without calling drop and destroying the underlying stack

@rushilmehra rushilmehra merged commit a7bfe0d into master Aug 15, 2024
23 checks passed
@rushilmehra rushilmehra deleted the add-mtls-apis branch August 15, 2024 22:09
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.

5 participants