Skip to content

Conversation

@tiran
Copy link
Member

@tiran tiran commented Sep 8, 2017

Replace Python's custom ssl.match_hostname() with OpenSSL 1.0.2 APIs. The hostname or IP address are now verified by OpenSSL during the TLS handshake. A failure to verify the name results in a proper handshake abort with TLS Alert bad cert.

Signed-off-by: Christian Heimes [email protected]

https://bugs.python.org/issue31399

@tiran tiran force-pushed the openssl_check_hostname branch from a3fa2cf to 75d33a3 Compare January 11, 2018 20:51
@tiran tiran requested a review from a team as a code owner January 11, 2018 20:51
@tiran tiran force-pushed the openssl_check_hostname branch from 75d33a3 to 35176ba Compare January 11, 2018 21:14
@tiran tiran requested review from 1st1 and asvetlov as code owners January 11, 2018 21:14
@tiran tiran force-pushed the openssl_check_hostname branch from 35176ba to 65faab4 Compare January 11, 2018 21:18
Copy link
Member

@alex alex left a comment

Choose a reason for hiding this comment

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

Mostly looks good, from a first skim

Modules/_ssl.c Outdated
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand this comment

Copy link
Member Author

@tiran tiran Jan 11, 2018

Choose a reason for hiding this comment

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

OpenSSL has no API to get hostflags from X509_VERIFY_PARAM. We have to maintain our own copy. I'll clarify the comment in my next push.

Copy link
Member

Choose a reason for hiding this comment

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

Ahhh. Now I understand. I think "OpenSSL does not have an API for getting the hostflags from an X509_VERIFY_PARAM" would be clearer.

Also is there a bug/PR on OpenSSL for this we can link?

Modules/_ssl.c Outdated
Copy link
Member

Choose a reason for hiding this comment

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

Do we need X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS as well?

@tiran
Copy link
Member Author

tiran commented Jan 11, 2018

Thanks @alex

I have updated the comment for hostflaggs and created openssl/openssl#5061.

@tiran tiran force-pushed the openssl_check_hostname branch 6 times, most recently from e499eca to 89a28a5 Compare January 12, 2018 14:54
@tiran
Copy link
Member Author

tiran commented Jan 16, 2018

Blocked by #5180

@asvetlov @1st1 The PR touches asyncio. Please review the test changes.

@tiran tiran changed the title bpo-31399: [WIP] Let OpenSSL verify hostname and IP address bpo-31399: Let OpenSSL verify hostname and IP address Jan 16, 2018
Copy link
Contributor

@asvetlov asvetlov left a comment

Choose a reason for hiding this comment

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

asyncio test change is correct (but please update assertion as suggested)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please use self.assertIsNone(proto.transport)

@tiran tiran force-pushed the openssl_check_hostname branch 2 times, most recently from c400837 to d1dd4f3 Compare January 16, 2018 21:09
@tiran
Copy link
Member Author

tiran commented Jan 16, 2018

@asvetlov Thanks, I changed the assert as requested

I also forgot to remove the call to match_hostname. How should I handle the call in asyncio? If I remove it, the code will no worker be secure on 3.6.

@1st1
Copy link
Member

1st1 commented Jan 16, 2018

I also forgot to remove the call to match_hostname. How should I handle the call in asyncio? If I remove it, the code will no worker be secure on 3.6.

This change is for 3.7 only, right? If so just remove the call in the master branch. We no longer try to use asyncio CPython code with other CPython versions.

@tiran tiran force-pushed the openssl_check_hostname branch from d1dd4f3 to d07134d Compare January 17, 2018 10:20
@tiran
Copy link
Member Author

tiran commented Jan 17, 2018

Yes, it's 3.7 only.

@tiran tiran force-pushed the openssl_check_hostname branch from d07134d to 7b5edd5 Compare January 20, 2018 09:43
@tiran tiran requested a review from a team as a code owner January 20, 2018 09:43
@tiran
Copy link
Member Author

tiran commented Jan 20, 2018

This branch is now based on PR #5242. Please review and ACK the other PR first.

@tiran tiran force-pushed the openssl_check_hostname branch 3 times, most recently from 7c4fc70 to 197d51f Compare January 20, 2018 12:25
@asvetlov
Copy link
Contributor

Are there stoppers for the issue left?

@tiran
Copy link
Member Author

tiran commented Jan 20, 2018

@asvetlov No stoppers, I'm just waiting for a final review. The PR has changed a bit since @alex reviewed it. I have added more documentation and additional checks for LibreSSL.

@asvetlov
Copy link
Contributor

asvetlov commented Jan 20, 2018

Good to know.
Just for note -- LGTM.

Copy link
Member

@alex alex left a comment

Choose a reason for hiding this comment

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

Code looks fine, I have some API design concerns though.

Copy link
Member

Choose a reason for hiding this comment

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

"no longer used by TLS connections" or something?

Copy link
Member

Choose a reason for hiding this comment

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

Do all of these need to be a public API?

I don't see a need to support things like partial wildcards or multi-label wildcards.

The previous implementation didn't support them, and I don't think expanding the public API like this is a good idea.

Copy link
Member Author

Choose a reason for hiding this comment

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

Partial wildcard matching is disabled by default. I have to expose the flag value so the getter can report the flag correctly:

>>> import ssl
>>> ssl.create_default_context().host_flags
<HostFlags.HOSTFLAG_NO_PARTIAL_WILDCARDS: 4>

I also like to add NEVER_CHECK_SUBJECT for urllib3, too.

I don't care about the other flags. I'm happy to remove them.

Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be a public API? I feel like we can set reasonable defaults that are applicable to everything.

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 exposed the flag primary for urllib3/urllib3#497. urllib3 wants to disable CN support and require certificates with SAN fields.

Copy link
Member

Choose a reason for hiding this comment

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

What if we just expose a single check_subject_hostname or something like that, instead of all these flags?

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'm ok with that, but I'm not 100% happy with the name.

  • check_subject_cn
  • check_common_name
  • check_subject_common_name
  • use_common_name
  • check_only_san
  • ...

I'm going to rename host_flags to _host_flags, keep all flags in _ssl and implement the flag in pure Python. That way we don't expose the API, but still have it available to implement other flags.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good -- my desire is for us not to commit to a really broad API when we really want a tiny thing.

hostname_checks_common_name perhaps?

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's explicit, almost German. I like it :)

The feature is only available with OpenSSL 1.1.0+. How should I handle OpenSSL 1.0.2? Don't define the property at all or only implement readonly property + ssl.HAS_NEVER_CHECK_HOSTNAME?

Copy link
Member

Choose a reason for hiding this comment

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

readonly + a constant makes sense to me

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

I'm currently at a conference and will take care of proper documentation next week.

Copy link
Member

Choose a reason for hiding this comment

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

Not sure this comment is needed here, since it's not acually referring to any code which exists

@tiran tiran force-pushed the openssl_check_hostname branch 2 times, most recently from 2199b99 to e819fbd Compare January 23, 2018 11:17
tiran added 7 commits January 26, 2018 15:27
The ssl module now uses OpenSSL's X509_VERIFY_PARAM_set1_host() and
X509_VERIFY_PARAM_set1_ip() API to verify hostname and IP addresses.

Signed-off-by: Christian Heimes <[email protected]>
Signed-off-by: Christian Heimes <[email protected]>
Signed-off-by: Christian Heimes <[email protected]>
libssl must provide X509_VERIFY_PARAM_set1_host()

Signed-off-by: Christian Heimes <[email protected]>
Remove all hostflags except for NO_PARTIAL_WILDCARDS and
NEVER_CHECK_SUBJECT. The other flags aren't that useful at the moment.

Don't support OpenSSL special mode with a leading dot, e.g.
".example.org" matches "www.example.org". It's not standard conform.

Signed-off-by: Christian Heimes <[email protected]>
Host flags are now in internal API. Public API is a new attribute
hostname_checks_common_name.

Signed-off-by: Christian Heimes <[email protected]>
@tiran tiran force-pushed the openssl_check_hostname branch from e819fbd to 845c149 Compare January 26, 2018 15:35
@tiran tiran merged commit 61d478c into python:master Jan 27, 2018
@bedevere-bot
Copy link

@tiran: Please replace # with GH- in the commit message next time. Thanks!

@tiran tiran deleted the openssl_check_hostname branch January 27, 2018 14:51
@asottile
Copy link
Contributor

Running into this patch on travis-ci as ubuntu trusty ships with openssl 1.0.1 -- this patch causes python3.7 to build without _ssl support on trusty. See also:

I agree that the patch is the right move going forward 👍, figured I'd post this here for visibility though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants