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

[3.7] bpo-30622: Improve NPN support detection (GH-5859) #5860

Merged
merged 1 commit into from Feb 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions Doc/library/ssl.rst
Expand Up @@ -2434,6 +2434,23 @@ successful call of :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or
:func:`~ssl.RAND_pseudo_bytes` is sufficient.


.. ssl-libressl:

LibreSSL support
----------------

LibreSSL is a fork of OpenSSL 1.0.1. The ssl module has limited support for
LibreSSL. Some features are not available when the ssl module is compiled
with LibreSSL.

* LibreSSL >= 2.6.1 no longer supports NPN. The methods
:meth:`SSLContext.set_npn_protocols` and
:meth:`SSLSocket.selected_npn_protocol` are not available.
* :meth:`SSLContext.set_default_verify_paths` ignores the env vars
:envvar:`SSL_CERT_FILE` and :envvar:`SSL_CERT_PATH` although
:func:`get_default_verify_paths` still reports them.


.. seealso::

Class :class:`socket.socket`
Expand Down
@@ -0,0 +1 @@
The ssl module now detects missing NPN support in LibreSSL.
29 changes: 21 additions & 8 deletions Modules/_ssl.c
Expand Up @@ -160,6 +160,19 @@ static void _PySSLFixErrno(void) {
# define HAVE_ALPN
#endif

/* We cannot rely on OPENSSL_NO_NEXTPROTONEG because LibreSSL 2.6.1 dropped
* NPN support but did not set OPENSSL_NO_NEXTPROTONEG for compatibility
* reasons. The check for TLSEXT_TYPE_next_proto_neg works with
* OpenSSL 1.0.1+ and LibreSSL.
*/
#ifdef OPENSSL_NO_NEXTPROTONEG
# define HAVE_NPN 0
#elif defined(TLSEXT_TYPE_next_proto_neg)
# define HAVE_NPN 1
#else
# define HAVE_NPN 0
# endif

#ifndef INVALID_SOCKET /* MS defines this */
#define INVALID_SOCKET (-1)
#endif
Expand Down Expand Up @@ -328,7 +341,7 @@ static unsigned int _ssl_locks_count = 0;
typedef struct {
PyObject_HEAD
SSL_CTX *ctx;
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
unsigned char *npn_protocols;
int npn_protocols_len;
#endif
Expand Down Expand Up @@ -1909,7 +1922,7 @@ _ssl__SSLSocket_version_impl(PySSLSocket *self)
return PyUnicode_FromString(version);
}

#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
/*[clinic input]
_ssl._SSLSocket.selected_npn_protocol
[clinic start generated code]*/
Expand Down Expand Up @@ -2874,7 +2887,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
self->ctx = ctx;
self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
self->protocol = proto_version;
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
self->npn_protocols = NULL;
#endif
#ifdef HAVE_ALPN
Expand Down Expand Up @@ -3013,7 +3026,7 @@ context_dealloc(PySSLContext *self)
PyObject_GC_UnTrack(self);
context_clear(self);
SSL_CTX_free(self->ctx);
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
PyMem_FREE(self->npn_protocols);
#endif
#ifdef HAVE_ALPN
Expand Down Expand Up @@ -3091,7 +3104,7 @@ _ssl__SSLContext_get_ciphers_impl(PySSLContext *self)
#endif


#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG) || defined(HAVE_ALPN)
#if defined(HAVE_NPN) || defined(HAVE_ALPN)
static int
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
const unsigned char *server_protocols, unsigned int server_protocols_len,
Expand All @@ -3117,7 +3130,7 @@ do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
}
#endif

#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */
static int
_advertiseNPN_cb(SSL *s,
Expand Down Expand Up @@ -3160,7 +3173,7 @@ _ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self,
Py_buffer *protos)
/*[clinic end generated code: output=72b002c3324390c6 input=319fcb66abf95bd7]*/
{
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
PyMem_Free(self->npn_protocols);
self->npn_protocols = PyMem_Malloc(protos->len);
if (self->npn_protocols == NULL)
Expand Down Expand Up @@ -5705,7 +5718,7 @@ PyInit__ssl(void)
Py_INCREF(r);
PyModule_AddObject(m, "HAS_ECDH", r);

#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
#ifdef HAVE_NPN
r = Py_True;
#else
r = Py_False;
Expand Down
6 changes: 3 additions & 3 deletions Modules/clinic/_ssl.c.h
Expand Up @@ -132,7 +132,7 @@ _ssl__SSLSocket_version(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
return _ssl__SSLSocket_version_impl(self);
}

#if (defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG))
#if defined(HAVE_NPN)

PyDoc_STRVAR(_ssl__SSLSocket_selected_npn_protocol__doc__,
"selected_npn_protocol($self, /)\n"
Expand All @@ -151,7 +151,7 @@ _ssl__SSLSocket_selected_npn_protocol(PySSLSocket *self, PyObject *Py_UNUSED(ign
return _ssl__SSLSocket_selected_npn_protocol_impl(self);
}

#endif /* (defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)) */
#endif /* defined(HAVE_NPN) */

#if defined(HAVE_ALPN)

Expand Down Expand Up @@ -1175,4 +1175,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje
#ifndef _SSL_ENUM_CRLS_METHODDEF
#define _SSL_ENUM_CRLS_METHODDEF
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
/*[clinic end generated code: output=d987411caeb106e7 input=a9049054013a1b77]*/
/*[clinic end generated code: output=a00fef6a470cfc2c input=a9049054013a1b77]*/