-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
Draft: Keep FIPS provider #48950
base: main
Are you sure you want to change the base?
Draft: Keep FIPS provider #48950
Conversation
Review requested:
|
Please note that I will be on holidays/off the grid for next week, so have patience when I'm not responding.😉 I just wanted to kick this off before leaving. |
In some cases, the CSPRNG may fail to seed properly, which currently results in assertion failure and core dump. This change will turn the behavior in a better-debuggable error report.
9fbb55f
to
03d3ccf
Compare
src/crypto/crypto_util.cc
Outdated
@@ -125,7 +125,6 @@ bool ProcessFipsOptions() { | |||
OSSL_PROVIDER* fips_provider = OSSL_PROVIDER_load(nullptr, "fips"); | |||
if (fips_provider == nullptr) | |||
return false; | |||
OSSL_PROVIDER_unload(fips_provider); |
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.
@danbev may remember why the load/unload. From the PR my read is that it was to be able to throw an error during options processing, but not have the provider loaded unless it was used.
Looking at the other branch I'm wondering if this would be consistent with what we have in the non >= section
#if OPENSSL_VERSION_MAJOR >= 3
if (!EVP_default_properties_is_fips_enabled(nullptr)) {
OSSL_PROVIDER* fips_provider = OSSL_PROVIDER_load(nullptr, "fips");
if (fips_provider == nullptr)
return false;
OSSL_PROVIDER_unload(fips_provider);
return EVP_default_properties_enable_fips(nullptr, 1) &&
EVP_default_properties_is_fips_enabled(nullptr);
}
I think you mentioned to me that doing that still fails. If so, then I wonder why if FIPs is already enabled on the base system, then why the appropriate provider is not already loaded. It would be good to confirm if its expected that you still need to load the provider even if the system has enabled FIPs to make sure the correct provider is being used.
@richardlau I know you have also look a bit at how FIPs and providers work so I'd be interested in any insights you have as well.
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, according to my testing just having the guard there does not help on non-FIPS system! I did not test it yet on FIPS system, as in my opinion coredump should not happen in any system configuration.
The issue might be in getting confused on what the EVP_…
functions actually do – according to manual page, they do not check the provider status at all:
EVP_default_properties_enable_fips() sets the 'fips=yes' to be a default property if enable is non zero, otherwise it clears 'fips' from the default property query for the given libctx. It merges the fips default property query with any existing query strings that have been set via EVP_set_default_properties().
EVP_default_properties_is_fips_enabled() indicates if 'fips=yes' is a default property for the given libctx.
I'm very much guessing in the dark here, but I suspect setting the EVP property fips=yes
does not load the provider, nor does it indicate it was ever loaded – it just indicate you want to use it by default.
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.
@richardlau I know you have also look a bit at how FIPs and providers work so I'd be interested in any insights you have as well.
FIPS support in OpenSSL 3 is completely different from what came before and works with the (new for OpenSSL 3) Provider model. This model is a lot more generic -- you may have any number of providers loaded (aka available) and can change the loaded providers at runtime (through OSSL_PROVIDER_load
/OSSL_PROVIDER_unload
). Since it's generic the providers themselves can be named anything, i.e. a FIPS provider does not need to be named "fips" (which means the code in Node.js being queried is overly specific and probably does need to change).
So what does "fips mode" mean in the provider model? When fetching an algorithm in OpenSSL the algorithm may be implemented by more than one loaded provider. If that is the case it is undefined which implemented is used unless the fetch was constrained by a property query, e.g. fips=yes
-- note this may still match more than one provider if those providers set fips=yes
on their algorithm implementations. You can limit to a specifically named provider by property (e.g. provider=fips
) but remember that OpenSSL puts no special meaning on the provider names themselves. The property query can either be set on every individual fetch call or as a default property (which applies to all fetch calls that don't specify their own). For OpenSSL 3 Node.js treats "fips mode" as the equivalent of having set a default query of fips=yes
.
The genericness of the provider model coupled with the ability to dynamically change the loaded providers also means that it is possible that an algorithm fetch returns no matching implementations. Such a corner case was reported in #46200 -- if fips=yes
is set as the default property query and no implementations that have that property are available (e.g. no provider has been loaded that provides a compatible implementation) then in that reported case it was not possible to initialize the cryptographically secure pseudorandom number generator (CSPRNG).
With regards to the code being commented on, I think the intention seems to be to determine if it might be possible to load a provider called "fips" (i.e. the provider is on the filesystem and could be loaded). This is a highly specific assumption -- OpenSSL 3 makes no guarantee that a provider called "fips" actually implements any FIPS compatible algorithms -- so is probably a flawed assumption to be making. I believe OpenSSL's OSSL_PROVIDER_load
/OSSL_PROVIDER_unload
operates via reference counting (so the provider may not actually be unloaded if it had already been loaded prior to the OSSL_PROVIDER_load
call on L125).
I suspect the issue might be that Node.js is not picking up the OpenSSL configuration as the default for Node.js is now to look for a configuration called In this case, where no configuration is loaded (because no |
Slight correction, at runtime you can pass |
So, finally back enough to respond 🙂
Thanks for the pointer – that does seem to help when I have my system configured to run in FIPS mode (still on Fedora):
I will update our build scripts to use the proper configure option. However, this still does not help when the system is not in FIPS mode:
The main culprit seems to be that in this case, even the shared configuration makes no effort to load the fips provider.
The question now is what do we want to do with this. I would have thought that if we are going to provide Alternatively, we need a better check for the availability of the fips provider, and only set the Thoughts? @richardlau |
My main concern is "the fips provider" is no longer a binary true/false. In a Linux distro like Fedora you could probably make some assumptions that, yes, there is a provider called "fips" that should be available and loaded, but in the general case it is a bad assumption that there is a provider called "fips", or (even if there is such a named provider) that it is providing FIPS capable functionality, or that some other named provider is available providing FIPS functionality. But maybe I'm overthinking this and we can restrict what |
Hm, given that it seems the entire code path is dependent on lot of assumptions that we are not comfortable making when working with libssl other than the bundled one ( My POV here is that a distribution could (and in case of Fedora/RHEL definitely does) have an opinion on how FIPS should be enabled and handled; in RHEL docs, there should currently be a paragraph strongly discouraging the user from ever touching the FIPS options on node, even should they work as assumed. We are currently having a discussion on downstream patching the node to effectively disabling the options anyway, but this seems like a good point to ask if that could be included here as well. 😇 |
I'd be okay with that. When I rewrote the FIPS documentation (#48194) for this project and was testing the instructions I did observe that cc @nodejs/crypto @nodejs/distros |
Something along these lines makes sense to me. I might be SemVer major (although it would not affect shipping binaries, just people who build themselves), but I think heading down the path where configuration of FIPs when using a shared OpenSSL libary is done outside of Node.js itself makes sense. does that make sense to you as well? |
03d3ccf
to
137735c
Compare
Another draft for your consideration.🙂 I've added a define A note should also be added to the docs about the fips flags status; I will add that if I get general approval on the proposed changes. WDYT? @mhdawson @richardlau |
@khardix I do think the update would be SemVer major, and would still like to get confirmation from at least one more distro that it makese sense to them as well. |
With shared OpenSSL >= 3, I'm encountering a core dump/assertion failure when trying to use
--force-fips/--enable-fips
:I'm on Fedora 38,
openssl-libs-3.0.9-1.fc38.x86_64
.I was toying with fixing this a bit, and this PR is my WIP approach at fixing this. The first commit just turns that assertion into an error message in order to better debug what is wrong with the CSPRNG; the other makes the error go away, but I'm not sure what other impact it can have.
On the other hand, can anyone tell me why the unload is there? I would thought that when the FIPS flags are encountered, we would want to actually leave the provider in place in order to be utilized by the application, not immediately yanking it back once confirmed it can be loaded.
I also noticed that in
TestFipsCrypto()
loads the provider again, then leaves it loaded… 😕I'm making it as draft for now, because I want to know more about the points above before trying to actually push the change.