-
Notifications
You must be signed in to change notification settings - Fork 7.3k
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
fix(esp-tls): make the wolfSSL backend send entire client certificate… (IDFGH-12621) #13618
Conversation
👋 Hello frankencode, we appreciate your contribution to this project! 📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more. 🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project. Click to see more instructions ...
Review and merge process you can expect ...
|
… chains This change makes the wolfSSL backend sent the complete TLS client certificate chain. This align the wolfSSL backend with the behavior of the mbedTLS backend. Some servers need the intermediate certificates to verify a client certificate. If the provided PEM file contains only a single certificate this change has no effect and the behavior will be as before. This impacts higher level APIs to function as someone would expect. E.g.: esp_websocket_client_config_t.client_cert: when passing here a pem file containing 2 certificates (the CA's and the client's) it would be expected that both are transmitted during TLS handshake.
Hi @frankencode and thank you for your interest in wolfssl. Please note that the espressif/esp-wolfssl is stale and not managed by wolfssl as noted in espressif/esp-wolfssl#24 (comment). We'd like to invite you to instead open pull requests or issues at wolfSSL/wolfssl. Additionally, please note that we have wolfSSL available as a Managed Component as well as some examples using a GitHub clone of wolfssl as a component in the ESP-IDF with only a CMakeLists.txt. |
@@ -97,7 +97,7 @@ static esp_err_t esp_load_wolfssl_verify_buffer(esp_tls_t *tls, const unsigned c | |||
wolf_fileformat = WOLFSSL_FILETYPE_ASN1; | |||
} | |||
if (type == FILE_TYPE_SELF_CERT) { | |||
if ((*err_ret = wolfSSL_CTX_use_certificate_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) { | |||
if ((*err_ret = wolfSSL_CTX_use_certificate_chain_buffer_format( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) { |
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.
Hi @frankencode Thanks for the PR.
Sorry for the really delayed review. I have confirmed that this change makes it same as mbedTLS.
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.
No hurry. Good things take time.
@@ -310,6 +310,14 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls | |||
#endif /* CONFIG_WOLFSSL_HAVE_ALPN */ | |||
} | |||
|
|||
#ifdef CONFIG_WOLFSSL_HAVE_OCSP |
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.
Regarding this change. Where do you plan to add this CONFIG option ? Have you defined it in your own project?
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.
I have same real issues building wolfssl as esp-idf component. I have a bit of hacked setup with the crappy scripts that ship with wolfssl which wildly copy around files to create an esp-idf component... That said I also added this OCSP option to the esp-wolfssl package which is still referenced in the esp-tls documentation:
espressif/esp-wolfssl#24 . The good thing about this esp-wolfssl is that it builds wolfssl from github and allows to cleanly track the upstream. At some point someone probably has to update this package to point to the latest wolfssl version. The name of the option will definitely be CONFIG_WOLFSSL_HAVE_OCSP as HAVE_OCSP is also the name used internally in wolfssl's config headers.
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.
Ahh, okay I didn't notice this PR. Will see what can be done about the PR and updating the wolfSSL version
components/esp-tls/esp_tls_wolfssl.c
Outdated
wolfSSL_CTX_EnableOCSP((WOLFSSL_CTX *)tls->priv_ctx, 0); | ||
#endif | ||
|
||
wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, hostname, hostlen); |
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.
I think this change should go here ( i.e. under the skip_common_name option)
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.
I'm not sure how SNI is related to CN server certificate check. In our day and age virtual hosting is common place and providing the actual Server Name (Indicator) should be a default behavior.
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.
Yes it is the default behaviour.
If you notice that the option it is under is skip_common_name
.
By default it is not set hence the default behaviour is to indicate the server name, as per your expectation.
But in some cases (e.g., when there is a local server and the CN would not match the IP of that server) then we provide an option to skip sending the server name indication so that the TLS connection becomes successful even if the server name does not match.
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.
OK, I see mbedtls_ssl_set_hostname() has a special twist. As the documentation states: "Warning: If the hostname is not set with this function, Mbed TLS will silently skip certificate verification entirely. " Hence I moved the wolfSSL_CTX_UseSNI() call to make it behave similar when switching to the wolfSSL esp-tls backend. Check out the last commit. (I've force push updated it!-) I've tested it on my customer's app with wireshark. Works as expected for me.
components/esp-tls/esp_tls_wolfssl.c
Outdated
@@ -288,6 +288,9 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls | |||
free(use_host); | |||
return ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED; | |||
} | |||
/* Mimick the semantics of mbedtls_ssl_set_hostname(), which does not only set the SNI, | |||
but if not set will make the mbedTLS stack skip certificate verification. */ | |||
wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, use_host, strlen(use_host)); |
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 you please check the return code in this case
components/esp-tls/esp_tls_wolfssl.c
Outdated
@@ -310,6 +313,12 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls | |||
#endif /* CONFIG_WOLFSSL_HAVE_ALPN */ | |||
} | |||
|
|||
#ifdef CONFIG_WOLFSSL_HAVE_OCSP | |||
wolfSSL_CTX_EnableOCSPStapling((WOLFSSL_CTX *)tls->priv_ctx ); |
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 you check the return codes for all the API?
Also, I think the order should be
wolfSSL_CTX_EnableOCSP
wolfSSL_CTX_EnableOCSPStapling
wolfSSL_UseOCSPStapling
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.
Hi Aditya, I've updated the commit once more. I've added proper error handling and added comments to the calls. These OCSP related API calls are sadly not documented. I simply go by the usage as shown in the example client application (https://github.com/wolfSSL/wolfssl/blob/master/examples/client/client.c). And YES, I checked it actually works with wireshark of having OCSPv1 TLS extension send by client and having OCSP status forwarded by server.
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.
Please see:
The web page appears to not always immediately auto-scroll to the correct location for me, so give it a few seconds, make sure the entire page loads. Ctrl-F might help navigate to the specific API documentation.
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.
Thanks Jim, I didn't realize there are separate manual pages (I was missing the doxygen docs I used to see in other places in wolfSSL;-). I quickly checked if there is additional useful information. The "wolfSSL_CTX_EnableOCSP" man page shows an example for a different function and is missing the "WOLFSSL_OCSP_CHECKALL" flag used in the example client. The others I think I got right from reading the source code!-)
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.
Glad to help. Having a robust integration with Espressif ESP-IDF is definitely on the roadmap for wolfSSL.
In the meantime, I've opened an issue regarding the documentation. See wolfSSL/wolfssl#7564
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.
@gojimmypi Looks good to me. Btw. I figured I couldn't find the docs initially because that folder is not copied by your IDE/Espressif/*/setup.sh script. When I find the time I will update esp-wolfssl (or create something similar for my convenience).
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.
Hi @frankencode - cool, thanks for taking a look. The PR was merger earlier.
yes - the script currently only copies the essential code for build and not the docs. I'm curious why you choose the script copy method? The new CMakeLists.txt
allows you to point the component at a GitHub repo directory. For me, it is much easier to see and track changes with the cmake file.
I'm always interested in how others are using the wolfSSL library.
Cheers
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.
Jim, I have to look at your new method at some later time, again. It initially turned me off that you couldn't follow the common conventions there for the example CMakeLists.txt. Components are searched for by EXTRA_COMPONENT_DIRS or similar. Paths to some local installation directories do not belong into a CMakeLists file! Also, as I told you before, with the current setup there are missing Kconfig variables which allow to select wolfSSL as esp-tls backend. And I think when I did add those I ran into many build issues... I'm currently only testing/prototyping. I surely going to clean up my build setup later-on and keep you posted when I figured out how to build wolfSSL latest version cleanly for esp-idf. So far updating esp-wolfssl looks like the best bet to me.
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 for the feedback! You are absolutely right: the Kconfig definitely needs improvement. I'll be giving that attention soon. Let me know if there are any specifics that you think should be given priority. Perhaps you have a sample? Ideally, I'd of course like all of the wolfSSL settings to be configurable via Kconfig... but there are many.
Paths to some local installation directories do not belong into a CMakeLists file!
Although yes, there's flexibility to do this, the default behavior is to simply search the parent directories in the case of the examples and use the wolfSSL a few directories up.
In the case of a different directory, such as in my case - I have this:
workspace/wolfssl
workspace/myproject_using_wolfssl
workspace/myproject2_using_wolfssl
The value being ensuring all projects are pointing to a fixed wolfSSL git source code repository, and easily point to an alternate as desired. If there's a wolfssl
directory in the myproject
, then that one will be used.
I'll be sure and take a look at your EXTRA_COMPONENT_DIRS
suggestion. I'm always open to making improvements.
I do believe that still works for a completely different location, as there's an Apple Homekit project doing that with wolfSSL.
Kind regards.
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.
As far as I could observe the informal convention is to place components into "components" directories in esp-idf and have EXTRA_COMPONENT_DIRS point to it if its not a default location that is searched for. (An nobody certainly wants a custom search logic for each 3rd party component). I was initially testing my setup with the esp websocket client example: https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client/examples/target . (But there are certainly better examples;-)
If you have some time I'd also be happy if you can review the two commits this comment section was actually meant to be about. THX.
components/esp-tls/esp_tls_wolfssl.c
Outdated
@@ -310,6 +317,23 @@ static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls | |||
#endif /* CONFIG_WOLFSSL_HAVE_ALPN */ | |||
} | |||
|
|||
#ifdef CONFIG_WOLFSSL_HAVE_OCSP | |||
/* increase error verbosity */ | |||
wolfSSL_CTX_EnableOCSP((WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_OCSP_CHECKALL); |
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 you please add return check for this API as well.
I see in the wolfSSL manual that it is of int return type.
WOLFSSL_CTX* ctx = wolfSSL_CTX_new( method ); int options; // initialize to option constant
...
int ret = wolfSSL_CTX_EnableOCSP(ctx, options); if(ret != SSL_SUCCESS){
// OCSP is not enabled
}
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.
You're right. I've added error handling to the wolfSSL_CTX_EnableOCSP(), too.
components/esp-tls/esp_tls_wolfssl.c
Outdated
ESP_LOGE(TAG, "wolfSSL_UseOCSPStapling failed, returned %d", ret); | ||
return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED; | ||
} | ||
/* decrease error verbosity */ |
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.
Why is it required to reduce the error verbosity after enabling OCSP stapling?
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.
That is a good question I was asking myself, too. This call is something I copied from the example wolfSSL client. I thought it might be needed because I'm not registering a callback to receive status information on the OCSP progress/status. But it turns out this call has no effect on wolfSSL 5.7. The certificate manager associated with the context is the only component using this OCSP_CHECKALL flag and it cannot be reset to zero afterwards. That said I removed the last call and retested everything with wolfSSL 5.7 and I can see OCSP extension being transmitted as before. Which improves my understanding of this API and I also changed the comments accordingly.
sha=c909e58b39419dba9c0ac2878fc6d1b95db53057 |
components/esp-tls/esp_tls_wolfssl.c
Outdated
/* Mimic the semantics of mbedtls_ssl_set_hostname(), which does not only set the SNI, | ||
but if not set will make the mbedTLS stack skip certificate verification. */ | ||
if ((ret = wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, use_host, strlen(use_host))) != WOLFSSL_SUCCESS) | ||
{ |
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.
/* Mimic the semantics of mbedtls_ssl_set_hostname(), which does not only set the SNI, | |
but if not set will make the mbedTLS stack skip certificate verification. */ | |
if ((ret = wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, use_host, strlen(use_host))) != WOLFSSL_SUCCESS) | |
{ | |
/* Mimic the semantics of mbedtls_ssl_set_hostname(). */ | |
if ((ret = wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, use_host, strlen(use_host))) != WOLFSSL_SUCCESS) { |
Sorry I missed this syntax change previously.
I think commit needs to be updated as well, mbedTLS stack does not skip certificate verification.
Can you also check if the pre-commit passes for the MR
You can check if the pre-commit is passing with following command
pre-commit run --all-files --from-ref HEAD~8 --to-ref HEAD
I hope you have already installed pre-commits, if not please find the instructions here
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.
@frankencode Can you please make this one change.
Thanks
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.
I actually copied that comment from mbedtls documentation. The documentation on mbedtls_ssl_set_hostname states:
Warning: If the hostname is not set with this function, Mbed TLS will silently skip certificate verification entirely. Always set the hostname with this function - even when not using SNI!
For the esp_tls_mbedtls.c logic this means if cfg->skip_common_name is true mbedtls might do as the documenation says and skip any checks on the certificate send by the server.
But you are right, that might be an issue that needs to be raised on the mbedtls backend side and the comment is misplaced here!
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.
I've updated the comment. Check out the new commit.
Almost all sites these days are virtually hosted and hence SNI (server name indicator TLS extension) should be enabled by default. In addition this change enables OCSP (online server status protocol) support for esp-tls clients using the wolfSSL backend. The 3 code lines enable OCSP stabling v1. By default this feature is disabled. (I will send another PR on esp-wolfssl repository to allow to enable it easily.)
sha=d2d43bbe78bdd9d62698f9c1db33d11a23ac8382 |
… chains
This change makes the wolfSSL backend sent the complete TLS client certificate chain. This align the wolfSSL backend with the behavior of the mbedTLS backend. Some servers need the intermediate certificates to verify a client certificate. If the provided PEM file contains only a single certificate this change has no effect and the behavior will be as before.
This impacts higher level APIs to function as someone would expect. E.g.: esp_websocket_client_config_t.client_cert: when passing here a pem file containing 2 certificates (the CA's and the client's) it would be expected that both are transmitted during TLS handshake.