Allow building with multiple SSL backends, selecting which one to use at runtime #1601
Allow building with multiple SSL backends, selecting which one to use at runtime #1601dscho wants to merge 34 commits into
Conversation
|
Oh, and any idea why Travis fails with: |
This is because checksrc.pl fails:
... and some more. On Windows, you can run projects\checksrc.bat. |
Sorry, I haven't yet looked at the code so I'll just answer in general terms first: connectdata is the struct for each independent connection so I think it makes sense to make sure that knows which SSL backend is being used for that. I don't think we'll be able to mix/change SSL-backend on an existing connection so if you want to change, you need a new connection. (As a side-note to remember: this needs to be taken into account when re-using connections in multi-ssl mode.)
Sure. But as you say we should probably consider how to make it work with other combos later on. And while the pandora is already out of the box: having more than two...
Yeah, some of the backend build-time stuff clearly needs to be made into run-time conditionals since I suppose there will be a few features that not all backends in the multi-ssl bundle support uniformly!
Yes, sounds like the way forward. Each backend could setup their own structs, similar to protocols set up structs for each protocol. Then we'd end up with a struct pointer to use for doing backend-ssl operations and for multi-ssl we would simply need to be able to use more than one/change the pointer run-time.
Tricky stuff. Can we start with you saying something how you envision this support to be used by application code? With a better understanding for the use case, maybe figuring out the API for it will be easier.
From a user's perspective, I would imagine that just adding SSL-options to configure would be the logical way. Ie both |
Ah, thanks! That is a problem, of course, as the Should I use a shorter name? Or introduce a macro? If so, what name would you prefer? |
Oh... I did not mean to mix SSL backends, sorry, that use case never crossed my mind...
That is easy: just add the straight-forward support for another SSL backend to It will be even easier with the vtable-like approach.
Right. The first commit already converts quite a few, such as Related: I saw that some of the SSL backends' headers define quite a few constants that seem to be used only by the SSL backends themselves, e.g.
You mean That sounds really clean to me, too.
Oh, sorry, should have said that earlier. The use case is So what I imagine is that either the administrator or the user set a config variable (say, In other words, it needs to be set once per process. Git for Windows would not need the ability to re-configure cURL to use another one after the initial configuration.
That's a good idea! I just do not know how to handle the default SSL backend, then (you'd want to use the first configured one, right?). And that does not even start to handle CMake, with which I ask for a lot more guidance because I know it so little... |
|
Oh, about the |
|
Okay, buckle up... |
|
This is an almost complete rewrite of the topic branch. And it is quite a bit bigger. I tried my best to reorder the patches in a logical manner, and to split the changes into meaningful commits. This time, I handle all SSL backends (although you have to configure at build time which ones you want to build against explicitly). This time, Since I do not have access to all of the SSL backends (only GNU TLS, OpenSSL and Secure Channel), I could not compile-test, unfortunately. The good news is that in practice, it will only matter if anybody tries to build with a different set of multiple backends: in that case, something like 65c442e may be needed (i.e. some As before, though, the runtime configuration happens via the environment variable Also as before, CMake is not handled at all. But then, it looks as if CMake lags behind on SSL backends anyway 😄 Also as before, I have no test coverage (I have not mentioned this before, though). Any guidance? |
467192e to
6896623
Compare
|
Coverage decreased (-73.8%) to 0.0% when pulling bc6e3478123173a12d4c09738ae38be374789786 on dscho:ssl-multi-backend into 922f800 on curl:master. |
|
Oh, i just realized that GitHub juggled the commits around in the PR view, because it orders them by commit date instead of topologically. Please let me know if you prefer strongly for me to rewrite the commits so that their commit dates do not reflect the original commits' dates, but are completely rewritten instead to force GitHub to show them in the correct order. |
|
Coverage decreased (-0.01%) to 73.796% when pulling 6896623e7f1cda98934a9cd636cb1b96ddaafe23 on dscho:ssl-multi-backend into 922f800 on curl:master. |
|
Coverage increased (+0.02%) to 73.828% when pulling 6896623e7f1cda98934a9cd636cb1b96ddaafe23 on dscho:ssl-multi-backend into 922f800 on curl:master. |
|
Any way I can help move this forward? |
|
Sorry, it's just me being a bit slow. I haven't forgot about it and I don't mean to intentionally leave you behind. I'll shape up and get some feedback to you on this soonish! |
|
@bagder no worries! You had a tough week, I fully understand. Besides, you are an OSS maintainer, and I understand the pressures of that role, too. For the record: I would love to get this into Git for Windows soon-ish, even as an experimental, patched version of cURL. To that end, I want to wait a little for the feedback I get, to gage how much of the design needs to change (if only at most cosmetic changes are necessary to get this merged, I would be happy with taking an early version for an experimental/intermediate MinGit for Windows). So if there is a chance for an early-bird preview of an overall verdict ("design looks good" or "actually, the way this is done needs to be overhauled to use XYZ instead of ABC"), I would be most thankful. |
|
I think the general design looks perfectly fine! I see you opted to make it possible to change ssl backend by setting an environment variable. Is that how you actually would like it done? For general applications that's a pretty awkward API so there should at least also be an additional option to set somehow, and possibly one of those options could be one that allows the environment variable control. There are compiler warnings in vtls/vtls.c (I recommend configure's Nit: please don't use |
Thanks! 😄
I find the environment variable less than ideal myself. However, I have no idea what the proper way is, according to cURL style. Is there a common way to pre-configure certain defaults before initializing cURL? Or should we have it as an option to be configured via the "curl_easy", safeguarding against setting it multiple times?
I tried to compile this with
Okay, fair enough. I am not too happy about "multissl" either. I thought of "varssl", but it still does not convey the "configure at run-time" nature of the backend... So far, I am still leaning toward "multissl". |
I've struggled with figuring out the best way for this init. There is the Otherwise I've been thinking that since multi-ssl is new functionality anyway, we might want to consider adding a totally new way to init this as well.
That would be cool, but several of the TLS libraries have thread-unsafe inits, so we want them done at the time of the global_init so that applications can have more control of that specific phase. warningsSorry for being brief, I just rebuilt the code with my "usual" configure setup which boils down to:
.. this finds and uses OpenSSL 1.1.0f (only). That then generates this set of warnings: |
|
The warnings are actually also in the travis build (I think we should consider doing |
|
Oops, yes, we do werror already since fc2e81c =) |
So maybe a new function
Ah... got it...
D'oh, I should have checked, and then simply read |
(brainstorming mode, just writing out my ideas and thoughts here) Maybe something like this (I picked the
Returns:
The avail value is a bitmask that is returned with information about which SSL backends that are available. The bit number in the bitmask represents the particular backend: That way an application can call |
In 86b8894 (sasl_gssapi: Added GSS-API based Kerberos V5 variables, 2014-12-03), an SSPI-specific field was added to the kerberos5data struct without moving the #include "curl_sspi.h" later in the same file. This broke the build when SSPI was enabled, unless Secure Channel was used as SSL backend, because it just so happens that Secure Channel also requires "curl_sspi.h" to be #included. In f4739f6 (urldata: include curl_sspi.h when Windows SSPI is enabled, 2017-02-21), this bug was fixed incorrectly: Instead of moving the appropriate conditional #include, the Secure Channel-conditional part was now also SSPI-conditional. Fix this problem by moving the correct #include instead. This is also required for an upcoming patch that moves all the Secure Channel-specific stuff out of urldata.h and encapsulates it properly in vtls/schannel.c instead. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
At the moment, cURL's SSL backend needs to be configured at build time. As such, it is totally okay for them to hard-code their backend-specific data in the ssl_connect_data struct. In preparation for making the SSL backend a runtime option, let's make the access of said private data a bit more abstract so that it can be adjusted later in an easy manner. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
So far, all of the SSL backends' private data has been declared as part of the ssl_connect_data struct, in one big #if .. #elif .. #endif block. This can only work as long as the SSL backend is a compile-time option, something we want to change in the next commits. Therefore, let's encapsulate the exact data needed by each SSL backend into a private struct, and let's avoid bleeding any SSL backend-specific information into urldata.h. This is also necessary to allow multiple SSL backends to be compiled in at the same time, as e.g. OpenSSL's and CyaSSL's headers cannot be included in the same .c file. To avoid too many malloc() calls, we simply append the private structs to the connectdata struct in allocate_conn(). This requires us to take extra care of alignment issues: struct fields often need to be aligned on certain boundaries e.g. 32-bit values need to be stored at addresses that divide evenly by 4 (= 32 bit / 8 bit-per-byte). We do that by assuming that no SSL backend's private data contains any fields that need to be aligned on boundaries larger than `long long` (typically 64-bit) would need. Under this assumption, we simply add a dummy field of type `long long` to the `struct connectdata` struct. This field will never be accessed but acts as a placeholder for the four instances of ssl_backend_data instead. the size of each ssl_backend_data struct is stored in the SSL backend-specific metadata, to allow allocate_conn() to know how much extra space to allocate, and how to initialize the ssl[sockindex]->backend and proxy_ssl[sockindex]->backend pointers. This would appear to be a little complicated at first, but is really necessary to encapsulate the private data of each SSL backend correctly. And we need to encapsulate thusly if we ever want to allow selecting CyaSSL and OpenSSL at runtime, as their headers cannot be included within the same .c file (there are just too many conflicting definitions and declarations for that). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When building software for the masses, it is sometimes not possible to decide for all users which SSL backend is appropriate. Git for Windows, for example, uses cURL to perform clones, fetches and pushes via HTTPS, and some users strongly prefer OpenSSL, while other users really need to use Secure Channel because it offers enterprise-ready tools to manage credentials via Windows' Credential Store. The current Git for Windows versions use the ugly work-around of building libcurl once with OpenSSL support and once with Secure Channel support, and switching out the binaries in the installer depending on the user's choice. Needless to say, this is a super ugly workaround that actually only works in some cases: Git for Windows also comes in a portable form, and in a form intended for third-party applications requiring Git functionality, in which cases this "swap out libcurl-4.dll" simply is not an option. Therefore, the Git for Windows project has a vested interest in teaching cURL to make the SSL backend a *runtime* option. This patch makes that possible. By running ./configure with multiple --with-<backend> options, cURL will be built with multiple backends. For the moment, the backend can be configured using the environment variable CURL_SSL_BACKEND (valid values are e.g. "openssl" and "schannel"). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
There is information about the compiled-in SSL backends that is really no concern of any code other than the SSL backend itself, such as which function (if any) implements SHA-256 summing. And there is information that is really interesting to the user, such as the name, or the curl_sslbackend value. Let's factor out the latter into a publicly visible struct. This information will be used in the upcoming API to set the SSL backend globally. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Let's add a compile time safe API to select an SSL backend. This function needs to be called *before* curl_global_init(), and can be called only once. Side note: we do not explicitly test that it is called before curl_global_init(), but we do verify that it is not called multiple times (even implicitly). If SSL is used before the function was called, it will use whatever the CURL_SSL_BACKEND environment variable says (or default to the first available SSL backend), and if a subsequent call to curl_global_sslset() disagrees with the previous choice, it will fail with CURLSSLSET_TOO_LATE. The function also accepts an "avail" parameter to point to a (read-only) NULL-terminated list of available backends. This comes in real handy if an application wants to let the user choose between whatever SSL backends the currently available libcurl has to offer: simply call curl_global_sslset(-1, NULL, &avail); which will return CURLSSLSET_UNKNOWN_BACKEND and populate the avail variable to point to the relevant information to present to the user. Just like with the HTTP/2 push functions, we have to add the function declaration of curl_global_sslset() function to the header file *multi.h* because VMS and OS/400 require a stable order of functions declared in include/curl/*.h (where the header files are sorted alphabetically). This looks a bit funny, but it cannot be helped. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The newly-introduced curl_global_sslset() function deserves to be show-cased. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Previously, the code assumed that at most one of the SSL backends would be compiled in, emulating OpenSSL's functions if the configured backend was not OpenSSL itself. However, now we allow building with multiple SSL backends and choosing one at runtime. Therefore, metalink needs to be adjusted to handle this scenario, too. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This new feature flag reports When cURL was built with multiple SSL backends. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
To discern the active one from the inactive ones, put the latter into parentheses. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When only one SSL backend is configured, it is totally unnecessary to let multissl_init() configure the backend at runtime, we can select the correct backend at build time already. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Previously, we used as default SSL backend whatever was first in the `available_backends` array. However, some users may want to override that default without patching the source code. Now they can: with the --with-default-ssl-backend=<backend> option of the ./configure script. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
There is a mode in which libcurl is compiled with versioned symbols, depending on the active SSL backend. When multiple SSL backends are active, it does not make sense to favor one over the others, so let's not: introduce a new prefix for the case where multiple SSL backends are compiled into cURL. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
8f47553 to
ee68d80
Compare
|
Okay, i added two more commits (and rebased the whole shebang, sorry that my previous "fixed in " no longer apply verbatim). Now you can set the default SSL backend via While working on that, I noticed something I had missed earlier: the symbols are optionally prefixed with the current SSL backend. If more than one SSL backend was compiled in, one of them was picked for said versioning. I changed that to the While at it, I also moved the determination whether more than one SSL backend was configured into the @bagder I hope you agree with this course of action? |
The Pull Request at curl/curl#1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Pull Request at curl/curl#1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Pull Request at curl/curl#1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Pull Request at curl/curl#1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Pull Request at curl/curl#1601 adds support for choosing the SSL backend at runtime to cURL, and will hopefully be merged before version 7.56.0 comes out. Git for Windows will ship with those patches backported to 7.54.1 (and come August 9th, 2017, 7.55.0 and later). This patch adds the Git side of that feature: by setting http.sslBackend to "openssl" or "schannel", Git for Windows can now choose the SSL backend at runtime. This comes in handy because Secure Channel ("schannel") is the native Windows solution, accessing the Windows Credential Store, thereby allowing for enterprise-wide management of certificates. For historical reasons, Git for Windows needs to support OpenSSL still, as it has previously been the only supported SSL backend in Git for Windows for almost a decade. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Is this PR still on track for 7.56.0? |
|
absolutely! |
Awesome! Please ping me with whatever fallout there is to fix, and I will try my best to address the issues as quickly as time allows. |
|
(And sorry about the incorrect version in the man page, I saw that you fixed this in |
Git for Windows uses cURL and offers users the choice whether to use OpenSSL or Secure Channel for its HTTPS transport. Unfortunately, this is an install-time option (swapping out binaries because it is a build time option in cURL), when it would be so much better a user experience if this could be a runtime option.
The latest cURL user survey also lists the desire for runtime-configurable SSL backends (and I was not the only one asking for it).
So here is a start.
What it does is this: it moves OpenSSL's and Secure Channel's private data into a union, and then introduces a new SSL backend dubbed "multi" that enables both members of that union, and sets a set of function pointers upon
Curl_ssl_init()to the function implementations of the backend configured via the new environment variableCURL_SSL_BACKEND. Supported values are "openssl" and "schannel" for the moment.In the interest of efficiency, I would like to solicit reviews and advice at this stage, which is already usable as far as Git for Windows' needs are concerned. In particular, I would love to get feedback on these questions:
backendunion instruct connectdata? Or would another method of adjusting the backends' code be preferable?#undef HAS_ALPNbefore including the Secure Channel code in the multi backend. That's because my setup (a slightly modified MSYS2 environment) seems to offer ALPN support with OpenSSL, but not with Secure Channel. This would need to be a runtime option, right? Would it be okay to fold that into the newly-introducedcurlssl_featuresenum? What other flags need to go there (I guessHTTPS_PROXY_SUPPORT, at least)?#define curlssl_*at the end of its header file, offering backend-specific functions via a unified interface. Should I introduce a new C++-like "virtual table" struct for these functions instead of setting function pointers as I do right now inmulti_init()?configure? I do not want to duplicate it (--with-opensslwould need to perform the same auto-detection of files and features as--with-multissl=openssl[...])...CURL_SSL_BACKENDin the caller, that looks a little too fragile to me)?I would love to get lots of good feedback how to develop this topic branch so that it can eventually be integrated into cURL.