Skip to content
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

retire TLS1.0/1.1 ciphers from default list #40746

Merged
merged 9 commits into from Aug 17, 2020
Merged

Conversation

wfurt
Copy link
Member

@wfurt wfurt commented Aug 13, 2020

This is follow-up on #39633 and #40303. Product change is update to DOTNET_DEFAULT_CIPHERSTRING to have it the way how @bartonjs originally wanted. As #40303 fixed most tests, this should just work now.

Bulk of this change is update to PlatformDetection.SupportsTlsXX. In the past that would be based on OS version but since this impacts wide ranges of distributions, the static mapping is difficult to maintain and it created fragility for developers as their systems may be different.

So instead of static map, This change updated detection of Tls 1.0/1.1 to actually try to do handshake using our PAL CryptoNative_SslCtxCreate(). I originally wanted to do that using Interop but that is heavily intertwined with SslStream and build and dependencies were messy so I eventually give up. Also the platform detection is used in may places and build is special so I did not want to pull inn dependency on SslStream.

Instead, I added one function to OpenSSL PAL. That has minimal code to generate certificate on the flaw and test handshake using memory BIO. I had to pull in few more functions but the outcome is self contained and the decision only depends on OpenSSL it self. I may eventually updated other parts as well but for now I'm happy it coverts the two "problematic" version.

contributes to #30767

@wfurt wfurt added area-System.Net.Security os-linux Linux OS (any supported distro) labels Aug 13, 2020
@wfurt wfurt requested review from bartonjs and a team August 13, 2020 01:55
@wfurt wfurt self-assigned this Aug 13, 2020
@ghost
Copy link

ghost commented Aug 13, 2020

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

@geoffkizer geoffkizer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only test changes right?

A few comments above but generally LGTM. I don't grok all the OpenSsl stuff, so you may want to get a review from someone who understands that better.

@wfurt wfurt added the breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. label Aug 13, 2020
@wfurt
Copy link
Member Author

wfurt commented Aug 13, 2020

This is only test changes right?

no. DOTNET_DEFAULT_CIPHERSTRING impacts the product. With that in place, SslStream may or may not be able to negotiate older TLS versions. (depending on openssl configuration) @bartonjs can probably explain better.

@geoffkizer
Copy link
Contributor

Ok, seems like @bartonjs should review this.

return ret;
}

int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of native code to add for a test probe, especially with needing to add creating a self-signed cert. If it's just for tests can we detect the state somehow else? Maybe move the cert generation to managed code and leave this as passing in a cert and a protocol? (Then it's easier to expand to test for the combinatorics of {RSA,DSA,ECDSA} x { TLS protocol versions })

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is where I started. But the PlatfdormDetection build did not work for me the way other tests projects work and I was getting unresolved references. I can try to ping some build experts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I'd prefer to see this as

Suggested change
int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol)
int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol, X509* cert, EVP_PKEY* key)

and the cert generation moved to managed code (and then no longer need the X509 mutation functions), if this is what we have to do based on the calendar, it's OK for a test probe.

@wfurt
Copy link
Member Author

wfurt commented Aug 13, 2020

#39633 claimed:

  • Anything provided via the CipherSuitePolicy, otherwise
  • Anything provided via config (OPENSSL_CONF or default, OpenSSL 1.1+ only)
  • An opinionated default

There is problem with OpenSSL 1.0 as CipherSuitePolicy throws PNSP and config is not available.
So user would be stuck unconditionally with "opinionated default".
That seems like support pain as older systems (more likely needing legacy behavior) would be stuck on TLS12 only.
It seems like we should not enforce CipherSuitePolicy if there is no override.

@@ -42,7 +43,7 @@ static int32_t g_config_specified_ciphersuites = 0;

static void DetectCiphersuiteConfiguration()
{
// This routine will always produce g_config_specified_ciphersuites = 0 on OpenSSL 1.0.x,
// This routine will always produce g_config_specified_ciphersuites = 1 on OpenSSL 1.0.x,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a non-portable build is built against OpenSSL 1.0.x it'll still return 0 with the current code.

if (API_EXISTS(SSL_state))
{
// OpenSSL 1.0 does not support CipherSuites so there is no way for caller to override default
g_config_specified_ciphersuites = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intent is really "OpenSSL 1.0.x should use the OpenSSL default list" (assuming @blowdart said OK), this check could be much earlier and shortcut a lot of work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the percentage of platforms stuck on OpenSSL 1.0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any numbers. AFAIK That covers Ubuntu 16 and some Cernots and RH builds.

cc: @tmds @omajid

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RHEL.7 (EOL Aug 2021), CentOS.7 (mainstream support ended, extended ends 2024), Ubuntu 16.04 (EOL April 2021).

Probably anything that hit RTM before ~Mar 2018; which might be just those three.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and the Fedora/RHEL builds will already hit a special case where they have improved the built-in defaults. So we might only really be talking Ubuntu 16.04. Debian 9 (Stretch) has already received its last update, and is now only in extended support.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RHEL.7 (EOL Aug 2021), CentOS.7 (mainstream support ended, extended ends 2024), Ubuntu 16.04 (EOL April 2021).

CentOS 7 is based on the RHEL 7 lifecycle. RHEL 7's EOL would be Aug 2024 (or later).

Comment on lines 55 to 60
if (x509->cert_info->validity->notBefore)
{
ASN1_TIME_free(x509->cert_info->validity->notBefore);
}

x509->cert_info->validity->notBefore = ASN1_STRING_dup(time);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be wrapped in if (x509->cert_info->validity->notBefore != time), both for side effect observability reasons, and to avoid X509_set1_notBefore(cert, X509_get0_notBefore(cert)) from segfaulting.

Comment on lines 71 to 76
if (x509->cert_info->validity->notAfter)
{
ASN1_TIME_free(x509->cert_info->validity->notAfter);
}

x509->cert_info->validity->notAfter = ASN1_STRING_dup(time);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly should be guarded with "if time isn't the current value"

@@ -19,7 +19,7 @@

#define CRYPTO_LOCK_X509 3
#define CRYPTO_LOCK_EVP_PKEY 10

#define CRYPTO_LOCK_BIO 21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define CRYPTO_LOCK_BIO 21
#define CRYPTO_LOCK_BIO 21

Please leave the separation between the crypto lock IDs and the SSL_CTRL IDs.

{
// OpenSSL 1.0 does not support CipherSuites so there is no way for caller to override default
g_config_specified_ciphersuites = 1;
}
}
else
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't put a comment far enough down, but if the intent is to just use the builtin default on OpenSSL 1.0, replace the complex #elif !defined(FEATURE_DISTRO_AGNOSTIC_SSL) && defined(SSL_SYSTEM_DEFAULT_CIPHER_LIST) with just #else

Comment on lines 966 to 967
#define X509_new X509_new_ptr
#define X509_NAME_get_index_by_NID X509_NAME_get_index_by_NID_ptr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define X509_new X509_new_ptr
#define X509_NAME_get_index_by_NID X509_NAME_get_index_by_NID_ptr
#define X509_NAME_get_index_by_NID X509_NAME_get_index_by_NID_ptr
#define X509_new X509_new_ptr

Comment on lines 970 to 971
#define X509_subject_name_hash X509_subject_name_hash_ptr
#define X509_set_pubkey X509_set_pubkey_ptr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define X509_subject_name_hash X509_subject_name_hash_ptr
#define X509_set_pubkey X509_set_pubkey_ptr
#define X509_set_pubkey X509_set_pubkey_ptr
#define X509_subject_name_hash X509_subject_name_hash_ptr

@danmoseley
Copy link
Member

I see this is tagged breaking change -- we don't have much preview time left for feedback to get an idea of the level of impact. Should we have a way for customers to force the old behavior, an AppContext?

if (!IsOpenSslSupported)
{
// on Windows and macOS TLS1.0/1.1 are supported.
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment doesn't make sense with the return. Perhaps this if should just be Debug.Assert(IsOpenSslSupported);?

@bartonjs
Copy link
Member

Should we have a way for customers to force the old behavior, an AppContext?

They can set it in OpenSSL's config. AppContext won't work because the place that needs to read it is native code.

@bartonjs
Copy link
Member

Some nitpicks, but LGTM once all 1.0-based executions agree on which default list to use (and Barry has OK'd/asserted the choice)

@wfurt
Copy link
Member Author

wfurt commented Aug 14, 2020

thanks @bartonjs. I'm still looking into doing the cert in managed code. If I don't get that working today, I'll proceed with the change (assuming @blowdart is OK). I know it is large C chunk but if I find better way it can still go to 5 later as safe test-only change or it can stay in master.

@blowdart
Copy link
Contributor

I would say this needs to be accompanied by very clear documentation on the docs site on how to reenable them if you need to, but otherwise, have fun :)

@wfurt
Copy link
Member Author

wfurt commented Aug 14, 2020

I don't understand your comment @blowdart. The problem is that with old (1.0) OpenSSL you cannot override the default to the proposal and current change is to level it as it was before on old OSes. With 1.1, we will strip pout TLS1.0/1.1 ciphers and proceeded with opinionated default. And yes, we should document and verify that override actually works.

@blowdart
Copy link
Contributor

I mean document how, if for some ungodly reason, someone needs TLS1.1, document how they can turn it add it back. You're not stripping entirely just setting the default selection because linux & openssl can't be trusted to be sensible :),

@danmoseley danmoseley added the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Aug 14, 2020
@danmoseley
Copy link
Member

it seems this broke powershell ? (mentioned in PowerShell/PowerShell#13643) which reinforces that we should have a breaking change note published.

@bartonjs
Copy link
Member

@wfurt I wonder if both of us were silently thinking the other was going to write the documentation for this. If you haven't already started, I'll find some time today or tomorrow to do it.

@wfurt
Copy link
Member Author

wfurt commented Sep 24, 2020

that would be great @bartonjs. It was in my TODO list but I got distracted with some other activities ;(

@@ -1048,13 +1074,16 @@ FOR_ALL_OPENSSL_FUNCTIONS
#define SSL_is_init_finished local_SSL_is_init_finished
#define X509_CRL_get0_nextUpdate local_X509_CRL_get0_nextUpdate
#define X509_NAME_get0_der local_X509_NAME_get0_der
#define X509_new local_X509_new
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a leftover and should be removed, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a PR for this: #43039

@bartonjs bartonjs removed the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Oct 6, 2020
@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 7, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. os-linux Linux OS (any supported distro)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants