-
Notifications
You must be signed in to change notification settings - Fork 851
Add boringssl support to the ja4_fingerprint plugin #12790
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
base: master
Are you sure you want to change the base?
Conversation
|
[approve ci autest 1] |
bneradt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @jasmine-nahrain for exploring this. I think your patch here makes the plugin code look much better. I'm content with this idea as a compromise that addresses our variety of wants here (minimize noise in the ts.h namespace along with a clean boring+openssl interface for the plugin).
@maskit : I'm curious to hear your opinion on this as well. Are you content with this patch?
include/ts/apidefs.h.in
Outdated
| struct tsapi_ssl_client_hello { | ||
| uint16_t version; | ||
| const uint8_t *cipher_suites; | ||
| size_t cipher_suites_len; | ||
| const uint8_t *extensions; | ||
| size_t extensions_len; | ||
| int *extension_ids; | ||
| size_t extension_ids_len; | ||
| void *ssl_ptr; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's initialize these here (nullptr, 0).
Also: as a tweak on this, what do you think of making these private and adding public getters for these such that we can lazily load them as they are requested? Subsequent requests can then return the populated (cached) values if the same value is asked for twice. Currently, the caller has to pay for the population of all of these even though they might not need them all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lazy load would be tricky. Best we can do is probably read and cache everything in TSVConnClientHelloGet.
As I commented on somewhere, SSL_CLIENT_HELLO is only available during BoringSSL callback functions are called. So TSVConnClientHelloGet needs to be called on certain hooks (this should be documented). This plugin seems fine since TSVConnClientHelloGet and TSClientHelloDestroy are called on TS_SSL_CLIENT_HELLO_HOOK, but if another plugin needs information from Client Hello later on another hook, everything needs to be deep copied when TSVConnClientHelloGet is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public getters would be nice even if we don't do lazy land. Those would enable removing ifdef from the plugin code.
maskit
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API documentation is mandatory.
The structure should probably be a class with public getters (and private members). Regardless, some of member variables should have const.
| if (ch->extensions != nullptr) { | ||
| const uint8_t *ext = ch->extensions; | ||
| size_t remaining = ch->extensions_len; | ||
| // For BoringSSL, we have direct access to the extensions buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't ch have a function that returns an iterator for extension type IDs so plugins don't need to deal with the difference?
for (auto ext_type : ch->get_extension_types()) {
summary.add_extension(ext_type);
}
bneradt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. As @maskit said, we'll need doc/ documentation for this too.
bneradt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
| Description | ||
| =========== | ||
|
|
||
| :type:`TSClientHello` is an opaque handle to a TLS ClientHello message sent by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not just a handle. I think the accessors need to be listed. Otherwise reading the header file would be the only way to know how to use it.
|
|
||
| // Returns a vector of extension type IDs | ||
| // This abstracts the difference between BoringSSL (extensions buffer) and OpenSSL (extension_ids array) | ||
| std::vector<uint16_t> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I was expecting a custom iterable object. This implementation copies each item (extension type) into a vector. A custom iterator that internally manages an index/offset would avoid the copy.
Maybe we can hide the actual type, which is currently std:vector, by typedef/using for now, and tell that this function returns an iterable object. Then we can replace/optimize the implementation later. It would be nice if we could declare it like Iterable interface in Java, but I'm not sure how to do it in C++.
| =========== | ||
|
|
||
| :func:`TSVConnClientHelloGet` retrieves ClientHello message data from the TLS | ||
| virtual connection :arg:`sslp`. This function is typically called from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't deep copy the information, we can use this function only on limited hooks. I'm sure we can use TS_EVENT_SSL_CLIENT_HELLO, but I'm not sure if it's the only hook or there are other hooks.
The documentation should list up all the hooks, or tell that TS_EVENT_SSL_CLIENT_HELLO is the only hook supported (maybe for now, until somebody confirms another hook work as well).
Problem
OpenSSL provides
SSL_client_hello_get0_ext(),SSL_client_hello_get0_ciphers()andSSL_client_hello_get1_extensions_present()to get client hello from an SSL object. BoringSSL doesn't have comparable functions. It requires theSSL_CLIENT_HELLOobject viaSSL_early_callback_ctx_extension_get(). Currently, there's no way to get theSSL_CLIENT_HELLOobject in plugins, which causes friction when writing SSL-related plugins that need to work with both libraries.Proposed Solution:
TSClientHello TSVConnClientHelloGet(TSVConn sslp);This API provides access to the
SSL_CLIENT_HELLOobject within plugins and is usable during SSL hooks (TS_SSL_CLIENT_HELLO_HOOK, TS_SSL_SERVERNAME_HOOK).Use Case: This enables plugins to access ClientHello data (cipher suites, extensions, SNI, ALPN, supported TLS versions) when using BoringSSL. Currently, the ja4_fingerprint plugin only works for openssl, this change allows us to add boringssl support.
Implementation Notes:
This is a non-breaking addition. Existing OpenSSL-based plugins continue to work unchanged.