add support for ALPN (closes #20188)

This commit is contained in:
Benjamin Peterson 2015-01-23 16:35:37 -05:00
parent 06140f2e04
commit cca2732a82
5 changed files with 231 additions and 29 deletions

View file

@ -109,6 +109,11 @@ struct py_ssl_library_code {
# define HAVE_SNI 0
#endif
/* ALPN added in OpenSSL 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT)
# define HAVE_ALPN
#endif
enum py_ssl_error {
/* these mirror ssl.h */
PY_SSL_ERROR_NONE,
@ -180,9 +185,13 @@ typedef struct {
PyObject_HEAD
SSL_CTX *ctx;
#ifdef OPENSSL_NPN_NEGOTIATED
char *npn_protocols;
unsigned char *npn_protocols;
int npn_protocols_len;
#endif
#ifdef HAVE_ALPN
unsigned char *alpn_protocols;
int alpn_protocols_len;
#endif
#ifndef OPENSSL_NO_TLSEXT
PyObject *set_hostname;
#endif
@ -1460,7 +1469,20 @@ static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) {
if (out == NULL)
Py_RETURN_NONE;
return PyUnicode_FromStringAndSize((char *) out, outlen);
return PyUnicode_FromStringAndSize((char *)out, outlen);
}
#endif
#ifdef HAVE_ALPN
static PyObject *PySSL_selected_alpn_protocol(PySSLSocket *self) {
const unsigned char *out;
unsigned int outlen;
SSL_get0_alpn_selected(self->ssl, &out, &outlen);
if (out == NULL)
Py_RETURN_NONE;
return PyUnicode_FromStringAndSize((char *)out, outlen);
}
#endif
@ -2053,6 +2075,9 @@ static PyMethodDef PySSLMethods[] = {
{"version", (PyCFunction)PySSL_version, METH_NOARGS},
#ifdef OPENSSL_NPN_NEGOTIATED
{"selected_npn_protocol", (PyCFunction)PySSL_selected_npn_protocol, METH_NOARGS},
#endif
#ifdef HAVE_ALPN
{"selected_alpn_protocol", (PyCFunction)PySSL_selected_alpn_protocol, METH_NOARGS},
#endif
{"compression", (PyCFunction)PySSL_compression, METH_NOARGS},
{"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS,
@ -2159,6 +2184,9 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
#ifdef OPENSSL_NPN_NEGOTIATED
self->npn_protocols = NULL;
#endif
#ifdef HAVE_ALPN
self->alpn_protocols = NULL;
#endif
#ifndef OPENSSL_NO_TLSEXT
self->set_hostname = NULL;
#endif
@ -2218,7 +2246,10 @@ context_dealloc(PySSLContext *self)
context_clear(self);
SSL_CTX_free(self->ctx);
#ifdef OPENSSL_NPN_NEGOTIATED
PyMem_Free(self->npn_protocols);
PyMem_FREE(self->npn_protocols);
#endif
#ifdef HAVE_ALPN
PyMem_FREE(self->alpn_protocols);
#endif
Py_TYPE(self)->tp_free(self);
}
@ -2244,6 +2275,23 @@ set_ciphers(PySSLContext *self, PyObject *args)
Py_RETURN_NONE;
}
static int
do_protocol_selection(unsigned char **out, unsigned char *outlen,
const unsigned char *remote_protocols, unsigned int remote_protocols_len,
unsigned char *our_protocols, unsigned int our_protocols_len)
{
if (our_protocols == NULL) {
our_protocols = (unsigned char*)"";
our_protocols_len = 0;
}
SSL_select_next_proto(out, outlen,
remote_protocols, remote_protocols_len,
our_protocols, our_protocols_len);
return SSL_TLSEXT_ERR_OK;
}
#ifdef OPENSSL_NPN_NEGOTIATED
/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */
static int
@ -2254,10 +2302,10 @@ _advertiseNPN_cb(SSL *s,
PySSLContext *ssl_ctx = (PySSLContext *) args;
if (ssl_ctx->npn_protocols == NULL) {
*data = (unsigned char *) "";
*data = (unsigned char *)"";
*len = 0;
} else {
*data = (unsigned char *) ssl_ctx->npn_protocols;
*data = ssl_ctx->npn_protocols;
*len = ssl_ctx->npn_protocols_len;
}
@ -2270,23 +2318,9 @@ _selectNPN_cb(SSL *s,
const unsigned char *server, unsigned int server_len,
void *args)
{
PySSLContext *ssl_ctx = (PySSLContext *) args;
unsigned char *client = (unsigned char *) ssl_ctx->npn_protocols;
int client_len;
if (client == NULL) {
client = (unsigned char *) "";
client_len = 0;
} else {
client_len = ssl_ctx->npn_protocols_len;
}
SSL_select_next_proto(out, outlen,
server, server_len,
client, client_len);
return SSL_TLSEXT_ERR_OK;
PySSLContext *ctx = (PySSLContext *)args;
return do_protocol_selection(out, outlen, server, server_len,
ctx->npn_protocols, ctx->npn_protocols_len);
}
#endif
@ -2329,6 +2363,50 @@ _set_npn_protocols(PySSLContext *self, PyObject *args)
#endif
}
#ifdef HAVE_ALPN
static int
_selectALPN_cb(SSL *s,
const unsigned char **out, unsigned char *outlen,
const unsigned char *client_protocols, unsigned int client_protocols_len,
void *args)
{
PySSLContext *ctx = (PySSLContext *)args;
return do_protocol_selection((unsigned char **)out, outlen,
client_protocols, client_protocols_len,
ctx->alpn_protocols, ctx->alpn_protocols_len);
}
#endif
static PyObject *
_set_alpn_protocols(PySSLContext *self, PyObject *args)
{
#ifdef HAVE_ALPN
Py_buffer protos;
if (!PyArg_ParseTuple(args, "y*:set_npn_protocols", &protos))
return NULL;
PyMem_FREE(self->alpn_protocols);
self->alpn_protocols = PyMem_Malloc(protos.len);
if (!self->alpn_protocols)
return PyErr_NoMemory();
memcpy(self->alpn_protocols, protos.buf, protos.len);
self->alpn_protocols_len = protos.len;
PyBuffer_Release(&protos);
if (SSL_CTX_set_alpn_protos(self->ctx, self->alpn_protocols, self->alpn_protocols_len))
return PyErr_NoMemory();
SSL_CTX_set_alpn_select_cb(self->ctx, _selectALPN_cb, self);
PyBuffer_Release(&protos);
Py_RETURN_NONE;
#else
PyErr_SetString(PyExc_NotImplementedError,
"The ALPN extension requires OpenSSL 1.0.2 or later.");
return NULL;
#endif
}
static PyObject *
get_verify_mode(PySSLContext *self, void *c)
{
@ -3307,6 +3385,8 @@ static struct PyMethodDef context_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"set_ciphers", (PyCFunction) set_ciphers,
METH_VARARGS, NULL},
{"_set_alpn_protocols", (PyCFunction) _set_alpn_protocols,
METH_VARARGS, NULL},
{"_set_npn_protocols", (PyCFunction) _set_npn_protocols,
METH_VARARGS, NULL},
{"load_cert_chain", (PyCFunction) load_cert_chain,
@ -4502,6 +4582,14 @@ PyInit__ssl(void)
Py_INCREF(r);
PyModule_AddObject(m, "HAS_NPN", r);
#ifdef HAVE_ALPN
r = Py_True;
#else
r = Py_False;
#endif
Py_INCREF(r);
PyModule_AddObject(m, "HAS_ALPN", r);
/* Mappings for error codes */
err_codes_to_names = PyDict_New();
err_names_to_codes = PyDict_New();