classification
Title: ssl.create_default_context()
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: christian.heimes, gvanrossum, janssen, pitrou, python-dev
Priority: normal Keywords: patch

Created on 2013-11-22 03:22 by christian.heimes, last changed 2014-10-04 21:18 by pitrou. This issue is now closed.

Files
File name Uploaded Description Edit
ssl_create_default_context.patch christian.heimes, 2013-11-22 03:22 review
ssl_create_default_context2.patch christian.heimes, 2013-11-22 17:18 review
ssl_create_default_context3.patch christian.heimes, 2013-11-23 14:28
Messages (16)
msg203718 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 03:22
A few weeks ago I suggested the addition of ssl.create_default_context() to the stdlib. The patch implements my proposal. It replaces code in several of modules with one central function. The patch also removes ssl.wrap_socket() in favor for a SSLContext object.

As soon as #19292 gets accepted I'll add the additional keyword argument "purpose=None" to the arguments of ssl.create_default_context() in order to load the default system certs::
    
    if purpose is not None and verify_mode != CERT_NONE:
        context.load_default_certs(purpose)
msg203757 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 13:22
> A few weeks ago I suggested the addition of
> ssl.create_default_context() to the stdlib. The patch implements my
> proposal. It replaces code in several of modules with one central
> function. The patch also removes ssl.wrap_socket() in favor for a
> SSLContext object.

Can you call it create_default_client_context() (a bit long) and/or
stress that it's for client use?

Or will it be ok for server purposes too, i.e. do you promise that it'll
never get CERT_REQUIRED by default?
msg203758 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 14:12
Good point!

We need a purpose flag anyway in order to load the appropriate root CA certs. The purpose flag can be used for purpose-specific verify mode:

SERVER_AUTH = _ASN1Object('1.3.6.1.5.5.7.3.1')
CLIENT_AUTH = _ASN1Object('1.3.6.1.5.5.7.3.2')

    if isinstance(purpose, str):
        purpose = _ASN1Object.fromname(purpose)
    if verify_mode is None:
        if purpose == SERVER_AUTH:
            # authenticate a TLS web server (for client sockets). The default 
            # setting may change in the future.
            verify_mode = CERT_NONE
        elif purpose == CLIENT_AUTH:
            # authenticate a TLS web client (for server sockets). The default
            # setting is guaranteed to be stable and will never change.
            verify_mode = CERT_NONE
        else:
            # other (code signing, S/MIME, IPSEC, ...), default may change.
            verify_mode = CERT_NONE
    context.verify_mode = verify_mode
msg203761 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 14:19
> SERVER_AUTH = _ASN1Object('1.3.6.1.5.5.7.3.1')
> CLIENT_AUTH = _ASN1Object('1.3.6.1.5.5.7.3.2')

That's a bit ugly. How about an enum?
msg203765 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 14:27
In my opinion enums are for a closed batch of known entities. There are at least 20-30 purpose flags, maybe more. Everybody is allowed to define their own OIDs, too.
msg203768 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 14:33
> In my opinion enums are for a closed batch of known entities. There
> are at least 20-30 purpose flags, maybe more. Everybody is allowed to
> define their own OIDs, too.

Well, how many purposes are we going to expose? I don't think users
should know what ASN1 objects are, and a nice repr() is really useful.

(but there's no reason an enum cannot have 20 or 30 members)
msg203770 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 14:36
The objects already have a (more or less) nice representation:

>>> ssl._ASN1Object.fromname("1.3.6.1.5.5.7.3.1")
_ASN1Object(nid=129, shortname='serverAuth', longname='TLS Web Server Authentication', oid='1.3.6.1.5.5.7.3.1')
>>> ssl._ASN1Object.fromname("1.3.6.1.5.5.7.3.2")
_ASN1Object(nid=130, shortname='clientAuth', longname='TLS Web Client Authentication', oid='1.3.6.1.5.5.7.3.2')
>>> ssl._ASN1Object.fromname("1.3.6.1.5.5.7.3.8")
_ASN1Object(nid=133, shortname='timeStamping', longname='Time Stamping', oid='1.3.6.1.5.5.7.3.8')
msg203774 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 14:47
Ok. Note that as long as they aren't actually passed to OpenSSL, they don't need to be ASN1 objects at all, i.e. if it's only a parameter to create_default_context(), it can perfectly well be a str or enum.
msg203812 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 17:18
New patch with enum and more cleanups.

I'd like to explain the rationals for the purpose argument in create_default_context and the ASN1Object thing. There are multiple things involved here. First of all a certificate may have key usage and extended key usage OIDs in its X509v3 extensions. OpenSSL already checks them according to its mode.

The purpose is also required to load the correct set of certs from a  certificate provider (e.g. Windows cert store, Mozilla NSS certdata, Apple's keystore). The system or user can impose additional restrictions for certificates, e.g. disable a cert for TLS web server auth although the X.509 struct specifies 1.3.6.1.5.5.7.3.1 in its X509v3 extensions. NSS certdata also contains invalid certificates or certificates that are not suitable for server auth although the cert claims it.

In order to load only trusted certs for a purpose the API needs a purpose flag (usually an OID or a NID). Most Linux users have never seen this differentiation because /etc/ssl/certs/ either contains only server auth certs or their distributions screw up, See https://bugs.launchpad.net/ubuntu/+source/ca-certificates/+bug/1207004 or http://www.egenix.com/company/news/eGenix-pyOpenSSL-Distribution-0.13.2.1.0.1.5.html
msg203813 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 17:22
More links:

https://www.imperialviolet.org/2012/01/30/mozillaroots.html
https://github.com/bagder/curl/commit/51f0b798fa
https://github.com/kennethreitz/requests/issues/1659
msg203816 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 17:29
Ok, so I still have a couple of issues with the proposed API:

- if its purpose is to create a *default* context, create_default_context() shouldn't have that many arguments. The nice thing with contexts is that you can change their parameters later... So basically the function signature should be:

  create_default_context(purpose, *, cafile, cadata, capath)

Also, the default for "purpose" should probably be serverAuth.

- "PurposeEKU" is cryptic, please simply "Purpose" or "CertPurpose".
msg203842 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-22 19:24
Antoine and I have agreed upon a slightly different API. I'm going to split it up into one public API that creates a best practice context and one internal stdlib API to unify all places that deals with SSL sockets.

AP:
how about we use more strict and modern settings for the public API? TLSv1, no insecure stuff like RC4, MD5, DSS etc. https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
msg203844 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-22 19:40
> how about we use more strict and modern settings for the public API?
> TLSv1, no insecure stuff like RC4, MD5, DSS etc.
> https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/

Fine, but I'd like to see something more open-ended for the ciphers
string. e.g.
'HIGH:!ADH:!AECDH:!MD5:!DSS:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2' ?
msg204031 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-11-23 14:28
The patch implements HIGH:!aNULL:!RC4:!DSS

HIGH already covers !MD5:!EXPORT:!NULL:!SSLv2 and more
msg204039 - (view) Author: Roundup Robot (python-dev) Date: 2013-11-23 14:59
New changeset 63df21e74c65 by Christian Heimes in branch 'default':
Issue #19689: Add ssl.create_default_context() factory function. It creates
http://hg.python.org/cpython/rev/63df21e74c65
msg213010 - (view) Author: Roundup Robot (python-dev) Date: 2014-03-10 01:35
New changeset 8b4b6609cd31 by R David Murray in branch 'default':
whatsnew: ssl.create_default_context (#19689).
http://hg.python.org/cpython/rev/8b4b6609cd31
History
Date User Action Args
2014-10-04 21:18:44pitrousetstatus: open -> closed
2014-03-10 01:35:05python-devsetstatus: pending -> open

messages: + msg213010
2013-11-23 15:43:10christian.heimessetstatus: open -> pending
assignee: christian.heimes
resolution: fixed
stage: patch review -> resolved
2013-11-23 14:59:16python-devsetnosy: + python-dev
messages: + msg204039
2013-11-23 14:28:58christian.heimessetfiles: + ssl_create_default_context3.patch

messages: + msg204031
2013-11-22 19:40:57pitrousetmessages: + msg203844
2013-11-22 19:24:50christian.heimessetmessages: + msg203842
2013-11-22 19:10:39giampaolo.rodolasetnosy: - giampaolo.rodola
2013-11-22 17:29:54pitrousetmessages: + msg203816
2013-11-22 17:22:09christian.heimessetmessages: + msg203813
2013-11-22 17:18:25christian.heimessetfiles: + ssl_create_default_context2.patch

messages: + msg203812
2013-11-22 14:47:30pitrousetmessages: + msg203774
2013-11-22 14:36:14christian.heimessetmessages: + msg203770
2013-11-22 14:33:34pitrousetmessages: + msg203768
2013-11-22 14:27:54christian.heimessetmessages: + msg203765
2013-11-22 14:19:56pitrousetmessages: + msg203761
2013-11-22 14:12:56christian.heimessetmessages: + msg203758
2013-11-22 13:22:13pitrousetmessages: + msg203757
2013-11-22 03:22:24christian.heimescreate