diff options
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r-- | Modules/_ssl.c | 1794 |
1 files changed, 1185 insertions, 609 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index c27073c..7a9e066 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -64,6 +64,7 @@ static PySocketModule_APIObject PySocketModule; #include "openssl/ssl.h" #include "openssl/err.h" #include "openssl/rand.h" +#include "openssl/bio.h" /* SSL error object */ static PyObject *PySSLErrorObject; @@ -108,6 +109,11 @@ struct py_ssl_library_code { # define HAVE_SNI 0 #endif +/* ALPN added in OpenSSL 1.0.2 */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT) +# define HAVE_ALPN +#endif + enum py_ssl_error { /* these mirror ssl.h */ PY_SSL_ERROR_NONE, @@ -161,13 +167,6 @@ static unsigned int _ssl_locks_count = 0; #define X509_NAME_MAXLEN 256 -/* RAND_* APIs got added to OpenSSL in 0.9.5 */ -#if OPENSSL_VERSION_NUMBER >= 0x0090500fL -# define HAVE_OPENSSL_RAND 1 -#else -# undef HAVE_OPENSSL_RAND -#endif - /* SSL_CTX_clear_options() and SSL_clear_options() were first added in * OpenSSL 0.9.8m but do not appear in some 0.9.9-dev versions such the * 0.9.9 from "May 2008" that NetBSD 5.0 uses. */ @@ -181,36 +180,18 @@ static unsigned int _ssl_locks_count = 0; * older SSL, but let's be safe */ #define PySSL_CB_MAXLEN 128 -/* SSL_get_finished got added to OpenSSL in 0.9.5 */ -#if OPENSSL_VERSION_NUMBER >= 0x0090500fL -# define HAVE_OPENSSL_FINISHED 1 -#else -# define HAVE_OPENSSL_FINISHED 0 -#endif - -/* ECDH support got added to OpenSSL in 0.9.8 */ -#if OPENSSL_VERSION_NUMBER < 0x0090800fL && !defined(OPENSSL_NO_ECDH) -# define OPENSSL_NO_ECDH -#endif - -/* compression support got added to OpenSSL in 0.9.8 */ -#if OPENSSL_VERSION_NUMBER < 0x0090800fL && !defined(OPENSSL_NO_COMP) -# define OPENSSL_NO_COMP -#endif - -/* X509_VERIFY_PARAM got added to OpenSSL in 0.9.8 */ -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL -# define HAVE_OPENSSL_VERIFY_PARAM -#endif - 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 @@ -226,20 +207,35 @@ typedef struct { char shutdown_seen_zero; char handshake_done; enum py_ssl_server_or_client socket_type; + PyObject *owner; /* Python level "owner" passed to servername callback */ + PyObject *server_hostname; } PySSLSocket; +typedef struct { + PyObject_HEAD + BIO *bio; + int eof_written; +} PySSLMemoryBIO; + static PyTypeObject PySSLContext_Type; static PyTypeObject PySSLSocket_Type; +static PyTypeObject PySSLMemoryBIO_Type; + +/*[clinic input] +module _ssl +class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type" +class _ssl._SSLSocket "PySSLSocket *" "&PySSLSocket_Type" +class _ssl.MemoryBIO "PySSLMemoryBIO *" "&PySSLMemoryBIO_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7bf7cb832638e2e1]*/ -static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args); -static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args); -static int check_socket_and_wait_for_timeout(PySocketSockObject *s, - int writing); -static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args); -static PyObject *PySSL_cipher(PySSLSocket *self); +#include "clinic/_ssl.c.h" + +static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout); #define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type) #define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type) +#define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type) typedef enum { SOCKET_IS_NONBLOCKING, @@ -251,11 +247,16 @@ typedef enum { } timeout_state; /* Wrap error strings with filename and line # */ -#define STRINGIFY1(x) #x -#define STRINGIFY2(x) STRINGIFY1(x) #define ERRSTR1(x,y,z) (x ":" y ": " z) -#define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x) +#define ERRSTR(x) ERRSTR1("_ssl.c", Py_STRINGIFY(__LINE__), x) + +/* Get the socket from a PySSLSocket, if it has one */ +#define GET_SOCKET(obj) ((obj)->Socket ? \ + (PySocketSockObject *) PyWeakref_GetObject((obj)->Socket) : NULL) +/* If sock is NULL, use a timeout of 0 second */ +#define GET_SOCKET_TIMEOUT(sock) \ + ((sock != NULL) ? (sock)->sock_timeout : 0) /* * SSL errors. @@ -419,13 +420,12 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno) case SSL_ERROR_SYSCALL: { if (e == 0) { - PySocketSockObject *s - = (PySocketSockObject *) PyWeakref_GetObject(obj->Socket); + PySocketSockObject *s = GET_SOCKET(obj); if (ret == 0 || (((PyObject *)s) == Py_None)) { p = PY_SSL_ERROR_EOF; type = PySSLEOFErrorObject; errstr = "EOF occurred in violation of protocol"; - } else if (ret == -1) { + } else if (s && ret == -1) { /* underlying BIO reported an I/O error */ Py_INCREF(s); ERR_clear_error(); @@ -479,10 +479,12 @@ _setSSLError (char *errstr, int errcode, char *filename, int lineno) { static PySSLSocket * newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, enum py_ssl_server_or_client socket_type, - char *server_hostname) + char *server_hostname, + PySSLMemoryBIO *inbio, PySSLMemoryBIO *outbio) { PySSLSocket *self; SSL_CTX *ctx = sslctx->ctx; + PyObject *hostname; long mode; self = PyObject_New(PySSLSocket, &PySSLSocket_Type); @@ -495,6 +497,18 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, self->ctx = sslctx; self->shutdown_seen_zero = 0; self->handshake_done = 0; + self->owner = NULL; + if (server_hostname != NULL) { + hostname = PyUnicode_Decode(server_hostname, strlen(server_hostname), + "idna", "strict"); + if (hostname == NULL) { + Py_DECREF(self); + return NULL; + } + self->server_hostname = hostname; + } else + self->server_hostname = NULL; + Py_INCREF(sslctx); /* Make sure the SSL error state is initialized */ @@ -504,8 +518,17 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS - SSL_set_app_data(self->ssl,self); - SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); + SSL_set_app_data(self->ssl, self); + if (sock) { + SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); + } else { + /* BIOs are reference counted and SSL_set_bio borrows our reference. + * To prevent a double free in memory_bio_dealloc() we need to take an + * extra reference here. */ + CRYPTO_add(&inbio->bio->references, 1, CRYPTO_LOCK_BIO); + CRYPTO_add(&outbio->bio->references, 1, CRYPTO_LOCK_BIO); + SSL_set_bio(self->ssl, inbio->bio, outbio->bio); + } mode = SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; #ifdef SSL_MODE_AUTO_RETRY mode |= SSL_MODE_AUTO_RETRY; @@ -520,7 +543,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, /* If the socket is in non-blocking mode or timeout mode, set the BIO * to non-blocking mode (blocking is the default) */ - if (sock->sock_timeout >= 0.0) { + if (sock && sock->sock_timeout >= 0) { BIO_set_nbio(SSL_get_rbio(self->ssl), 1); BIO_set_nbio(SSL_get_wbio(self->ssl), 1); } @@ -533,35 +556,52 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_END_ALLOW_THREADS self->socket_type = socket_type; - self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL); - if (self->Socket == NULL) { - Py_DECREF(self); - return NULL; + if (sock != NULL) { + self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL); + if (self->Socket == NULL) { + Py_DECREF(self); + Py_XDECREF(self->server_hostname); + return NULL; + } } return self; } /* SSL object methods */ -static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self) +/*[clinic input] +_ssl._SSLSocket.do_handshake +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) +/*[clinic end generated code: output=6c0898a8936548f6 input=d2d737de3df018c8]*/ { int ret; int err; int sockstate, nonblocking; - PySocketSockObject *sock - = (PySocketSockObject *) PyWeakref_GetObject(self->Socket); + PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; + + if (sock) { + if (((PyObject*)sock) == Py_None) { + _setSSLError("Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); + return NULL; + } + Py_INCREF(sock); - if (((PyObject*)sock) == Py_None) { - _setSSLError("Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; + /* just in case the blocking state of the socket has been changed */ + nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } - Py_INCREF(sock); - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + timeout = GET_SOCKET_TIMEOUT(sock); + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; /* Actually negotiate SSL connection */ /* XXX If SSL_do_handshake() returns 0, it's also a failure. */ @@ -570,15 +610,21 @@ static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self) ret = SSL_do_handshake(self->ssl); err = SSL_get_error(self->ssl, ret); PySSL_END_ALLOW_THREADS + if (PyErr_CheckSignals()) goto error; + + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); } else { sockstate = SOCKET_OPERATION_OK; } + if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySocketModule.timeout_error, ERRSTR("The handshake operation timed out")); @@ -595,7 +641,7 @@ static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self) break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); - Py_DECREF(sock); + Py_XDECREF(sock); if (ret < 1) return PySSL_SetError(self, ret, __FILE__, __LINE__); @@ -610,7 +656,7 @@ static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self) return Py_None; error: - Py_DECREF(sock); + Py_XDECREF(sock); return NULL; } @@ -779,12 +825,7 @@ _get_peer_alt_names (X509 *certificate) { char buf[2048]; char *vptr; int len; - /* Issue #2973: ASN1_item_d2i() API changed in OpenSSL 0.9.6m */ -#if OPENSSL_VERSION_NUMBER >= 0x009060dfL const unsigned char *p; -#else - unsigned char *p; -#endif if (certificate == NULL) return peer_alt_names; @@ -1274,25 +1315,28 @@ _certificate_to_der(X509 *certificate) return retval; } -static PyObject * -PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { +/*[clinic input] +_ssl._test_decode_cert + path: object(converter="PyUnicode_FSConverter") + / + +[clinic start generated code]*/ +static PyObject * +_ssl__test_decode_cert_impl(PyModuleDef *module, PyObject *path) +/*[clinic end generated code: output=679e01db282804e9 input=cdeaaf02d4346628]*/ +{ PyObject *retval = NULL; - PyObject *filename; X509 *x=NULL; BIO *cert; - if (!PyArg_ParseTuple(args, "O&:test_decode_certificate", - PyUnicode_FSConverter, &filename)) - return NULL; - if ((cert=BIO_new(BIO_s_file())) == NULL) { PyErr_SetString(PySSLErrorObject, "Can't malloc memory to read file"); goto fail0; } - if (BIO_read_filename(cert, PyBytes_AsString(filename)) <= 0) { + if (BIO_read_filename(cert, PyBytes_AsString(path)) <= 0) { PyErr_SetString(PySSLErrorObject, "Can't open file"); goto fail0; @@ -1309,20 +1353,33 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { X509_free(x); fail0: - Py_DECREF(filename); + Py_DECREF(path); if (cert != NULL) BIO_free(cert); return retval; } +/*[clinic input] +_ssl._SSLSocket.peer_certificate + der as binary_mode: bool = False + / + +Returns the certificate for the peer. + +If no certificate was provided, returns None. If a certificate was +provided, but not validated, returns an empty dictionary. Otherwise +returns a dict containing information about the peer certificate. + +If the optional argument is True, returns a DER-encoded copy of the +peer certificate, or None if no certificate was provided. This will +return the certificate even if it wasn't validated. +[clinic start generated code]*/ + static PyObject * -PySSL_peercert(PySSLSocket *self, PyObject *args) +_ssl__SSLSocket_peer_certificate_impl(PySSLSocket *self, int binary_mode) +/*[clinic end generated code: output=f0dc3e4d1d818a1d input=8281bd1d193db843]*/ { int verification; - int binary_mode = 0; - - if (!PyArg_ParseTuple(args, "|p:peer_certificate", &binary_mode)) - return NULL; if (!self->handshake_done) { PyErr_SetString(PyExc_ValueError, @@ -1344,68 +1401,123 @@ PySSL_peercert(PySSLSocket *self, PyObject *args) } } -PyDoc_STRVAR(PySSL_peercert_doc, -"peer_certificate([der=False]) -> certificate\n\ -\n\ -Returns the certificate for the peer. If no certificate was provided,\n\ -returns None. If a certificate was provided, but not validated, returns\n\ -an empty dictionary. Otherwise returns a dict containing information\n\ -about the peer certificate.\n\ -\n\ -If the optional argument is True, returns a DER-encoded copy of the\n\ -peer certificate, or None if no certificate was provided. This will\n\ -return the certificate even if it wasn't validated."); - -static PyObject *PySSL_cipher (PySSLSocket *self) { - - PyObject *retval, *v; - const SSL_CIPHER *current; - char *cipher_name; - char *cipher_protocol; - - if (self->ssl == NULL) - Py_RETURN_NONE; - current = SSL_get_current_cipher(self->ssl); - if (current == NULL) - Py_RETURN_NONE; - - retval = PyTuple_New(3); +static PyObject * +cipher_to_tuple(const SSL_CIPHER *cipher) +{ + const char *cipher_name, *cipher_protocol; + PyObject *v, *retval = PyTuple_New(3); if (retval == NULL) return NULL; - cipher_name = (char *) SSL_CIPHER_get_name(current); + cipher_name = SSL_CIPHER_get_name(cipher); if (cipher_name == NULL) { Py_INCREF(Py_None); PyTuple_SET_ITEM(retval, 0, Py_None); } else { v = PyUnicode_FromString(cipher_name); if (v == NULL) - goto fail0; + goto fail; PyTuple_SET_ITEM(retval, 0, v); } - cipher_protocol = (char *) SSL_CIPHER_get_version(current); + + cipher_protocol = SSL_CIPHER_get_version(cipher); if (cipher_protocol == NULL) { Py_INCREF(Py_None); PyTuple_SET_ITEM(retval, 1, Py_None); } else { v = PyUnicode_FromString(cipher_protocol); if (v == NULL) - goto fail0; + goto fail; PyTuple_SET_ITEM(retval, 1, v); } - v = PyLong_FromLong(SSL_CIPHER_get_bits(current, NULL)); + + v = PyLong_FromLong(SSL_CIPHER_get_bits(cipher, NULL)); if (v == NULL) - goto fail0; + goto fail; PyTuple_SET_ITEM(retval, 2, v); + return retval; - fail0: + fail: Py_DECREF(retval); return NULL; } +/*[clinic input] +_ssl._SSLSocket.shared_ciphers +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self) +/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/ +{ + SSL_SESSION *sess = SSL_get_session(self->ssl); + STACK_OF(SSL_CIPHER) *ciphers; + int i; + PyObject *res; + + if (!sess || !sess->ciphers) + Py_RETURN_NONE; + ciphers = sess->ciphers; + res = PyList_New(sk_SSL_CIPHER_num(ciphers)); + if (!res) + return NULL; + for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { + PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i)); + if (!tup) { + Py_DECREF(res); + return NULL; + } + PyList_SET_ITEM(res, i, tup); + } + return res; +} + +/*[clinic input] +_ssl._SSLSocket.cipher +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_cipher_impl(PySSLSocket *self) +/*[clinic end generated code: output=376417c16d0e5815 input=548fb0e27243796d]*/ +{ + const SSL_CIPHER *current; + + if (self->ssl == NULL) + Py_RETURN_NONE; + current = SSL_get_current_cipher(self->ssl); + if (current == NULL) + Py_RETURN_NONE; + return cipher_to_tuple(current); +} + +/*[clinic input] +_ssl._SSLSocket.version +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_version_impl(PySSLSocket *self) +/*[clinic end generated code: output=178aed33193b2cdb input=900186a503436fd6]*/ +{ + const char *version; + + if (self->ssl == NULL) + Py_RETURN_NONE; + version = SSL_get_version(self->ssl); + if (!strcmp(version, "unknown")) + Py_RETURN_NONE; + return PyUnicode_FromString(version); +} + #ifdef OPENSSL_NPN_NEGOTIATED -static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) { +/*[clinic input] +_ssl._SSLSocket.selected_npn_protocol +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_selected_npn_protocol_impl(PySSLSocket *self) +/*[clinic end generated code: output=b91d494cd207ecf6 input=c28fde139204b826]*/ +{ const unsigned char *out; unsigned int outlen; @@ -1414,11 +1526,38 @@ 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 +/*[clinic input] +_ssl._SSLSocket.selected_alpn_protocol +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_selected_alpn_protocol_impl(PySSLSocket *self) +/*[clinic end generated code: output=ec33688b303d250f input=442de30e35bc2913]*/ +{ + 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 -static PyObject *PySSL_compression(PySSLSocket *self) { +/*[clinic input] +_ssl._SSLSocket.compression +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_compression_impl(PySSLSocket *self) +/*[clinic end generated code: output=bd16cb1bb4646ae7 input=5d059d0a2bbc32c8]*/ +{ #ifdef OPENSSL_NO_COMP Py_RETURN_NONE; #else @@ -1473,6 +1612,54 @@ on the SSLContext to change the certificate information associated with the\n\ SSLSocket before the cryptographic exchange handshake messages\n"); +static PyObject * +PySSL_get_server_side(PySSLSocket *self, void *c) +{ + return PyBool_FromLong(self->socket_type == PY_SSL_SERVER); +} + +PyDoc_STRVAR(PySSL_get_server_side_doc, +"Whether this is a server-side socket."); + +static PyObject * +PySSL_get_server_hostname(PySSLSocket *self, void *c) +{ + if (self->server_hostname == NULL) + Py_RETURN_NONE; + Py_INCREF(self->server_hostname); + return self->server_hostname; +} + +PyDoc_STRVAR(PySSL_get_server_hostname_doc, +"The currently set server hostname (for SNI)."); + +static PyObject * +PySSL_get_owner(PySSLSocket *self, void *c) +{ + PyObject *owner; + + if (self->owner == NULL) + Py_RETURN_NONE; + + owner = PyWeakref_GetObject(self->owner); + Py_INCREF(owner); + return owner; +} + +static int +PySSL_set_owner(PySSLSocket *self, PyObject *value, void *c) +{ + Py_XDECREF(self->owner); + self->owner = PyWeakref_NewRef(value, NULL); + if (self->owner == NULL) + return -1; + return 0; +} + +PyDoc_STRVAR(PySSL_get_owner_doc, +"The Python-level owner of this object.\ +Passed as \"self\" in servername callback."); + static void PySSL_dealloc(PySSLSocket *self) { @@ -1482,6 +1669,8 @@ static void PySSL_dealloc(PySSLSocket *self) SSL_free(self->ssl); Py_XDECREF(self->Socket); Py_XDECREF(self->ctx); + Py_XDECREF(self->server_hostname); + Py_XDECREF(self->owner); PyObject_Del(self); } @@ -1491,17 +1680,27 @@ static void PySSL_dealloc(PySSLSocket *self) */ static int -check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) +PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout) { + int rc; +#ifdef HAVE_POLL + struct pollfd pollfd; + _PyTime_t ms; +#else + int nfds; fd_set fds; struct timeval tv; - int rc; +#endif /* Nothing to do unless we're in timeout mode (not non-blocking) */ - if (s->sock_timeout < 0.0) - return SOCKET_IS_BLOCKING; - else if (s->sock_timeout == 0.0) + if ((s == NULL) || (timeout == 0)) return SOCKET_IS_NONBLOCKING; + else if (timeout < 0) { + if (s->sock_timeout > 0) + return SOCKET_HAS_TIMED_OUT; + else + return SOCKET_IS_BLOCKING; + } /* Guard against closed socket */ if (s->sock_fd < 0) @@ -1510,85 +1709,91 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) /* Prefer poll, if available, since you can poll() any fd * which can't be done with select(). */ #ifdef HAVE_POLL - { - struct pollfd pollfd; - int timeout; - - pollfd.fd = s->sock_fd; - pollfd.events = writing ? POLLOUT : POLLIN; - - /* s->sock_timeout is in seconds, timeout in ms */ - timeout = (int)(s->sock_timeout * 1000 + 0.5); - PySSL_BEGIN_ALLOW_THREADS - rc = poll(&pollfd, 1, timeout); - PySSL_END_ALLOW_THREADS + pollfd.fd = s->sock_fd; + pollfd.events = writing ? POLLOUT : POLLIN; - goto normal_return; - } -#endif + /* timeout is in seconds, poll() uses milliseconds */ + ms = (int)_PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); + assert(ms <= INT_MAX); + PySSL_BEGIN_ALLOW_THREADS + rc = poll(&pollfd, 1, (int)ms); + PySSL_END_ALLOW_THREADS +#else /* Guard against socket too large for select*/ if (!_PyIsSelectable_fd(s->sock_fd)) return SOCKET_TOO_LARGE_FOR_SELECT; - /* Construct the arguments to select */ - tv.tv_sec = (int)s->sock_timeout; - tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING); + FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); - /* See if the socket is ready */ + /* Wait until the socket becomes ready */ PySSL_BEGIN_ALLOW_THREADS + nfds = Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int); if (writing) - rc = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), - NULL, &fds, NULL, &tv); + rc = select(nfds, NULL, &fds, NULL, &tv); else - rc = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), - &fds, NULL, NULL, &tv); + rc = select(nfds, &fds, NULL, NULL, &tv); PySSL_END_ALLOW_THREADS - -#ifdef HAVE_POLL -normal_return: #endif + /* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise (when we are able to write or when there's something to read) */ return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK; } -static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args) +/*[clinic input] +_ssl._SSLSocket.write + b: Py_buffer + / + +Writes the bytes-like object b into the SSL object. + +Returns the number of bytes written. +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) +/*[clinic end generated code: output=aa7a6be5527358d8 input=77262d994fe5100a]*/ { - Py_buffer buf; int len; int sockstate; int err; int nonblocking; - PySocketSockObject *sock - = (PySocketSockObject *) PyWeakref_GetObject(self->Socket); - - if (((PyObject*)sock) == Py_None) { - _setSSLError("Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - - if (!PyArg_ParseTuple(args, "y*:write", &buf)) { - Py_DECREF(sock); - return NULL; + PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; + + if (sock != NULL) { + if (((PyObject*)sock) == Py_None) { + _setSSLError("Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); + return NULL; + } + Py_INCREF(sock); } - if (buf.len > INT_MAX) { + if (b->len > INT_MAX) { PyErr_Format(PyExc_OverflowError, "string longer than %d bytes", INT_MAX); goto error; } - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + if (sock != NULL) { + /* just in case the blocking state of the socket has been changed */ + nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + } + + timeout = GET_SOCKET_TIMEOUT(sock); + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; - sockstate = check_socket_and_wait_for_timeout(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySocketModule.timeout_error, "The write operation timed out"); @@ -1602,21 +1807,27 @@ static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args) "Underlying socket too large for select()."); goto error; } + do { PySSL_BEGIN_ALLOW_THREADS - len = SSL_write(self->ssl, buf.buf, (int)buf.len); + len = SSL_write(self->ssl, b->buf, (int)b->len); err = SSL_get_error(self->ssl, len); PySSL_END_ALLOW_THREADS - if (PyErr_CheckSignals()) { + + if (PyErr_CheckSignals()) goto error; - } + + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); } else { sockstate = SOCKET_OPERATION_OK; } + if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySocketModule.timeout_error, "The write operation timed out"); @@ -1630,26 +1841,26 @@ static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args) } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); - Py_DECREF(sock); - PyBuffer_Release(&buf); + Py_XDECREF(sock); if (len > 0) return PyLong_FromLong(len); else return PySSL_SetError(self, len, __FILE__, __LINE__); error: - Py_DECREF(sock); - PyBuffer_Release(&buf); + Py_XDECREF(sock); return NULL; } -PyDoc_STRVAR(PySSL_SSLwrite_doc, -"write(s) -> len\n\ -\n\ -Writes the string s into the SSL object. Returns the number\n\ -of bytes written."); +/*[clinic input] +_ssl._SSLSocket.pending -static PyObject *PySSL_SSLpending(PySSLSocket *self) +Returns the number of already decrypted bytes available for read, pending on the connection. +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_pending_impl(PySSLSocket *self) +/*[clinic end generated code: output=983d9fecdc308a83 input=2b77487d6dfd597f]*/ { int count = 0; @@ -1662,49 +1873,52 @@ static PyObject *PySSL_SSLpending(PySSLSocket *self) return PyLong_FromLong(count); } -PyDoc_STRVAR(PySSL_SSLpending_doc, -"pending() -> count\n\ -\n\ -Returns the number of already decrypted bytes available for read,\n\ -pending on the connection.\n"); +/*[clinic input] +_ssl._SSLSocket.read + size as len: int + [ + buffer: Py_buffer(accept={rwbuffer}) + ] + / -static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args) +Read up to size bytes from the SSL socket. +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1, + Py_buffer *buffer) +/*[clinic end generated code: output=00097776cec2a0af input=ff157eb918d0905b]*/ { PyObject *dest = NULL; - Py_buffer buf; char *mem; - int len, count; - int buf_passed = 0; + int count; int sockstate; int err; int nonblocking; - PySocketSockObject *sock - = (PySocketSockObject *) PyWeakref_GetObject(self->Socket); - - if (((PyObject*)sock) == Py_None) { - _setSSLError("Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; + PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; + + if (sock != NULL) { + if (((PyObject*)sock) == Py_None) { + _setSSLError("Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); + return NULL; + } + Py_INCREF(sock); } - Py_INCREF(sock); - buf.obj = NULL; - buf.buf = NULL; - if (!PyArg_ParseTuple(args, "i|w*:read", &len, &buf)) - goto error; - - if ((buf.buf == NULL) && (buf.obj == NULL)) { + if (!group_right_1) { dest = PyBytes_FromStringAndSize(NULL, len); if (dest == NULL) goto error; mem = PyBytes_AS_STRING(dest); } else { - buf_passed = 1; - mem = buf.buf; - if (len <= 0 || len > buf.len) { - len = (int) buf.len; - if (buf.len != len) { + mem = buffer->buf; + if (len <= 0 || len > buffer->len) { + len = (int) buffer->len; + if (buffer->len != len) { PyErr_SetString(PyExc_OverflowError, "maximum length can't fit in a C 'int'"); goto error; @@ -1712,31 +1926,43 @@ static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args) } } - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + if (sock != NULL) { + /* just in case the blocking state of the socket has been changed */ + nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + } + + timeout = GET_SOCKET_TIMEOUT(sock); + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); err = SSL_get_error(self->ssl, count); PySSL_END_ALLOW_THREADS + if (PyErr_CheckSignals()) goto error; + + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(sock, 1); - } else if ((err == SSL_ERROR_ZERO_RETURN) && - (SSL_get_shutdown(self->ssl) == - SSL_RECEIVED_SHUTDOWN)) + sockstate = PySSL_select(sock, 1, timeout); + } else if (err == SSL_ERROR_ZERO_RETURN && + SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN) { count = 0; goto done; - } else { - sockstate = SOCKET_OPERATION_OK; } + else + sockstate = SOCKET_OPERATION_OK; + if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySocketModule.timeout_error, "The read operation timed out"); @@ -1745,55 +1971,66 @@ static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args) break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + if (count <= 0) { PySSL_SetError(self, count, __FILE__, __LINE__); goto error; } done: - Py_DECREF(sock); - if (!buf_passed) { + Py_XDECREF(sock); + if (!group_right_1) { _PyBytes_Resize(&dest, count); return dest; } else { - PyBuffer_Release(&buf); return PyLong_FromLong(count); } error: - Py_DECREF(sock); - if (!buf_passed) + Py_XDECREF(sock); + if (!group_right_1) Py_XDECREF(dest); - else - PyBuffer_Release(&buf); return NULL; } -PyDoc_STRVAR(PySSL_SSLread_doc, -"read([len]) -> string\n\ -\n\ -Read up to len bytes from the SSL socket."); +/*[clinic input] +_ssl._SSLSocket.shutdown + +Does the SSL shutdown handshake with the remote end. -static PyObject *PySSL_SSLshutdown(PySSLSocket *self) +Returns the underlying socket object. +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_shutdown_impl(PySSLSocket *self) +/*[clinic end generated code: output=ca1aa7ed9d25ca42 input=ede2cc1a2ddf0ee4]*/ { int err, ssl_err, sockstate, nonblocking; int zeros = 0; - PySocketSockObject *sock - = (PySocketSockObject *) PyWeakref_GetObject(self->Socket); + PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; + + if (sock != NULL) { + /* Guard against closed socket */ + if ((((PyObject*)sock) == Py_None) || (sock->sock_fd < 0)) { + _setSSLError("Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); + return NULL; + } + Py_INCREF(sock); - /* Guard against closed socket */ - if ((((PyObject*)sock) == Py_None) || (sock->sock_fd < 0)) { - _setSSLError("Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; + /* Just in case the blocking state of the socket has been changed */ + nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } - Py_INCREF(sock); - /* Just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + timeout = GET_SOCKET_TIMEOUT(sock); + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; while (1) { PySSL_BEGIN_ALLOW_THREADS @@ -1809,6 +2046,7 @@ static PyObject *PySSL_SSLshutdown(PySSLSocket *self) SSL_set_read_ahead(self->ssl, 0); err = SSL_shutdown(self->ssl); PySSL_END_ALLOW_THREADS + /* If err == 1, a secure shutdown with SSL_shutdown() is complete */ if (err > 0) break; @@ -1823,14 +2061,18 @@ static PyObject *PySSL_SSLshutdown(PySSLSocket *self) continue; } + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + /* Possibly retry shutdown until timeout or failure */ ssl_err = SSL_get_error(self->ssl, err); if (ssl_err == SSL_ERROR_WANT_READ) - sockstate = check_socket_and_wait_for_timeout(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); else if (ssl_err == SSL_ERROR_WANT_WRITE) - sockstate = check_socket_and_wait_for_timeout(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); else break; + if (sockstate == SOCKET_HAS_TIMED_OUT) { if (ssl_err == SSL_ERROR_WANT_READ) PyErr_SetString(PySocketModule.timeout_error, @@ -1851,27 +2093,31 @@ static PyObject *PySSL_SSLshutdown(PySSLSocket *self) } if (err < 0) { - Py_DECREF(sock); + Py_XDECREF(sock); return PySSL_SetError(self, err, __FILE__, __LINE__); } - else + if (sock) /* It's already INCREF'ed */ return (PyObject *) sock; + else + Py_RETURN_NONE; error: - Py_DECREF(sock); + Py_XDECREF(sock); return NULL; } -PyDoc_STRVAR(PySSL_SSLshutdown_doc, -"shutdown(s) -> socket\n\ -\n\ -Does the SSL shutdown handshake with the remote end, and returns\n\ -the underlying socket object."); +/*[clinic input] +_ssl._SSLSocket.tls_unique_cb + +Returns the 'tls-unique' channel binding data, as defined by RFC 5929. + +If the TLS handshake is not yet complete, None is returned. +[clinic start generated code]*/ -#if HAVE_OPENSSL_FINISHED static PyObject * -PySSL_tls_unique_cb(PySSLSocket *self) +_ssl__SSLSocket_tls_unique_cb_impl(PySSLSocket *self) +/*[clinic end generated code: output=f3a832d603f586af input=439525c7b3d8d34d]*/ { PyObject *retval = NULL; char buf[PySSL_CB_MAXLEN]; @@ -1895,42 +2141,32 @@ PySSL_tls_unique_cb(PySSLSocket *self) return retval; } -PyDoc_STRVAR(PySSL_tls_unique_cb_doc, -"tls_unique_cb() -> bytes\n\ -\n\ -Returns the 'tls-unique' channel binding data, as defined by RFC 5929.\n\ -\n\ -If the TLS handshake is not yet complete, None is returned"); - -#endif /* HAVE_OPENSSL_FINISHED */ - static PyGetSetDef ssl_getsetlist[] = { {"context", (getter) PySSL_get_context, (setter) PySSL_set_context, PySSL_set_context_doc}, + {"server_side", (getter) PySSL_get_server_side, NULL, + PySSL_get_server_side_doc}, + {"server_hostname", (getter) PySSL_get_server_hostname, NULL, + PySSL_get_server_hostname_doc}, + {"owner", (getter) PySSL_get_owner, (setter) PySSL_set_owner, + PySSL_get_owner_doc}, {NULL}, /* sentinel */ }; static PyMethodDef PySSLMethods[] = { - {"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS}, - {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, - PySSL_SSLwrite_doc}, - {"read", (PyCFunction)PySSL_SSLread, METH_VARARGS, - PySSL_SSLread_doc}, - {"pending", (PyCFunction)PySSL_SSLpending, METH_NOARGS, - PySSL_SSLpending_doc}, - {"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS, - PySSL_peercert_doc}, - {"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS}, -#ifdef OPENSSL_NPN_NEGOTIATED - {"selected_npn_protocol", (PyCFunction)PySSL_selected_npn_protocol, METH_NOARGS}, -#endif - {"compression", (PyCFunction)PySSL_compression, METH_NOARGS}, - {"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, - PySSL_SSLshutdown_doc}, -#if HAVE_OPENSSL_FINISHED - {"tls_unique_cb", (PyCFunction)PySSL_tls_unique_cb, METH_NOARGS, - PySSL_tls_unique_cb_doc}, -#endif + _SSL__SSLSOCKET_DO_HANDSHAKE_METHODDEF + _SSL__SSLSOCKET_WRITE_METHODDEF + _SSL__SSLSOCKET_READ_METHODDEF + _SSL__SSLSOCKET_PENDING_METHODDEF + _SSL__SSLSOCKET_PEER_CERTIFICATE_METHODDEF + _SSL__SSLSOCKET_CIPHER_METHODDEF + _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF + _SSL__SSLSOCKET_VERSION_METHODDEF + _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_COMPRESSION_METHODDEF + _SSL__SSLSOCKET_SHUTDOWN_METHODDEF + _SSL__SSLSOCKET_TLS_UNIQUE_CB_METHODDEF {NULL, NULL} }; @@ -1973,20 +2209,21 @@ static PyTypeObject PySSLSocket_Type = { * _SSLContext objects */ +/*[clinic input] +@classmethod +_ssl._SSLContext.__new__ + protocol as proto_version: int + / +[clinic start generated code]*/ + static PyObject * -context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +_ssl__SSLContext_impl(PyTypeObject *type, int proto_version) +/*[clinic end generated code: output=2cf0d7a0741b6bd1 input=8d58a805b95fc534]*/ { - char *kwlist[] = {"protocol", NULL}; PySSLContext *self; - int proto_version = PY_SSL_VERSION_SSL23; long options; SSL_CTX *ctx = NULL; - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "i:_SSLContext", kwlist, - &proto_version)) - return NULL; - PySSL_BEGIN_ALLOW_THREADS if (proto_version == PY_SSL_VERSION_TLS1) ctx = SSL_CTX_new(TLSv1_method()); @@ -2031,6 +2268,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 @@ -2099,20 +2339,25 @@ 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); } +/*[clinic input] +_ssl._SSLContext.set_ciphers + cipherlist: str + / +[clinic start generated code]*/ + static PyObject * -set_ciphers(PySSLContext *self, PyObject *args) +_ssl__SSLContext_set_ciphers_impl(PySSLContext *self, const char *cipherlist) +/*[clinic end generated code: output=3a3162f3557c0f3f input=a7ac931b9f3ca7fc]*/ { - int ret; - const char *cipherlist; - - if (!PyArg_ParseTuple(args, "s:set_ciphers", &cipherlist)) - return NULL; - ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist); + int ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist); if (ret == 0) { /* Clearing the error queue is necessary on some OpenSSL versions, otherwise the error will be reported again when another SSL call @@ -2126,6 +2371,30 @@ set_ciphers(PySSLContext *self, PyObject *args) } #ifdef OPENSSL_NPN_NEGOTIATED +static int +do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen, + const unsigned char *server_protocols, unsigned int server_protocols_len, + const unsigned char *client_protocols, unsigned int client_protocols_len) +{ + int ret; + if (client_protocols == NULL) { + client_protocols = (unsigned char *)""; + client_protocols_len = 0; + } + if (server_protocols == NULL) { + server_protocols = (unsigned char *)""; + server_protocols_len = 0; + } + + ret = SSL_select_next_proto(out, outlen, + server_protocols, server_protocols_len, + client_protocols, client_protocols_len); + if (alpn && ret != OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_NOACK; + + return SSL_TLSEXT_ERR_OK; +} + /* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */ static int _advertiseNPN_cb(SSL *s, @@ -2135,10 +2404,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; } @@ -2151,46 +2420,30 @@ _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(0, out, outlen, server, server_len, + ctx->npn_protocols, ctx->npn_protocols_len); } #endif +/*[clinic input] +_ssl._SSLContext._set_npn_protocols + protos: Py_buffer + / +[clinic start generated code]*/ + static PyObject * -_set_npn_protocols(PySSLContext *self, PyObject *args) +_ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self, + Py_buffer *protos) +/*[clinic end generated code: output=72b002c3324390c6 input=319fcb66abf95bd7]*/ { #ifdef OPENSSL_NPN_NEGOTIATED - Py_buffer protos; - - if (!PyArg_ParseTuple(args, "y*:set_npn_protocols", &protos)) - return NULL; - - if (self->npn_protocols != NULL) { - PyMem_Free(self->npn_protocols); - } - - self->npn_protocols = PyMem_Malloc(protos.len); - if (self->npn_protocols == NULL) { - PyBuffer_Release(&protos); + PyMem_Free(self->npn_protocols); + self->npn_protocols = PyMem_Malloc(protos->len); + if (self->npn_protocols == NULL) return PyErr_NoMemory(); - } - memcpy(self->npn_protocols, protos.buf, protos.len); - self->npn_protocols_len = (int) protos.len; + memcpy(self->npn_protocols, protos->buf, protos->len); + self->npn_protocols_len = (int) protos->len; /* set both server and client callbacks, because the context can * be used to create both types of sockets */ @@ -2201,7 +2454,6 @@ _set_npn_protocols(PySSLContext *self, PyObject *args) _selectNPN_cb, self); - PyBuffer_Release(&protos); Py_RETURN_NONE; #else PyErr_SetString(PyExc_NotImplementedError, @@ -2210,6 +2462,51 @@ _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(1, (unsigned char **)out, outlen, + ctx->alpn_protocols, ctx->alpn_protocols_len, + client_protocols, client_protocols_len); +} +#endif + +/*[clinic input] +_ssl._SSLContext._set_alpn_protocols + protos: Py_buffer + / +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext__set_alpn_protocols_impl(PySSLContext *self, + Py_buffer *protos) +/*[clinic end generated code: output=87599a7f76651a9b input=9bba964595d519be]*/ +{ +#ifdef HAVE_ALPN + 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; + + 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); + + 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) { @@ -2253,7 +2550,6 @@ set_verify_mode(PySSLContext *self, PyObject *arg, void *c) return 0; } -#ifdef HAVE_OPENSSL_VERIFY_PARAM static PyObject * get_verify_flags(PySSLContext *self, void *c) { @@ -2291,7 +2587,6 @@ set_verify_flags(PySSLContext *self, PyObject *arg, void *c) } return 0; } -#endif static PyObject * get_options(PySSLContext *self, void *c) @@ -2448,11 +2743,19 @@ error: return -1; } +/*[clinic input] +_ssl._SSLContext.load_cert_chain + certfile: object + keyfile: object = NULL + password: object = NULL + +[clinic start generated code]*/ + static PyObject * -load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds) +_ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, + PyObject *keyfile, PyObject *password) +/*[clinic end generated code: output=9480bc1c380e2095 input=7cf9ac673cbee6fc]*/ { - char *kwlist[] = {"certfile", "keyfile", "password", NULL}; - PyObject *certfile, *keyfile = NULL, *password = NULL; PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; pem_password_cb *orig_passwd_cb = self->ctx->default_passwd_callback; void *orig_passwd_userdata = self->ctx->default_passwd_callback_userdata; @@ -2461,10 +2764,6 @@ load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds) errno = 0; ERR_clear_error(); - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|OO:load_cert_chain", kwlist, - &certfile, &keyfile, &password)) - return NULL; if (keyfile == Py_None) keyfile = NULL; if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) { @@ -2632,21 +2931,26 @@ _add_ca_certs(PySSLContext *self, void *data, Py_ssize_t len, } +/*[clinic input] +_ssl._SSLContext.load_verify_locations + cafile: object = NULL + capath: object = NULL + cadata: object = NULL + +[clinic start generated code]*/ + static PyObject * -load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds) +_ssl__SSLContext_load_verify_locations_impl(PySSLContext *self, + PyObject *cafile, + PyObject *capath, + PyObject *cadata) +/*[clinic end generated code: output=454c7e41230ca551 input=997f1fb3a784ef88]*/ { - char *kwlist[] = {"cafile", "capath", "cadata", NULL}; - PyObject *cafile = NULL, *capath = NULL, *cadata = NULL; PyObject *cafile_bytes = NULL, *capath_bytes = NULL; const char *cafile_buf = NULL, *capath_buf = NULL; int r = 0, ok = 1; errno = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|OOO:load_verify_locations", kwlist, - &cafile, &capath, &cadata)) - return NULL; - if (cafile == Py_None) cafile = NULL; if (capath == Py_None) @@ -2743,18 +3047,24 @@ load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds) } } +/*[clinic input] +_ssl._SSLContext.load_dh_params + path as filepath: object + / + +[clinic start generated code]*/ + static PyObject * -load_dh_params(PySSLContext *self, PyObject *filepath) +_ssl__SSLContext_load_dh_params(PySSLContext *self, PyObject *filepath) +/*[clinic end generated code: output=1c8e57a38e055af0 input=c8871f3c796ae1d6]*/ { FILE *f; DH *dh; f = _Py_fopen_obj(filepath, "rb"); - if (f == NULL) { - if (!PyErr_Occurred()) - PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); + if (f == NULL) return NULL; - } + errno = 0; PySSL_BEGIN_ALLOW_THREADS dh = PEM_read_DHparams(f, NULL, NULL, NULL); @@ -2776,38 +3086,76 @@ load_dh_params(PySSLContext *self, PyObject *filepath) Py_RETURN_NONE; } +/*[clinic input] +_ssl._SSLContext._wrap_socket + sock: object(subclass_of="PySocketModule.Sock_Type") + server_side: int + server_hostname as hostname_obj: object = None + +[clinic start generated code]*/ + static PyObject * -context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds) +_ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock, + int server_side, PyObject *hostname_obj) +/*[clinic end generated code: output=6973e4b60995e933 input=83859b9156ddfc63]*/ { - char *kwlist[] = {"sock", "server_side", "server_hostname", NULL}; - PySocketSockObject *sock; - int server_side = 0; char *hostname = NULL; - PyObject *hostname_obj, *res; + PyObject *res; /* server_hostname is either None (or absent), or to be encoded using the idna encoding. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i|O!:_wrap_socket", kwlist, - PySocketModule.Sock_Type, - &sock, &server_side, - Py_TYPE(Py_None), &hostname_obj)) { - PyErr_Clear(); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!iet:_wrap_socket", kwlist, - PySocketModule.Sock_Type, - &sock, &server_side, - "idna", &hostname)) + if (hostname_obj != Py_None) { + if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname)) return NULL; } - res = (PyObject *) newPySSLSocket(self, sock, server_side, - hostname); + res = (PyObject *) newPySSLSocket(self, (PySocketSockObject *)sock, + server_side, hostname, + NULL, NULL); if (hostname != NULL) PyMem_Free(hostname); return res; } +/*[clinic input] +_ssl._SSLContext._wrap_bio + incoming: object(subclass_of="&PySSLMemoryBIO_Type", type="PySSLMemoryBIO *") + outgoing: object(subclass_of="&PySSLMemoryBIO_Type", type="PySSLMemoryBIO *") + server_side: int + server_hostname as hostname_obj: object = None + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming, + PySSLMemoryBIO *outgoing, int server_side, + PyObject *hostname_obj) +/*[clinic end generated code: output=4fe4ba75ad95940d input=17725ecdac0bf220]*/ +{ + char *hostname = NULL; + PyObject *res; + + /* server_hostname is either None (or absent), or to be encoded + using the idna encoding. */ + if (hostname_obj != Py_None) { + if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname)) + return NULL; + } + + res = (PyObject *) newPySSLSocket(self, NULL, server_side, hostname, + incoming, outgoing); + + PyMem_Free(hostname); + return res; +} + +/*[clinic input] +_ssl._SSLContext.session_stats +[clinic start generated code]*/ + static PyObject * -session_stats(PySSLContext *self, PyObject *unused) +_ssl__SSLContext_session_stats_impl(PySSLContext *self) +/*[clinic end generated code: output=0d96411c42893bfb input=7e0a81fb11102c8b]*/ { int r; PyObject *value, *stats = PyDict_New(); @@ -2845,8 +3193,13 @@ error: return NULL; } +/*[clinic input] +_ssl._SSLContext.set_default_verify_paths +[clinic start generated code]*/ + static PyObject * -set_default_verify_paths(PySSLContext *self, PyObject *unused) +_ssl__SSLContext_set_default_verify_paths_impl(PySSLContext *self) +/*[clinic end generated code: output=0bee74e6e09deaaa input=35f3408021463d74]*/ { if (!SSL_CTX_set_default_verify_paths(self->ctx)) { _setSSLError(NULL, 0, __FILE__, __LINE__); @@ -2856,8 +3209,16 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused) } #ifndef OPENSSL_NO_ECDH +/*[clinic input] +_ssl._SSLContext.set_ecdh_curve + name: object + / + +[clinic start generated code]*/ + static PyObject * -set_ecdh_curve(PySSLContext *self, PyObject *name) +_ssl__SSLContext_set_ecdh_curve(PySSLContext *self, PyObject *name) +/*[clinic end generated code: output=23022c196e40d7d2 input=c2bafb6f6e34726b]*/ { PyObject *name_bytes; int nid; @@ -2912,11 +3273,25 @@ _servername_callback(SSL *s, int *al, void *args) ssl = SSL_get_app_data(s); assert(PySSLSocket_Check(ssl)); - ssl_socket = PyWeakref_GetObject(ssl->Socket); + + /* The servername callback expects a argument that represents the current + * SSL connection and that has a .context attribute that can be changed to + * identify the requested hostname. Since the official API is the Python + * level API we want to pass the callback a Python level object rather than + * a _ssl.SSLSocket instance. If there's an "owner" (typically an + * SSLObject) that will be passed. Otherwise if there's a socket then that + * will be passed. If both do not exist only then the C-level object is + * passed. */ + if (ssl->owner) + ssl_socket = PyWeakref_GetObject(ssl->owner); + else if (ssl->Socket) + ssl_socket = PyWeakref_GetObject(ssl->Socket); + else + ssl_socket = (PyObject *) ssl; + Py_INCREF(ssl_socket); - if (ssl_socket == Py_None) { + if (ssl_socket == Py_None) goto error; - } if (servername == NULL) { result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, @@ -2977,25 +3352,23 @@ error: } #endif -PyDoc_STRVAR(PySSL_set_servername_callback_doc, -"set_servername_callback(method)\n\ -\n\ -This sets a callback that will be called when a server name is provided by\n\ -the SSL/TLS client in the SNI extension.\n\ -\n\ -If the argument is None then the callback is disabled. The method is called\n\ -with the SSLSocket, the server name as a string, and the SSLContext object.\n\ -See RFC 6066 for details of the SNI extension."); +/*[clinic input] +_ssl._SSLContext.set_servername_callback + method as cb: object + / + +Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension. + +If the argument is None then the callback is disabled. The method is called +with the SSLSocket, the server name as a string, and the SSLContext object. +See RFC 6066 for details of the SNI extension. +[clinic start generated code]*/ static PyObject * -set_servername_callback(PySSLContext *self, PyObject *args) +_ssl__SSLContext_set_servername_callback(PySSLContext *self, PyObject *cb) +/*[clinic end generated code: output=3439a1b2d5d3b7ea input=a2a83620197d602b]*/ { #if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) - PyObject *cb; - - if (!PyArg_ParseTuple(args, "O", &cb)) - return NULL; - Py_CLEAR(self->set_hostname); if (cb == Py_None) { SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); @@ -3022,17 +3395,21 @@ set_servername_callback(PySSLContext *self, PyObject *args) #endif } -PyDoc_STRVAR(PySSL_get_stats_doc, -"cert_store_stats() -> {'crl': int, 'x509_ca': int, 'x509': int}\n\ -\n\ -Returns quantities of loaded X.509 certificates. X.509 certificates with a\n\ -CA extension and certificate revocation lists inside the context's cert\n\ -store.\n\ -NOTE: Certificates in a capath directory aren't loaded unless they have\n\ -been used at least once."); +/*[clinic input] +_ssl._SSLContext.cert_store_stats + +Returns quantities of loaded X.509 certificates. + +X.509 certificates with a CA extension and certificate revocation lists +inside the context's cert store. + +NOTE: Certificates in a capath directory aren't loaded unless they have +been used at least once. +[clinic start generated code]*/ static PyObject * -cert_store_stats(PySSLContext *self) +_ssl__SSLContext_cert_store_stats_impl(PySSLContext *self) +/*[clinic end generated code: output=5f356f4d9cca874d input=eb40dd0f6d0e40cf]*/ { X509_STORE *store; X509_OBJECT *obj; @@ -3065,27 +3442,26 @@ cert_store_stats(PySSLContext *self) "x509_ca", ca); } -PyDoc_STRVAR(PySSL_get_ca_certs_doc, -"get_ca_certs(binary_form=False) -> list of loaded certificate\n\ -\n\ -Returns a list of dicts with information of loaded CA certs. If the\n\ -optional argument is True, returns a DER-encoded copy of the CA certificate.\n\ -NOTE: Certificates in a capath directory aren't loaded unless they have\n\ -been used at least once."); +/*[clinic input] +_ssl._SSLContext.get_ca_certs + binary_form: bool = False + +Returns a list of dicts with information of loaded CA certs. + +If the optional argument is True, returns a DER-encoded copy of the CA +certificate. + +NOTE: Certificates in a capath directory aren't loaded unless they have +been used at least once. +[clinic start generated code]*/ static PyObject * -get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds) +_ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) +/*[clinic end generated code: output=0d58f148f37e2938 input=6887b5a09b7f9076]*/ { - char *kwlist[] = {"binary_form", NULL}; X509_STORE *store; PyObject *ci = NULL, *rlist = NULL; int i; - int binary_mode = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|p:get_ca_certs", - kwlist, &binary_mode)) { - return NULL; - } if ((rlist = PyList_New(0)) == NULL) { return NULL; @@ -3106,7 +3482,7 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds) if (!X509_check_ca(cert)) { continue; } - if (binary_mode) { + if (binary_form) { ci = _certificate_to_der(cert); } else { ci = _decode_certificate(cert); @@ -3133,42 +3509,28 @@ static PyGetSetDef context_getsetlist[] = { (setter) set_check_hostname, NULL}, {"options", (getter) get_options, (setter) set_options, NULL}, -#ifdef HAVE_OPENSSL_VERIFY_PARAM {"verify_flags", (getter) get_verify_flags, (setter) set_verify_flags, NULL}, -#endif {"verify_mode", (getter) get_verify_mode, (setter) set_verify_mode, NULL}, {NULL}, /* sentinel */ }; static struct PyMethodDef context_methods[] = { - {"_wrap_socket", (PyCFunction) context_wrap_socket, - METH_VARARGS | METH_KEYWORDS, NULL}, - {"set_ciphers", (PyCFunction) set_ciphers, - METH_VARARGS, NULL}, - {"_set_npn_protocols", (PyCFunction) _set_npn_protocols, - METH_VARARGS, NULL}, - {"load_cert_chain", (PyCFunction) load_cert_chain, - METH_VARARGS | METH_KEYWORDS, NULL}, - {"load_dh_params", (PyCFunction) load_dh_params, - METH_O, NULL}, - {"load_verify_locations", (PyCFunction) load_verify_locations, - METH_VARARGS | METH_KEYWORDS, NULL}, - {"session_stats", (PyCFunction) session_stats, - METH_NOARGS, NULL}, - {"set_default_verify_paths", (PyCFunction) set_default_verify_paths, - METH_NOARGS, NULL}, -#ifndef OPENSSL_NO_ECDH - {"set_ecdh_curve", (PyCFunction) set_ecdh_curve, - METH_O, NULL}, -#endif - {"set_servername_callback", (PyCFunction) set_servername_callback, - METH_VARARGS, PySSL_set_servername_callback_doc}, - {"cert_store_stats", (PyCFunction) cert_store_stats, - METH_NOARGS, PySSL_get_stats_doc}, - {"get_ca_certs", (PyCFunction) get_ca_certs, - METH_VARARGS | METH_KEYWORDS, PySSL_get_ca_certs_doc}, + _SSL__SSLCONTEXT__WRAP_SOCKET_METHODDEF + _SSL__SSLCONTEXT__WRAP_BIO_METHODDEF + _SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF + _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF + _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF + _SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF + _SSL__SSLCONTEXT_LOAD_VERIFY_LOCATIONS_METHODDEF + _SSL__SSLCONTEXT_SESSION_STATS_METHODDEF + _SSL__SSLCONTEXT_SET_DEFAULT_VERIFY_PATHS_METHODDEF + _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF + _SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF + _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF + _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -3210,23 +3572,252 @@ static PyTypeObject PySSLContext_Type = { 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ - context_new, /*tp_new*/ + _ssl__SSLContext, /*tp_new*/ +}; + + +/* + * MemoryBIO objects + */ + +/*[clinic input] +@classmethod +_ssl.MemoryBIO.__new__ + +[clinic start generated code]*/ + +static PyObject * +_ssl_MemoryBIO_impl(PyTypeObject *type) +/*[clinic end generated code: output=8820a58db78330ac input=26d22e4909ecb1b5]*/ +{ + BIO *bio; + PySSLMemoryBIO *self; + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + PyErr_SetString(PySSLErrorObject, + "failed to allocate BIO"); + return NULL; + } + /* Since our BIO is non-blocking an empty read() does not indicate EOF, + * just that no data is currently available. The SSL routines should retry + * the read, which we can achieve by calling BIO_set_retry_read(). */ + BIO_set_retry_read(bio); + BIO_set_mem_eof_return(bio, -1); + + assert(type != NULL && type->tp_alloc != NULL); + self = (PySSLMemoryBIO *) type->tp_alloc(type, 0); + if (self == NULL) { + BIO_free(bio); + return NULL; + } + self->bio = bio; + self->eof_written = 0; + + return (PyObject *) self; +} + +static void +memory_bio_dealloc(PySSLMemoryBIO *self) +{ + BIO_free(self->bio); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +memory_bio_get_pending(PySSLMemoryBIO *self, void *c) +{ + return PyLong_FromLong(BIO_ctrl_pending(self->bio)); +} + +PyDoc_STRVAR(PySSL_memory_bio_pending_doc, +"The number of bytes pending in the memory BIO."); + +static PyObject * +memory_bio_get_eof(PySSLMemoryBIO *self, void *c) +{ + return PyBool_FromLong((BIO_ctrl_pending(self->bio) == 0) + && self->eof_written); +} + +PyDoc_STRVAR(PySSL_memory_bio_eof_doc, +"Whether the memory BIO is at EOF."); + +/*[clinic input] +_ssl.MemoryBIO.read + size as len: int = -1 + / + +Read up to size bytes from the memory BIO. + +If size is not specified, read the entire buffer. +If the return value is an empty bytes instance, this means either +EOF or that no data is available. Use the "eof" property to +distinguish between the two. +[clinic start generated code]*/ + +static PyObject * +_ssl_MemoryBIO_read_impl(PySSLMemoryBIO *self, int len) +/*[clinic end generated code: output=a657aa1e79cd01b3 input=574d7be06a902366]*/ +{ + int avail, nbytes; + PyObject *result; + + avail = BIO_ctrl_pending(self->bio); + if ((len < 0) || (len > avail)) + len = avail; + + result = PyBytes_FromStringAndSize(NULL, len); + if ((result == NULL) || (len == 0)) + return result; + + nbytes = BIO_read(self->bio, PyBytes_AS_STRING(result), len); + /* There should never be any short reads but check anyway. */ + if ((nbytes < len) && (_PyBytes_Resize(&result, len) < 0)) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +/*[clinic input] +_ssl.MemoryBIO.write + b: Py_buffer + / + +Writes the bytes b into the memory BIO. + +Returns the number of bytes written. +[clinic start generated code]*/ + +static PyObject * +_ssl_MemoryBIO_write_impl(PySSLMemoryBIO *self, Py_buffer *b) +/*[clinic end generated code: output=156ec59110d75935 input=e45757b3e17c4808]*/ +{ + int nbytes; + + if (b->len > INT_MAX) { + PyErr_Format(PyExc_OverflowError, + "string longer than %d bytes", INT_MAX); + return NULL; + } + + if (self->eof_written) { + PyErr_SetString(PySSLErrorObject, + "cannot write() after write_eof()"); + return NULL; + } + + nbytes = BIO_write(self->bio, b->buf, b->len); + if (nbytes < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + + return PyLong_FromLong(nbytes); +} + +/*[clinic input] +_ssl.MemoryBIO.write_eof + +Write an EOF marker to the memory BIO. + +When all data has been read, the "eof" property will be True. +[clinic start generated code]*/ + +static PyObject * +_ssl_MemoryBIO_write_eof_impl(PySSLMemoryBIO *self) +/*[clinic end generated code: output=d4106276ccd1ed34 input=56a945f1d29e8bd6]*/ +{ + self->eof_written = 1; + /* After an EOF is written, a zero return from read() should be a real EOF + * i.e. it should not be retried. Clear the SHOULD_RETRY flag. */ + BIO_clear_retry_flags(self->bio); + BIO_set_mem_eof_return(self->bio, 0); + + Py_RETURN_NONE; +} + +static PyGetSetDef memory_bio_getsetlist[] = { + {"pending", (getter) memory_bio_get_pending, NULL, + PySSL_memory_bio_pending_doc}, + {"eof", (getter) memory_bio_get_eof, NULL, + PySSL_memory_bio_eof_doc}, + {NULL}, /* sentinel */ }; +static struct PyMethodDef memory_bio_methods[] = { + _SSL_MEMORYBIO_READ_METHODDEF + _SSL_MEMORYBIO_WRITE_METHODDEF + _SSL_MEMORYBIO_WRITE_EOF_METHODDEF + {NULL, NULL} /* sentinel */ +}; +static PyTypeObject PySSLMemoryBIO_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_ssl.MemoryBIO", /*tp_name*/ + sizeof(PySSLMemoryBIO), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)memory_bio_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + memory_bio_methods, /*tp_methods*/ + 0, /*tp_members*/ + memory_bio_getsetlist, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + _ssl_MemoryBIO, /*tp_new*/ +}; -#ifdef HAVE_OPENSSL_RAND /* helper routines for seeding the SSL PRNG */ +/*[clinic input] +_ssl.RAND_add + string as view: Py_buffer(accept={str, buffer}) + entropy: double + / + +Mix string into the OpenSSL PRNG state. + +entropy (a float) is a lower bound on the entropy contained in +string. See RFC 1750. +[clinic start generated code]*/ + static PyObject * -PySSL_RAND_add(PyObject *self, PyObject *args) +_ssl_RAND_add_impl(PyModuleDef *module, Py_buffer *view, double entropy) +/*[clinic end generated code: output=0f8d5c8cce328958 input=580c85e6a3a4fe29]*/ { - char *buf; + const char *buf; Py_ssize_t len, written; - double entropy; - if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) - return NULL; + buf = (const char *)view->buf; + len = view->len; do { written = Py_MIN(len, INT_MAX); RAND_add(buf, (int)written, entropy); @@ -3237,12 +3828,6 @@ PySSL_RAND_add(PyObject *self, PyObject *args) return Py_None; } -PyDoc_STRVAR(PySSL_RAND_add_doc, -"RAND_add(string, entropy)\n\ -\n\ -Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ -bound on the entropy contained in string. See RFC 1750."); - static PyObject * PySSL_RAND(int len, int pseudo) { @@ -3282,60 +3867,72 @@ PySSL_RAND(int len, int pseudo) return NULL; } +/*[clinic input] +_ssl.RAND_bytes + n: int + / + +Generate n cryptographically strong pseudo-random bytes. +[clinic start generated code]*/ + static PyObject * -PySSL_RAND_bytes(PyObject *self, PyObject *args) +_ssl_RAND_bytes_impl(PyModuleDef *module, int n) +/*[clinic end generated code: output=7d8741bdc1d435f3 input=678ddf2872dfebfc]*/ { - int len; - if (!PyArg_ParseTuple(args, "i:RAND_bytes", &len)) - return NULL; - return PySSL_RAND(len, 0); + return PySSL_RAND(n, 0); } -PyDoc_STRVAR(PySSL_RAND_bytes_doc, -"RAND_bytes(n) -> bytes\n\ -\n\ -Generate n cryptographically strong pseudo-random bytes."); +/*[clinic input] +_ssl.RAND_pseudo_bytes + n: int + / + +Generate n pseudo-random bytes. + +Return a pair (bytes, is_cryptographic). is_cryptographic is True +if the bytes generated are cryptographically strong. +[clinic start generated code]*/ static PyObject * -PySSL_RAND_pseudo_bytes(PyObject *self, PyObject *args) +_ssl_RAND_pseudo_bytes_impl(PyModuleDef *module, int n) +/*[clinic end generated code: output=dd673813107f3875 input=58312bd53f9bbdd0]*/ { - int len; - if (!PyArg_ParseTuple(args, "i:RAND_pseudo_bytes", &len)) - return NULL; - return PySSL_RAND(len, 1); + return PySSL_RAND(n, 1); } -PyDoc_STRVAR(PySSL_RAND_pseudo_bytes_doc, -"RAND_pseudo_bytes(n) -> (bytes, is_cryptographic)\n\ -\n\ -Generate n pseudo-random bytes. is_cryptographic is True if the bytes\ -generated are cryptographically strong."); +/*[clinic input] +_ssl.RAND_status + +Returns 1 if the OpenSSL PRNG has been seeded with enough data and 0 if not. + +It is necessary to seed the PRNG with RAND_add() on some platforms before +using the ssl() function. +[clinic start generated code]*/ static PyObject * -PySSL_RAND_status(PyObject *self) +_ssl_RAND_status_impl(PyModuleDef *module) +/*[clinic end generated code: output=7f7ef57bc7dd1d1c input=8a774b02d1dc81f3]*/ { return PyLong_FromLong(RAND_status()); } -PyDoc_STRVAR(PySSL_RAND_status_doc, -"RAND_status() -> 0 or 1\n\ -\n\ -Returns 1 if the OpenSSL PRNG has been seeded with enough data and 0 if not.\n\ -It is necessary to seed the PRNG with RAND_add() on some platforms before\n\ -using the ssl() function."); - #ifdef HAVE_RAND_EGD -static PyObject * -PySSL_RAND_egd(PyObject *self, PyObject *args) -{ - PyObject *path; - int bytes; +/*[clinic input] +_ssl.RAND_egd + path: object(converter="PyUnicode_FSConverter") + / - if (!PyArg_ParseTuple(args, "O&:RAND_egd", - PyUnicode_FSConverter, &path)) - return NULL; +Queries the entropy gather daemon (EGD) on the socket named by 'path'. + +Returns number of bytes read. Raises SSLError if connection to EGD +fails or if it does not provide enough data to seed PRNG. +[clinic start generated code]*/ - bytes = RAND_egd(PyBytes_AsString(path)); +static PyObject * +_ssl_RAND_egd_impl(PyModuleDef *module, PyObject *path) +/*[clinic end generated code: output=8e728e501e28541b input=1aeb7eb948312195]*/ +{ + int bytes = RAND_egd(PyBytes_AsString(path)); Py_DECREF(path); if (bytes == -1) { PyErr_SetString(PySSLErrorObject, @@ -3345,27 +3942,21 @@ PySSL_RAND_egd(PyObject *self, PyObject *args) } return PyLong_FromLong(bytes); } - -PyDoc_STRVAR(PySSL_RAND_egd_doc, -"RAND_egd(path) -> bytes\n\ -\n\ -Queries the entropy gather daemon (EGD) on the socket named by 'path'.\n\ -Returns number of bytes read. Raises SSLError if connection to EGD\n\ -fails or if it does not provide enough data to seed PRNG."); #endif /* HAVE_RAND_EGD */ -#endif /* HAVE_OPENSSL_RAND */ -PyDoc_STRVAR(PySSL_get_default_verify_paths_doc, -"get_default_verify_paths() -> tuple\n\ -\n\ -Return search paths and environment vars that are used by SSLContext's\n\ -set_default_verify_paths() to load default CAs. The values are\n\ -'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'."); +/*[clinic input] +_ssl.get_default_verify_paths + +Return search paths and environment vars that are used by SSLContext's set_default_verify_paths() to load default CAs. + +The values are 'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'. +[clinic start generated code]*/ static PyObject * -PySSL_get_default_verify_paths(PyObject *self) +_ssl_get_default_verify_paths_impl(PyModuleDef *module) +/*[clinic end generated code: output=5a2820ce7e3304d3 input=5210c953d98c3eb5]*/ { PyObject *ofile_env = NULL; PyObject *ofile = NULL; @@ -3424,26 +4015,24 @@ asn1obj2py(ASN1_OBJECT *obj) } } -PyDoc_STRVAR(PySSL_txt2obj_doc, -"txt2obj(txt, name=False) -> (nid, shortname, longname, oid)\n\ -\n\ -Lookup NID, short name, long name and OID of an ASN1_OBJECT. By default\n\ -objects are looked up by OID. With name=True short and long name are also\n\ -matched."); +/*[clinic input] +_ssl.txt2obj + txt: str + name: bool = False -static PyObject* -PySSL_txt2obj(PyObject *self, PyObject *args, PyObject *kwds) +Lookup NID, short name, long name and OID of an ASN1_OBJECT. + +By default objects are looked up by OID. With name=True short and +long name are also matched. +[clinic start generated code]*/ + +static PyObject * +_ssl_txt2obj_impl(PyModuleDef *module, const char *txt, int name) +/*[clinic end generated code: output=2ae2c30531b8809f input=1c1e7d0aa7c48602]*/ { - char *kwlist[] = {"txt", "name", NULL}; PyObject *result = NULL; - char *txt; - int name = 0; ASN1_OBJECT *obj; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|p:txt2obj", - kwlist, &txt, &name)) { - return NULL; - } obj = OBJ_txt2obj(txt, name ? 0 : 1); if (obj == NULL) { PyErr_Format(PyExc_ValueError, "unknown object '%.100s'", txt); @@ -3454,21 +4043,21 @@ PySSL_txt2obj(PyObject *self, PyObject *args, PyObject *kwds) return result; } -PyDoc_STRVAR(PySSL_nid2obj_doc, -"nid2obj(nid) -> (nid, shortname, longname, oid)\n\ -\n\ -Lookup NID, short name, long name and OID of an ASN1_OBJECT by NID."); +/*[clinic input] +_ssl.nid2obj + nid: int + / -static PyObject* -PySSL_nid2obj(PyObject *self, PyObject *args) +Lookup NID, short name, long name and OID of an ASN1_OBJECT by NID. +[clinic start generated code]*/ + +static PyObject * +_ssl_nid2obj_impl(PyModuleDef *module, int nid) +/*[clinic end generated code: output=8db1df89e44badb8 input=51787a3bee7d8f98]*/ { PyObject *result = NULL; - int nid; ASN1_OBJECT *obj; - if (!PyArg_ParseTuple(args, "i:nid2obj", &nid)) { - return NULL; - } if (nid < NID_undef) { PyErr_SetString(PyExc_ValueError, "NID must be positive."); return NULL; @@ -3568,30 +4157,28 @@ parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags) return retval; } -PyDoc_STRVAR(PySSL_enum_certificates_doc, -"enum_certificates(store_name) -> []\n\ -\n\ -Retrieve certificates from Windows' cert store. store_name may be one of\n\ -'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\ -The function returns a list of (bytes, encoding_type, trust) tuples. The\n\ -encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\ -PKCS_7_ASN_ENCODING. The trust setting is either a set of OIDs or the\n\ -boolean True."); +/*[clinic input] +_ssl.enum_certificates + store_name: str + +Retrieve certificates from Windows' cert store. + +store_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide +more cert storages, too. The function returns a list of (bytes, +encoding_type, trust) tuples. The encoding_type flag can be interpreted +with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either +a set of OIDs or the boolean True. +[clinic start generated code]*/ static PyObject * -PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds) +_ssl_enum_certificates_impl(PyModuleDef *module, const char *store_name) +/*[clinic end generated code: output=cc4ebc10b8adacfc input=915f60d70461ea4e]*/ { - char *kwlist[] = {"store_name", NULL}; - char *store_name; HCERTSTORE hStore = NULL; PCCERT_CONTEXT pCertCtx = NULL; PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL; PyObject *result = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:enum_certificates", - kwlist, &store_name)) { - return NULL; - } result = PyList_New(0); if (result == NULL) { return NULL; @@ -3657,29 +4244,27 @@ PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds) return result; } -PyDoc_STRVAR(PySSL_enum_crls_doc, -"enum_crls(store_name) -> []\n\ -\n\ -Retrieve CRLs from Windows' cert store. store_name may be one of\n\ -'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\ -The function returns a list of (bytes, encoding_type) tuples. The\n\ -encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\ -PKCS_7_ASN_ENCODING."); +/*[clinic input] +_ssl.enum_crls + store_name: str + +Retrieve CRLs from Windows' cert store. + +store_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide +more cert storages, too. The function returns a list of (bytes, +encoding_type) tuples. The encoding_type flag can be interpreted with +X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. +[clinic start generated code]*/ static PyObject * -PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds) +_ssl_enum_crls_impl(PyModuleDef *module, const char *store_name) +/*[clinic end generated code: output=763490a2aa1c50d5 input=a1f1d7629f1c5d3d]*/ { - char *kwlist[] = {"store_name", NULL}; - char *store_name; HCERTSTORE hStore = NULL; PCCRL_CONTEXT pCrlCtx = NULL; PyObject *crl = NULL, *enc = NULL, *tup = NULL; PyObject *result = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:enum_crls", - kwlist, &store_name)) { - return NULL; - } result = PyList_New(0); if (result == NULL) { return NULL; @@ -3737,36 +4322,18 @@ PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds) #endif /* _MSC_VER */ /* List of functions exported by this module. */ - static PyMethodDef PySSL_methods[] = { - {"_test_decode_cert", PySSL_test_decode_certificate, - METH_VARARGS}, -#ifdef HAVE_OPENSSL_RAND - {"RAND_add", PySSL_RAND_add, METH_VARARGS, - PySSL_RAND_add_doc}, - {"RAND_bytes", PySSL_RAND_bytes, METH_VARARGS, - PySSL_RAND_bytes_doc}, - {"RAND_pseudo_bytes", PySSL_RAND_pseudo_bytes, METH_VARARGS, - PySSL_RAND_pseudo_bytes_doc}, -#ifdef HAVE_RAND_EGD - {"RAND_egd", PySSL_RAND_egd, METH_VARARGS, - PySSL_RAND_egd_doc}, -#endif - {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, - PySSL_RAND_status_doc}, -#endif - {"get_default_verify_paths", (PyCFunction)PySSL_get_default_verify_paths, - METH_NOARGS, PySSL_get_default_verify_paths_doc}, -#ifdef _MSC_VER - {"enum_certificates", (PyCFunction)PySSL_enum_certificates, - METH_VARARGS | METH_KEYWORDS, PySSL_enum_certificates_doc}, - {"enum_crls", (PyCFunction)PySSL_enum_crls, - METH_VARARGS | METH_KEYWORDS, PySSL_enum_crls_doc}, -#endif - {"txt2obj", (PyCFunction)PySSL_txt2obj, - METH_VARARGS | METH_KEYWORDS, PySSL_txt2obj_doc}, - {"nid2obj", (PyCFunction)PySSL_nid2obj, - METH_VARARGS, PySSL_nid2obj_doc}, + _SSL__TEST_DECODE_CERT_METHODDEF + _SSL_RAND_ADD_METHODDEF + _SSL_RAND_BYTES_METHODDEF + _SSL_RAND_PSEUDO_BYTES_METHODDEF + _SSL_RAND_EGD_METHODDEF + _SSL_RAND_STATUS_METHODDEF + _SSL_GET_DEFAULT_VERIFY_PATHS_METHODDEF + _SSL_ENUM_CERTIFICATES_METHODDEF + _SSL_ENUM_CRLS_METHODDEF + _SSL_TXT2OBJ_METHODDEF + _SSL_NID2OBJ_METHODDEF {NULL, NULL} /* Sentinel */ }; @@ -3906,6 +4473,8 @@ PyInit__ssl(void) return NULL; if (PyType_Ready(&PySSLSocket_Type) < 0) return NULL; + if (PyType_Ready(&PySSLMemoryBIO_Type) < 0) + return NULL; m = PyModule_Create(&_sslmodule); if (m == NULL) @@ -3969,6 +4538,9 @@ PyInit__ssl(void) if (PyDict_SetItemString(d, "_SSLSocket", (PyObject *)&PySSLSocket_Type) != 0) return NULL; + if (PyDict_SetItemString(d, "MemoryBIO", + (PyObject *)&PySSLMemoryBIO_Type) != 0) + return NULL; PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", PY_SSL_ERROR_ZERO_RETURN); PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", @@ -4109,11 +4681,7 @@ PyInit__ssl(void) Py_INCREF(r); PyModule_AddObject(m, "HAS_SNI", r); -#if HAVE_OPENSSL_FINISHED r = Py_True; -#else - r = Py_False; -#endif Py_INCREF(r); PyModule_AddObject(m, "HAS_TLS_UNIQUE", r); @@ -4133,6 +4701,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(); |