-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
Support both OpenSSL 1.1.0 and 1.0.2 #16130
Conversation
@shigeki, how does this approach sound to you? |
/cc @nodejs/crypto |
/cc @nodejs/tsc |
@davidben do you have an insight into if there is planned fips support for openssl 1.1.0? I know that has been a reason for us to not upgrade in the past |
You'll have to ask OpenSSL folks about that one. But I don't think it makes sense to wait for it before supporting it at all, maybe just before shipping it in the bundled copy. Switching to 1.1.0 is a large change with deprecation consequences. You're better off getting things in sooner rather than later so you can plan for it before 1.0.2 goes EOL. (I intentionally did not switch the bundled copy in this PR.) Moreover, folks like Linux distributions may switch before you do. They may just pick up the old PR which would be problematic as it didn't work right. |
@MylesBorins I think we are at the point where regardless of FIPs validation status for 1.1.0 we will need to upgrade. I believe that version 8.x was the last one where openssl 1.0.2 would be supported long enough to use it in an LTS release. |
@davidben thanks for putting this together. It is important that we have a path to 1.1.0 sooner than later. Based on the size of change, this may take a bit of time for the reviewers to be able to comment. |
node 8.x and openssl 1.0.2 both go out of support in Dec, 2019 Interestingly, openssl 1.1.0 goes out of support in Aug, 2018, before 1.0.2. We can use openssl 1.1.0 for node 9.x, because 9.x will be short-lived. For node 10.x, which is LTS for us, we will want to use an openssl release that will be supported for its lifetime, I assume. They haven't released one yet, and I can't find anywhere where they commit to a release date for the next LTS openssl, though they say this:
Since 1.0.2 was released in jan 2015, if I understand correctly, there should be a LTS release of openssl by jan 2019. But hopefully they won't wait to the last minute, because node 10.x goes LTS in october 2018. What would be most awkward for us would be if openssl 1.1.0 goes out of support while we are using it for 10.x and getting ready for LTS, and there is no replacement yet. What would we do, go back to openssl 1.0.2? I strongly suspect that won't happen. That either 1.1.0 will become LTS by then, or that there will be a 1.2.0 by then and it will be called LTS, but I'm speculating. |
I'm adding the TSC-Agenda label. Specifically I think the TSC should create a interest group specifically focused around coming up with some suggestions around how we can deal with this. We only have 6 months before the 10.x cut |
I can't speak for OpenSSL, but I am sure either 1.1.1 will be released before 1.1.0 goes out of support or they will extend support for 1.1.0. It wouldn't make much sense for 1.0.2 to be the only supported OpenSSL release at any point. :-) |
OpenSSL 1.1.1's release is essentially waiting on a final version of the TLS 1.3 specification from the IETF. There is no hard deadline on this happening, but I agree with @davidben that it would be silly for 1.0.2 to be the only supported OpenSSL release. |
/cc @danbev |
An important issue we need to consider here is how packagers are dynamically linking Node to their own versions of OpenSSL. Maximising the options for them to do that is going to be in our interests because we're not going to stop them from doing this (dynamic linking and separating out dependant packages is strict policy in Debian-land for instance) and they're already shipping Node in ways that are not strictly compatible with core since we do some patching of OpenSSL in our own statically linked version that is never picked up downstream. So, supporting both 1.0.2 and 1.1.0 as soon as possible makes a lot of sense, so I'd be keen to see this PR move forward even while we're tied primarily to 1.0.2. Packagers can then opt to compile against 1.1.0 if it suits their needs, or stick to 1.0.2 which is still going to be more common for Linux distros for a while yet. i.e. there's going to be an extended period where our downstream packagers are going to be (either forced to, or preferring to) opting for 1.1.x or 1.0.2 in their linking regardless of what we would prefer them to do and the less we help them the more they're going to be doing a hacky job of it. Given that we're about to hit 9.x, we're unlikely to get any breaking changes in there so the ideal might look something like this:
We can fire up at least one dedicated 1.1.0 node in Jenkins like we have for FIPS now, it'd be great to be testing it multiple ways (ideally we'd also have dynamic-linking nodes in there too, we've talked about that for a while but never done it). wrt to some of the specifics in OP:
Huge thanks to @davidben for this work and being so thorough in description and advice here! |
To add to the distro thing, another reason to get 1.1.0 support in sooner rather than later is this way the support won't bitrot and you can test out all the ramifications while you still have time for the 9.x deprecation cycle. Everything I wrote in the OP was stuff I only realized working on this PR.
Yup, the intent was that this PR has no user-visible changes when building against 1.0.2. (Though certainly this needs careful review to make sure I got it right!)
As far as I can tell, that's correct. The closest you all get to using some of it is in TLS tests used anonymous ciphers to avoid needing to configure certs. (This PR makes them configure certs instead.)
It's mostly that OpenSSL's errors are not very good at being stable. The most obvious change is that the function code switched from all caps to actually being the function code. And function codes existing at all mean renaming an internally OpenSSL function will change that part of the error string! And then there are some minor differences just because some of the SSL code was reworked and invalid things are detected differently. (Version negotiation in particular goes through a very different codepath. OpenSSL 1.0.x's version negotiation logic was kind of weird. OpenSSL 1.1.0 does it better.)
Not a problem in itself but the It's possible the answer is just exposing some constant[*] which queries the key length and asking callers to use that rather than a hardcoded 48? In that case, you probably could avoid the relevant commit in this patch and do a deprecation cycle? (I can adjust this PR to drop the "compatibility hack" commits you all don't end up needing. I erred on the side on including them here so it'd be easier to see what the options were.) [*] Or something? The OpenSSL API to query it is per-
Yeah, there's no rush here. You get a mess of deprecated API warnings, but it builds fine. |
https://nodejs.org/api/tls.html#tls_server_setticketkeys_keys
I see the problem now, we've hardwired it in our API and docs, that's a bit unfortunate. I'm not seeing an obvious best way around this. A constant could be backported to 8.x and maybe even 6.x so users could start querying it. The awkward part is that we throw if ! 48 ( Next step would be some reviews by people with more of a clue. @indutny, @bnoordhuis, @shigeki, maybe @sam-github? Would you mind taking a look over the code? It's a surprisingly small diff and shouldn't take too long. I've had a look over it and will play more before I give a +1. It'll also need some testing actually compiling against 1.1.0 across our platforms so we'll have to come up with a strategy that's a little more comprehensive than just adding a single 1.1.0 box into the mix. @davidben would you mind rebasing off master so we can run this in CI? Currently having a problem with that: https://ci.nodejs.org/job/node-test-commit/13351/console |
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.
Mostly LGTM. I agree on deprecating ecdhCurve
and that there probably isn't much risk in changing the ticket keys API (and defensible for v9.x because it's ultimately openssl that drives this change.)
X509_STORE* store = SSL_CTX_get_cert_store(ctx); | ||
X509_STORE_CTX store_ctx; | ||
|
||
ret = X509_STORE_CTX_init(&store_ctx, store, nullptr, nullptr); |
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 explain in the commit log why this no longer works? X509_STORE_CTX_init()
and X509_STORE_CTX_cleanup()
still exist in 1.1.0, don't they?
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.
Yeah, I'm not really sure why they still exist TBH. Some weird form of reset I guess. The reason this is needed is X509_STORE_CTX
is opaque and thus cannot be stack-allocated. (I can add a note in the commit message to clarify this.)
src/node_crypto_bio.cc
Outdated
bio->ptr = nullptr; | ||
if (BIO_get_shutdown(bio)) { | ||
if (BIO_get_init(bio) && BIO_get_data(bio) != nullptr) { | ||
delete NodeBIO::FromBIO(bio); |
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.
Just curious, is the qualification necessary?
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. I'm not sure what I was doing there. Removed. :-)
src/node_crypto_bio.cc
Outdated
#else | ||
static BIO_METHOD* method = nullptr; | ||
|
||
if (!method) { |
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.
Tiny style nit: method == nullptr
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.
Done.
src/node_crypto.cc
Outdated
static void SSL_SESSION_get0_ticket(const SSL_SESSION* s, | ||
const unsigned char** tick, size_t* len) { | ||
*len = s->tlsext_ticklen; | ||
if (tick != NULL) { |
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.
nullptr
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.
Done.
src/node_crypto.cc
Outdated
env, | ||
reinterpret_cast<char*>(sess->tlsext_tick), | ||
sess->tlsext_ticklen).ToLocalChecked(); | ||
env, reinterpret_cast<const char *>(ticket), length).ToLocalChecked(); |
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.
Tiny nit: const char*
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.
Done.
assert.ok( | ||
/SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol/.test(e.message), | ||
/SSL routines:[^:]*:(unknown protocol|wrong version number)/.test( | ||
e.message), |
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.
Tiny nit: can you indent by four spaces here and below? (Line continuation.)
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.
eslint gets unhappy and seems to really want this indent.
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.
2 spaces is the standard line continuation for our JS style guide. This seems fine to me but if you really, really wanted to make it a bit less awkward because of the length then maybe just put the RegExp into a variable... ?
@@ -1084,8 +1084,10 @@ void SecureContext::SetECDHCurve(const FunctionCallbackInfo<Value>& args) { | |||
|
|||
node::Utf8Value curve(env->isolate(), args[0]); | |||
|
|||
#if OPENSSL_VERSION_NUMBER < 0x10100000L | |||
SSL_CTX_set_options(sc->ctx_, SSL_OP_SINGLE_ECDH_USE); | |||
SSL_CTX_set_ecdh_auto(sc->ctx_, 1); |
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.
Somewhat amusingly, we only started doing that last month. Is there a better way of doing that with openssl 1.0.2? I couldn't find one.
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.
Nah, that's the way to do it. They added and deprecated APIs one release after another. :-) Both of those calls are no-ops in 1.1.0. The ifdef isn't needed, but it silences some build warnings, so I figured I'd clear them.
test/parallel/test-tls-econnreset.js
Outdated
this.destroy(); | ||
server.close(); | ||
}).write('123'); | ||
}).on('error', (err) => { assert.fail(err); }); |
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.
Could be simplified to .on('error', common.mustNotCall())
.
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.
Done.
@@ -7,20 +7,22 @@ if (!common.hasIPv6) | |||
common.skip('no IPv6 support'); | |||
|
|||
const assert = require('assert'); | |||
const fixtures = require('../common/fixtures'); |
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.
Not needed, available as common.fixtures
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.
Hrm. That doesn't seem to work. I also don't see common.fixtures
in any other test. There's common.fixturesDir
, though that just points to the directory. Is that plus fs.readFileSync
plus path-munging preferred over fixtures.readKey
?
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.
This seems fine as is. It matches what people were instructed to use in the latest Code & Learn.
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.
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; | ||
const cryptoConstants = require('crypto').constants; | ||
const SSL_OP_NO_TICKET = cryptoConstants.SSL_OP_NO_TICKET; | ||
const OPENSSL_VERSION_NUMBER = cryptoConstants.OPENSSL_VERSION_NUMBER; |
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 can DRY this like this:
const { OPENSSL_VERSION_NUMBER, SSL_OP_NO_TICKET } =
require('crypto').constants;
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.
Done.
e4c8282
to
fd6658e
Compare
I've addressed the comments, rebased to master, and removed the original I've left the ticket one alone for the time being, since it sounds like that's still being discussed. I also realize now I missed the "DSS1" one... shall I remove that workaround in favor of a deprecation + test suppression like |
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, basically LGTM pending two questions.
I'm not sure how to make that as deprecated, so I'll probably need some help from one of you all there.
Untested but this diff shows the basic principle:
diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md
index 75c5c0feb5..62c0a7a0ce 100644
--- a/doc/api/deprecations.md
+++ b/doc/api/deprecations.md
@@ -737,6 +737,13 @@ Type: Runtime
internal mechanics of the `REPLServer` itself, and is therefore not
necessary in user space.
+<a id="DEP0083"></a>
+### DEP0083: Disabling ECDH with `{ ecdhCurve: false }`
+
+Type: Runtime
+
+Blurb goes here.
+
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
[`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index d7e349b239..ff2fa178d8 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -919,6 +919,15 @@ Server.prototype.setTicketKeys = function setTicketKeys(keys) {
};
+function ecdhCurveWarning() {
+ if (ecdhCurveWarning.emitted) return;
+ process.emitWarning('{ ecdhCurve: false } is a deprecated no-op.',
+ 'DeprecationWarning',
+ 'DEP0083');
+ ecdhCurveWarning.emitted = true;
+}
+ecdhCurveWarning.emitted = false;
+
Server.prototype.setOptions = function(options) {
this.requestCert = options.requestCert === true;
this.rejectUnauthorized = options.rejectUnauthorized !== false;
@@ -931,8 +940,10 @@ Server.prototype.setOptions = function(options) {
if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
if (options.crl) this.crl = options.crl;
if (options.ciphers) this.ciphers = options.ciphers;
- if (options.ecdhCurve !== undefined)
+ if (options.ecdhCurve !== undefined) {
this.ecdhCurve = options.ecdhCurve;
+ if (this.ecdhCurve === false) ecdhCurveWarning();
+ }
if (options.dhparam) this.dhparam = options.dhparam;
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
I also realize now I missed the "DSS1" one... shall I remove that workaround in favor of a deprecation + test suppression like ecdhCurve?
No strong opinion. It doesn't cost much to keep around and logging deprecation warnings from C++ is kind of cumbersome because you have to thread through the node::Environment
. The function you are looking for is ProcessEmitWarning()
if you want to pursue that.
sc->ticket_key_aes_, iv) <= 0 || | ||
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), | ||
EVP_sha256(), nullptr) <= 0) { | ||
return -1; |
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.
Doesn't this leak the resources allocated by EVP_EncryptInit_ex()
when HMAC_Init_ex()
fails?
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 does not. hctx
is owned by the caller, so it's meant to get cleaned up after this function returns.
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.
Ah, I found the corresponding logic in t1_lib.c and s3_srvr.c. Okay, thanks for clearing that up.
if (EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), nullptr, sc->ticket_key_aes_, | ||
iv) <= 0 || | ||
HMAC_Init_ex(hctx, sc->ticket_key_hmac_, sizeof(sc->ticket_key_hmac_), | ||
EVP_sha256(), nullptr) <= 0) { |
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.
Same question.
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.
(ditto.)
src/node_crypto.cc
Outdated
@@ -3512,7 +3683,7 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) { | |||
ASSIGN_OR_RETURN_UNWRAP(&cipher, args.Holder()); | |||
|
|||
// Only callable after Final and if encrypting. | |||
if (cipher->initialised_ || | |||
if (cipher->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.
Ideally, use explicit nullptr checks for clarity.
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.
Done. (In patch set that will be uploaded shortly.)
@@ -7,20 +7,22 @@ if (!common.hasIPv6) | |||
common.skip('no IPv6 support'); | |||
|
|||
const assert = require('assert'); | |||
const fixtures = require('../common/fixtures'); |
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.
Sorry, you're both right. I thought (don't know why) that common.js re-exported everything from fixtures.js.
const OPENSSL_VERSION_NUMBER = | ||
require('crypto').constants.OPENSSL_VERSION_NUMBER; | ||
if (OPENSSL_VERSION_NUMBER >= 0x10100000) | ||
common.skip('false ecdhCurve not supported in OpenSSL 1.1.0') |
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.
missing semicolon
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.
Done. (In patch set that will be uploaded shortly.)
FYI https://ci.nodejs.org/job/node-test-commit/13361/ CI is happy with this as is sans the linting problem in test/parallel/test-tls-ecdh-disable.js |
I'm playing with 1.1.0 as a local (non-global) build to test this out and I have a few notes. First, for @davidben, I'm getting 2 failures, not just
Notes, mainly for me and for anyone else wanting to test this out for themselves without having to install OpenSSL globally:
My thinking here is that we could test in bulk across most of our CI by either checking in a A step beyond that for a more permanent test-dynamically-linked-openssl might be to bake in these steps into CI somewhere so they can be run on any of our hosts without requiring a special host like we do for FIPS. Or perhaps it's sufficient to just have a sub-job that reuses some of the other hosts to do this work across a small subset of our platforms. I'll take this up with the Build WG because it'd be great to start testing dynamic linking in our CI. We could then conceivably extend to some of the other shared libs we support. |
I'll try to get to the other comments later today or this week, but to answer the immediate test issues real quick: So, OpenSSL The immediate cause for this is openssl/openssl#4574, however I think ultimately it's Node that's misbehaving. You all are calling into user code within the OpenSSL info callback, which might re-entrantly call into more code. OpenSSL, 1.1.x or 1.0.x, is unlikely to behave well in all scenarios where Some part of the assembly leaking up to (Yeah, parallel/test-http2-create-client-connect failing is a known issue. Sorry I haven't gotten to writing up the details there yet. Just finished a much-needed vacation the past week, so I've got quite a backlog. :-) ) |
OpenSSL 1.0.x used a 48-byte ticket key, but OpenSSL 1.1.x made it larger by using a larger HMAC-SHA256 key and using AES-256-CBC to encrypt. However, Node's public API exposes the 48-byte key. Implement the ticket key callback to restore the OpenSSL 1.0.x behavior. PR-URL: #16130 Backport-PR-URL: #18622 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
OpenSSL 1.1.0 rejects RSA keys smaller than 1024 bits by default. Fix the tests to use larger ones. This test only cares that the PEM blob be missing a trailing newline. Certificate adapted from test/fixtures/cert.pem. PR-URL: #16130 Backport-PR-URL: #18622 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
This test is testing what happens to the server if the client shuts off the connection (so the server sees ECONNRESET), but the way it does it is convoluted. It uses a static RSA key exchange with a tiny (384-bit) RSA key. The server doesn't notice (since it is static RSA, the client acts on the key first), so the client tries to encrypt a premaster and fails: rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size SSL routines:ssl3_send_client_key_exchange:bad rsa encrypt OpenSSL happens not to send an alert in this case, so we get ECONNRESET with no alert. This is quite fragile and, notably, breaks in OpenSSL 1.1.0 now that small RSA keys are rejected by libssl. Instead, test by just connecting a TCP socket and immediately closing it. PR-URL: #16130 Backport-PR-URL: #18622 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
This is kind of hairy. OpenSSL 1.0.2 ignored the return value and always treated everything as SSL_TLSEXT_ERR_NOACK (so the comment was wrong and Node was never sending a warning alert). OpenSSL 1.1.0 honors SSL_TLSEXT_ERR_NOACK vs SSL_TLSEXT_ERR_FATAL_ALERT and treats everything unknown as SSL_TLSEXT_ERR_FATAL_ALERT. Since this is a behavior change (tests break too), start by aligning everything on SSL_TLSEXT_ERR_NOACK. If sending no_application_protocol is desirable in the future, this can by changed to SSL_TLSEXT_ERR_FATAL_ALERT with whatever deprecation process is appropriate. However, note that, contrary to https://rt.openssl.org/Ticket/Display.html?id=3463#txn-54498, SSL_TLSEXT_ERR_FATAL_ALERT is *not* useful to a server with no fallback protocol. Even if such mismatches were rejected, such a server must *still* account for the fallback protocol case when the client does not advertise ALPN at all. Thus this may not be worth bothering. PR-URL: #16130 Backport-PR-URL: #18622 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rod Vagg <rod@vagg.org>
Notable changes: * deps: * update V8 to 6.2.414.46 (Michaël Zasso) [#16413](#16413) * revert ABI breaking changes in V8 6.2 (Anna Henningsen) [#16413](#16413) * upgrade libuv to 1.19.1 (cjihrig) [#18260](#18260) * re land npm 5.6.0 (Myles Borins) [#18625](#18625) * ICU 60 bump (Steven R. Loomis) [#16876](#16876) * crypto: * Support both OpenSSL 1.1.0 and 1.0.2 (David Benjamin) [#16130](#16130) * warn on invalid authentication tag length (Tobias Nießen) [#17566](#17566) * async_hooks: * update defaultTriggerAsyncIdScope for perf (Anatoli Papirovski) [#18004](#18004) * use typed array stack as fast path (Anna Henningsen) [#17780](#17780) * use scope for defaultTriggerAsyncId (Andreas Madsen) [#17273](#17273) * separate missing from default context (Andreas Madsen) [#17273](#17273) * rename initTriggerId (Andreas Madsen) [#17273](#17273) * deprecate undocumented API (Andreas Madsen) [#16972](#16972) * add destroy event for gced AsyncResources (Sebastian Mayr) [#16998](#16998) * add trace events to async_hooks (Andreas Madsen) [#15538](#15538) * set HTTPParser trigger to socket (Andreas Madsen) [#18003](#18003) * add provider types for net server (Andreas Madsen) [#17157](#17157) * n-api: * add helper for addons to get the event loop (Anna Henningsen) [#17109](#17109) * cli: * add --stack-trace-limit to NODE_OPTIONS (Anna Henningsen) [#16495](#16495) * console: * add support for console.debug (Benjamin Zaslavsky) [#17033](#17033) * module: * add builtinModules (Jon Moss) [#16386](#16386) * replace default paths in require.resolve() (cjihrig) [#17113](#17113) * src: * add helper for addons to get the event loop (Anna Henningsen) [#17109](#17109) * add process.ppid (cjihrig) [#16839](#16839) * http: * support generic `Duplex` streams (Anna Henningsen) [#16267](#16267) * add rawPacket in err of `clientError` event (XadillaX) [#17672](#17672) * better support for IPv6 addresses (Mattias Holmlund) [#14772](#14772) * net: * remove ADDRCONFIG DNS hint on Windows (Bartosz Sosnowski) [#17662](#17662) * process: * fix reading zero-length env vars on win32 (Anna Henningsen) [#18463](#18463) * tls: * unconsume stream on destroy (Anna Henningsen) [#17478](#17478) * process: * improve unhandled rejection message (Madara Uchiha) [#17158](#17158) * stream: * remove usage of *State.highWaterMark (Calvin Metcalf) [#12860](#12860) * trace_events: * add executionAsyncId to init events (Andreas Madsen) [#17196](#17196) PR-URL: #18336
Notable changes: * deps: * update V8 to 6.2.414.46 (Michaël Zasso) [#16413](#16413) * revert ABI breaking changes in V8 6.2 (Anna Henningsen) [#16413](#16413) * upgrade libuv to 1.19.1 (cjihrig) [#18260](#18260) * re land npm 5.6.0 (Myles Borins) [#18625](#18625) * ICU 60 bump (Steven R. Loomis) [#16876](#16876) * crypto: * Support both OpenSSL 1.1.0 and 1.0.2 (David Benjamin) [#16130](#16130) * warn on invalid authentication tag length (Tobias Nießen) [#17566](#17566) * async_hooks: * update defaultTriggerAsyncIdScope for perf (Anatoli Papirovski) [#18004](#18004) * use typed array stack as fast path (Anna Henningsen) [#17780](#17780) * use scope for defaultTriggerAsyncId (Andreas Madsen) [#17273](#17273) * separate missing from default context (Andreas Madsen) [#17273](#17273) * rename initTriggerId (Andreas Madsen) [#17273](#17273) * deprecate undocumented API (Andreas Madsen) [#16972](#16972) * add destroy event for gced AsyncResources (Sebastian Mayr) [#16998](#16998) * add trace events to async_hooks (Andreas Madsen) [#15538](#15538) * set HTTPParser trigger to socket (Andreas Madsen) [#18003](#18003) * add provider types for net server (Andreas Madsen) [#17157](#17157) * n-api: * add helper for addons to get the event loop (Anna Henningsen) [#17109](#17109) * cli: * add --stack-trace-limit to NODE_OPTIONS (Anna Henningsen) [#16495](#16495) * console: * add support for console.debug (Benjamin Zaslavsky) [#17033](#17033) * module: * add builtinModules (Jon Moss) [#16386](#16386) * replace default paths in require.resolve() (cjihrig) [#17113](#17113) * src: * add helper for addons to get the event loop (Anna Henningsen) [#17109](#17109) * add process.ppid (cjihrig) [#16839](#16839) * http: * support generic `Duplex` streams (Anna Henningsen) [#16267](#16267) * add rawPacket in err of `clientError` event (XadillaX) [#17672](#17672) * better support for IPv6 addresses (Mattias Holmlund) [#14772](#14772) * net: * remove ADDRCONFIG DNS hint on Windows (Bartosz Sosnowski) [#17662](#17662) * process: * fix reading zero-length env vars on win32 (Anna Henningsen) [#18463](#18463) * tls: * unconsume stream on destroy (Anna Henningsen) [#17478](#17478) * process: * improve unhandled rejection message (Madara Uchiha) [#17158](#17158) * stream: * remove usage of *State.highWaterMark (Calvin Metcalf) [#12860](#12860) * trace_events: * add executionAsyncId to init events (Andreas Madsen) [#17196](#17196) PR-URL: #18336
Notable changes: * deps: * update V8 to 6.2.414.46 (Michaël Zasso) [nodejs#16413](nodejs#16413) * revert ABI breaking changes in V8 6.2 (Anna Henningsen) [nodejs#16413](nodejs#16413) * upgrade libuv to 1.19.1 (cjihrig) [nodejs#18260](nodejs#18260) * re land npm 5.6.0 (Myles Borins) [nodejs#18625](nodejs#18625) * ICU 60 bump (Steven R. Loomis) [nodejs#16876](nodejs#16876) * crypto: * Support both OpenSSL 1.1.0 and 1.0.2 (David Benjamin) [nodejs#16130](nodejs#16130) * warn on invalid authentication tag length (Tobias Nießen) [nodejs#17566](nodejs#17566) * async_hooks: * update defaultTriggerAsyncIdScope for perf (Anatoli Papirovski) [nodejs#18004](nodejs#18004) * use typed array stack as fast path (Anna Henningsen) [nodejs#17780](nodejs#17780) * use scope for defaultTriggerAsyncId (Andreas Madsen) [nodejs#17273](nodejs#17273) * separate missing from default context (Andreas Madsen) [nodejs#17273](nodejs#17273) * rename initTriggerId (Andreas Madsen) [nodejs#17273](nodejs#17273) * deprecate undocumented API (Andreas Madsen) [nodejs#16972](nodejs#16972) * add destroy event for gced AsyncResources (Sebastian Mayr) [nodejs#16998](nodejs#16998) * add trace events to async_hooks (Andreas Madsen) [nodejs#15538](nodejs#15538) * set HTTPParser trigger to socket (Andreas Madsen) [nodejs#18003](nodejs#18003) * add provider types for net server (Andreas Madsen) [nodejs#17157](nodejs#17157) * n-api: * add helper for addons to get the event loop (Anna Henningsen) [nodejs#17109](nodejs#17109) * cli: * add --stack-trace-limit to NODE_OPTIONS (Anna Henningsen) [nodejs#16495](nodejs#16495) * console: * add support for console.debug (Benjamin Zaslavsky) [nodejs#17033](nodejs#17033) * module: * add builtinModules (Jon Moss) [nodejs#16386](nodejs#16386) * replace default paths in require.resolve() (cjihrig) [nodejs#17113](nodejs#17113) * src: * add helper for addons to get the event loop (Anna Henningsen) [nodejs#17109](nodejs#17109) * add process.ppid (cjihrig) [nodejs#16839](nodejs#16839) * http: * support generic `Duplex` streams (Anna Henningsen) [nodejs#16267](nodejs#16267) * add rawPacket in err of `clientError` event (XadillaX) [nodejs#17672](nodejs#17672) * better support for IPv6 addresses (Mattias Holmlund) [nodejs#14772](nodejs#14772) * net: * remove ADDRCONFIG DNS hint on Windows (Bartosz Sosnowski) [nodejs#17662](nodejs#17662) * process: * fix reading zero-length env vars on win32 (Anna Henningsen) [nodejs#18463](nodejs#18463) * tls: * unconsume stream on destroy (Anna Henningsen) [nodejs#17478](nodejs#17478) * process: * improve unhandled rejection message (Madara Uchiha) [nodejs#17158](nodejs#17158) * stream: * remove usage of *State.highWaterMark (Calvin Metcalf) [nodejs#12860](nodejs#12860) * trace_events: * add executionAsyncId to init events (Andreas Madsen) [nodejs#17196](nodejs#17196) PR-URL: nodejs#18336
Setting ecdhCurve to false is already unsupported, so the deprecation should already be EOL. The test was skipped ever since we upgraded to OpenSSL 1.1.0. Refs: nodejs#16130
Setting ecdhCurve to false is already unsupported, so the deprecation should already be EOL. The test was skipped ever since we upgraded to OpenSSL 1.1.0. PR-URL: #22953 Refs: #16130 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Setting ecdhCurve to false is already unsupported, so the deprecation should already be EOL. The test was skipped ever since we upgraded to OpenSSL 1.1.0. PR-URL: #22953 Refs: #16130 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
First of all we fix the path of the inc file required by nodejs_6.13.1.bb by specifying a correct FILESEXTRAPATHS relative to the recipe's directory. We also add the DNS patch only for the 6 version as it is included in version 8 (which is the other version we are using). The last thing included in this patch is fixing compilation by using openssl 1.0.x for nodejs 6 because this version doesn't support openssl 1.1.x. See: nodejs/node#16130 Signed-off-by: Andrei Gherzan <andrei@balena.io>
While upgrading from OpenSSL 1.0.2 to 1.1.1, these tests were modified to recognize error messages from both OpenSSL releases. Given that OpenSSL 1.0.2 has been unsupported for years, it is safe to remove the older message patterns. Refs: nodejs#16130
While upgrading from OpenSSL 1.0.2 to 1.1.1, these tests were modified to recognize error messages from both OpenSSL releases. Given that OpenSSL 1.0.2 has been unsupported for years, it is safe to remove the older message patterns. Refs: nodejs#16130
While upgrading from OpenSSL 1.0.2 to 1.1.1, these tests were modified to recognize error messages from both OpenSSL releases. Given that OpenSSL 1.0.2 has been unsupported for years, it is safe to remove the older message patterns. Refs: #16130 PR-URL: #46709 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
While upgrading from OpenSSL 1.0.2 to 1.1.1, these tests were modified to recognize error messages from both OpenSSL releases. Given that OpenSSL 1.0.2 has been unsupported for years, it is safe to remove the older message patterns. Refs: #16130 PR-URL: #46709 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
While upgrading from OpenSSL 1.0.2 to 1.1.1, these tests were modified to recognize error messages from both OpenSSL releases. Given that OpenSSL 1.0.2 has been unsupported for years, it is safe to remove the older message patterns. Refs: #16130 PR-URL: #46709 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
WARNING: This is a rather large change.
In a previous PR, it was suggested that Node was uninterested in incremental changes for OpenSSL 1.1.0 support, with the worry that
OPENSSL_VERSION_NUMBER
ifdefs in the code will appear as if you support 1.1.0 when you don't. However, doing it all in one commit is also not feasible as one cannot possibly review such a change. I've instead attempted a middle ground where there is one PR, but it is made up of many many individual commits. It's recommended that you review each commit individually and not use Github's combined diff UI.Let me know if you would like the branch presented differently. (Go back to separate PRs, etc.)
This was partially adapted from PR #8491 but fixes some issues with it. This PR does not switch Node to OpenSSL 1.1.0, nor does it break support with OpenSSL 1.0.2. Two notes:
OpenSSL needs Allow DH_set0_key with only private key. openssl/openssl#4384 applied. This means OpenSSL 1.1.0g or later, which is not yet released. Since it would otherwise compile but not work, I've added a
OPENSSL_VERSION_NUMBER
check to catch this.test-http2-create-client-connect does not work. I've fixed an issue with the test itself, but it still doesn't pass in 1.1.0. The HTTP/2 code bypasses the usual JS afterWrite code which, combined with some weird OpenSSL behaviors, causes the error-handling to be a little off. It actually affects 1.0.2, just not on that particular test's input. I'll file a separate ticket for all that, but since it affects 1.0.2 too, I figure that should be separate to this PR. I'll put together a report with details later.
Additionally, switching to OpenSSL 1.1.0 comes with a number of removals. OpenSSL has documentation here:
https://www.openssl.org/news/openssl-1.1.0-notes.html
https://www.openssl.org/news/changelog.html#x7
I've described what I did notice below. Where it was feasible, I added a change to retain compatibility in Node's API as much as possible. I'll let you all decide exactly which of those you wish to include. For instance, one could imagine adding 1.1.0 support (so it doesn't bitrot), but still shipping with 1.0.2 for a deprecation cycle, and then switching to 1.1.0 with all the breakages that entails. That would let you drop some of the commits in this branch.
DSS1
In OpenSSL 1.0.2, the digest name "DSS1" was a DSA alias for SHA-1, part of the
EVP_Sign*
legacy. This has been removed in OpenSSL 1.1.0. In crypto: add compatibility logic for "DSS1" and "dss1", I've added a compatibility knob for it, but it may be better to skip this commit and instead deprecate the name, since there are other unavoidable removals anyway.Weak crypto
OpenSSL 1.1.0 disables a number of weak ciphers like RC4 or anonymous ciphers by default. You can build OpenSSL with
enable-weak-ssl-ciphers
if you wish to restore them. If you do, you probably want a deprecation cycle.OpenSSL 1.1.0 also rejects RSA keys smaller than 1024 bits by default and other settings. See https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_security_level.html
SHA-0
SHA-0 was removed in OpenSSL 1.1.0. This is an insecure and virtually unused early variant of SHA-1, but Node did nominally expose it via
crypto.createHash('sha')
. Short of adding a compatibility reimplementation of the hash, I think this is an unavoidable removal in OpenSSL 1.1.0.tlsSocket.getCipher().version
The
version
field returned bytlsSocket.getCipher()
did not do what was documented in 1.0.2 and always returned the string "TLSv1/SSLv3". I've retained that behavior in Node and fixed the documentation, but you all may wish to go through a deprecation cycle with that field. It's not useful anyway.See the commit crypto: hard-code tlsSocket.getCipher().version for details.
Error messages
Some of the errors OpenSSL emits are slightly differnent. I had to fix a few strings in tests. Dunno how much you care about this sort of behavior change.
ecdhCurve
The
ecdhCurve
server setting in the TLS module is defined to disable ECDH. This is a remnant of an implementation quirk in OpenSSL where it lacked curve negotiation logic. That meant there exists an ECDH-less state for OpenSSL servers separate from the cipher list. (Note no such mode exists on the client.) They fixed this and added this "ecdh_auto" opt-in knob in 1.0.2. In 1.1.0, this opt-in knob was made always on. Unfortunately, Node exposed this state by settingecdhCurve
to false.I've hacked it back in in the comment crypto: fix setting ecdhCurve to false with OpenSSL 1.1.0 by appending to the user's cipher list. Your call whether you wish to keep this. Since everything in TLS without ECDHE is insecure anyway, I would suggest taking a deprecation cycle to remove this instead and dropping the commit.
Ticket keys
OpenSSL 1.0.2 used a 16 byte key name, 16 byte AES-128-CBC key, and 16 byte HMAC-SHA256 key for session tickets. OpenSSL 1.1.0 switches this to a 16 byte key name, 32 byte AES-256-CBC key, and 32 byte HMAC-SHA256 key. One can query the size of the ticket key with SSL_CTX_get_tlsext_ticket_keys to avoid the hard-coded 48, but that doesn't really solve the problem. That ticket keys are 48 bytes are exposed in Node's public APIs.
In crypto: emulate OpenSSL 1.0.x ticket scheme in 1.1.x, I've implemented the callback to use the old scheme in OpenSSL 1.1.0. This works, but you may wish to think about alternate APIs (maybe expose a constant somewhere for the ticket key size) that allow the ticket scheme to change.
SSL_METHOD and protocol versions
OpenSSL's old SSL_METHODs are kind of a mess. You had methods like
TLSv1_1_client_method
which locked you to TLS 1.1 and only worked for clients. Then you had methods likeTLSv1_1_method
which were exactly the same asTLSv1_1_client_method
except you could use them for either clients or server. Then you hadSSLv23_method
which was actually version-flexible and misnamed. Then, to configure versions, you had toggles likeSSL_OP_NO_TLSv1_1
.This is a mess. In OpenSSL 1.1.0, things are much better.
SSLv23_method
is renamed toTLS_method
and then you have APIs likeSSL_CTX_set_min_proto_version
andSSL_CTX_set_max_proto_version
.TLS_client_method
andTLS_server_method
sadly still exist and are approximately as pointless as they always were. :-)Unfortunately, Node's APIs thoroughly expose this and has the user specify the string name of the method to use, rather than min/max version configurations. OpenSSL 1.1.0 retains the old APIs but marks them as deprecated, so this still works but you have a lot oc compile warnings. I have not attempted to remove the deprecated APIs in this branch as I think you all need a better API first.
My suggestion:
secureProtocol
option.minVersion
andmaxVersion
.secureProtocol
is completely removed, stop using the version-locked methods and tripping the deprecation warnings.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
crypto