summaryrefslogtreecommitdiff
path: root/Modules/_ssl.c
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@microsoft.com>2017-02-04 15:05:40 -0800
committerSteve Dower <steve.dower@microsoft.com>2017-02-04 15:05:40 -0800
commitb2fa705fd3887c326e811c418469c784353027f4 (patch)
treeb3428f73de91453edbfd4df1a5d4a212d182eb44 /Modules/_ssl.c
parent134e58fd3aaa2e91390041e143f3f0a21a60142b (diff)
parentb53654b6dbfce8318a7d4d1cdaddca7a7fec194b (diff)
downloadcpython-b2fa705fd3887c326e811c418469c784353027f4.tar.gz
Issue #29392: Prevent crash when passing invalid arguments into msvcrt module.
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r--Modules/_ssl.c601
1 files changed, 574 insertions, 27 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index a79c3a8886..b198857060 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -140,6 +140,8 @@ struct py_ssl_library_code {
#endif
#define TLS_method SSLv23_method
+#define TLS_client_method SSLv23_client_method
+#define TLS_server_method SSLv23_server_method
static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne)
{
@@ -147,10 +149,12 @@ static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne)
}
#ifndef OPENSSL_NO_COMP
+/* LCOV_EXCL_START */
static int COMP_get_type(const COMP_METHOD *meth)
{
return meth->type;
}
+/* LCOV_EXCL_STOP */
#endif
static pem_password_cb *SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx)
@@ -187,6 +191,19 @@ static X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store)
{
return store->param;
}
+
+static int
+SSL_SESSION_has_ticket(const SSL_SESSION *s)
+{
+ return (s->tlsext_ticklen > 0) ? 1 : 0;
+}
+
+static unsigned long
+SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s)
+{
+ return s->tlsext_tick_lifetime_hint;
+}
+
#endif /* OpenSSL < 1.1.0 or LibreSSL */
@@ -220,14 +237,16 @@ enum py_ssl_cert_requirements {
enum py_ssl_version {
PY_SSL_VERSION_SSL2,
PY_SSL_VERSION_SSL3=1,
- PY_SSL_VERSION_TLS,
+ PY_SSL_VERSION_TLS, /* SSLv23 */
#if HAVE_TLSv1_2
PY_SSL_VERSION_TLS1,
PY_SSL_VERSION_TLS1_1,
- PY_SSL_VERSION_TLS1_2
+ PY_SSL_VERSION_TLS1_2,
#else
- PY_SSL_VERSION_TLS1
+ PY_SSL_VERSION_TLS1,
#endif
+ PY_SSL_VERSION_TLS_CLIENT=0x10,
+ PY_SSL_VERSION_TLS_SERVER,
};
#ifdef WITH_THREAD
@@ -293,25 +312,35 @@ typedef struct {
int eof_written;
} PySSLMemoryBIO;
+typedef struct {
+ PyObject_HEAD
+ SSL_SESSION *session;
+ PySSLContext *ctx;
+} PySSLSession;
+
static PyTypeObject PySSLContext_Type;
static PyTypeObject PySSLSocket_Type;
static PyTypeObject PySSLMemoryBIO_Type;
+static PyTypeObject PySSLSession_Type;
/*[clinic input]
module _ssl
class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type"
class _ssl._SSLSocket "PySSLSocket *" "&PySSLSocket_Type"
class _ssl.MemoryBIO "PySSLMemoryBIO *" "&PySSLMemoryBIO_Type"
+class _ssl.SSLSession "PySSLSession *" "&PySSLSession_Type"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7bf7cb832638e2e1]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdc67fafeeaa8109]*/
#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)
+#define PySSLSession_Check(v) (Py_TYPE(v) == &PySSLSession_Type)
typedef enum {
SOCKET_IS_NONBLOCKING,
@@ -455,7 +484,7 @@ fail:
}
static PyObject *
-PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
+PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
{
PyObject *type = PySSLErrorObject;
char *errstr = NULL;
@@ -537,7 +566,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
}
static PyObject *
-_setSSLError (char *errstr, int errcode, char *filename, int lineno) {
+_setSSLError (const char *errstr, int errcode, const char *filename, int lineno) {
if (errstr == NULL)
errcode = ERR_peek_last_error();
@@ -1547,6 +1576,65 @@ cipher_to_tuple(const SSL_CIPHER *cipher)
return NULL;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
+static PyObject *
+cipher_to_dict(const SSL_CIPHER *cipher)
+{
+ const char *cipher_name, *cipher_protocol;
+
+ unsigned long cipher_id;
+ int alg_bits, strength_bits, len;
+ char buf[512] = {0};
+#if OPENSSL_VERSION_1_1
+ int aead, nid;
+ const char *skcipher = NULL, *digest = NULL, *kx = NULL, *auth = NULL;
+#endif
+
+ /* can be NULL */
+ cipher_name = SSL_CIPHER_get_name(cipher);
+ cipher_protocol = SSL_CIPHER_get_version(cipher);
+ cipher_id = SSL_CIPHER_get_id(cipher);
+ SSL_CIPHER_description(cipher, buf, sizeof(buf) - 1);
+ len = strlen(buf);
+ if (len > 1 && buf[len-1] == '\n')
+ buf[len-1] = '\0';
+ strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
+
+#if OPENSSL_VERSION_1_1
+ aead = SSL_CIPHER_is_aead(cipher);
+ nid = SSL_CIPHER_get_cipher_nid(cipher);
+ skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
+ nid = SSL_CIPHER_get_digest_nid(cipher);
+ digest = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
+ nid = SSL_CIPHER_get_kx_nid(cipher);
+ kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
+ nid = SSL_CIPHER_get_auth_nid(cipher);
+ auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
+#endif
+
+ return Py_BuildValue(
+ "{sksssssssisi"
+#if OPENSSL_VERSION_1_1
+ "sOssssssss"
+#endif
+ "}",
+ "id", cipher_id,
+ "name", cipher_name,
+ "protocol", cipher_protocol,
+ "description", buf,
+ "strength_bits", strength_bits,
+ "alg_bits", alg_bits
+#if OPENSSL_VERSION_1_1
+ ,"aead", aead ? Py_True : Py_False,
+ "symmetric", skcipher,
+ "digest", digest,
+ "kea", kx,
+ "auth", auth
+#endif
+ );
+}
+#endif
+
/*[clinic input]
_ssl._SSLSocket.shared_ciphers
[clinic start generated code]*/
@@ -2255,6 +2343,152 @@ _ssl__SSLSocket_tls_unique_cb_impl(PySSLSocket *self)
return retval;
}
+#ifdef OPENSSL_VERSION_1_1
+
+static SSL_SESSION*
+_ssl_session_dup(SSL_SESSION *session) {
+ SSL_SESSION *newsession = NULL;
+ int slen;
+ unsigned char *senc = NULL, *p;
+ const unsigned char *const_p;
+
+ if (session == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Invalid session");
+ goto error;
+ }
+
+ /* get length */
+ slen = i2d_SSL_SESSION(session, NULL);
+ if (slen == 0 || slen > 0xFF00) {
+ PyErr_SetString(PyExc_ValueError, "i2d() failed.");
+ goto error;
+ }
+ if ((senc = PyMem_Malloc(slen)) == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ p = senc;
+ if (!i2d_SSL_SESSION(session, &p)) {
+ PyErr_SetString(PyExc_ValueError, "i2d() failed.");
+ goto error;
+ }
+ const_p = senc;
+ newsession = d2i_SSL_SESSION(NULL, &const_p, slen);
+ if (session == NULL) {
+ goto error;
+ }
+ PyMem_Free(senc);
+ return newsession;
+ error:
+ if (senc != NULL) {
+ PyMem_Free(senc);
+ }
+ return NULL;
+}
+#endif
+
+static PyObject *
+PySSL_get_session(PySSLSocket *self, void *closure) {
+ /* get_session can return sessions from a server-side connection,
+ * it does not check for handshake done or client socket. */
+ PySSLSession *pysess;
+ SSL_SESSION *session;
+
+#ifdef OPENSSL_VERSION_1_1
+ /* duplicate session as workaround for session bug in OpenSSL 1.1.0,
+ * https://github.com/openssl/openssl/issues/1550 */
+ session = SSL_get0_session(self->ssl); /* borrowed reference */
+ if (session == NULL) {
+ Py_RETURN_NONE;
+ }
+ if ((session = _ssl_session_dup(session)) == NULL) {
+ return NULL;
+ }
+#else
+ session = SSL_get1_session(self->ssl);
+ if (session == NULL) {
+ Py_RETURN_NONE;
+ }
+#endif
+ pysess = PyObject_GC_New(PySSLSession, &PySSLSession_Type);
+ if (pysess == NULL) {
+ SSL_SESSION_free(session);
+ return NULL;
+ }
+
+ assert(self->ctx);
+ pysess->ctx = self->ctx;
+ Py_INCREF(pysess->ctx);
+ pysess->session = session;
+ PyObject_GC_Track(pysess);
+ return (PyObject *)pysess;
+}
+
+static int PySSL_set_session(PySSLSocket *self, PyObject *value,
+ void *closure)
+ {
+ PySSLSession *pysess;
+#ifdef OPENSSL_VERSION_1_1
+ SSL_SESSION *session;
+#endif
+ int result;
+
+ if (!PySSLSession_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "Value is not a SSLSession.");
+ return -1;
+ }
+ pysess = (PySSLSession *)value;
+
+ if (self->ctx->ctx != pysess->ctx->ctx) {
+ PyErr_SetString(PyExc_ValueError,
+ "Session refers to a different SSLContext.");
+ return -1;
+ }
+ if (self->socket_type != PY_SSL_CLIENT) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot set session for server-side SSLSocket.");
+ return -1;
+ }
+ if (self->handshake_done) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot set session after handshake.");
+ return -1;
+ }
+#ifdef OPENSSL_VERSION_1_1
+ /* duplicate session */
+ if ((session = _ssl_session_dup(pysess->session)) == NULL) {
+ return -1;
+ }
+ result = SSL_set_session(self->ssl, session);
+ /* free duplicate, SSL_set_session() bumps ref count */
+ SSL_SESSION_free(session);
+#else
+ result = SSL_set_session(self->ssl, pysess->session);
+#endif
+ if (result == 0) {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ return -1;
+ }
+ return 0;
+}
+
+PyDoc_STRVAR(PySSL_set_session_doc,
+"_setter_session(session)\n\
+\
+Get / set SSLSession.");
+
+static PyObject *
+PySSL_get_session_reused(PySSLSocket *self, void *closure) {
+ if (SSL_session_reused(self->ssl)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+PyDoc_STRVAR(PySSL_get_session_reused_doc,
+"Was the client session reused during handshake?");
+
static PyGetSetDef ssl_getsetlist[] = {
{"context", (getter) PySSL_get_context,
(setter) PySSL_set_context, PySSL_set_context_doc},
@@ -2264,6 +2498,10 @@ static PyGetSetDef ssl_getsetlist[] = {
PySSL_get_server_hostname_doc},
{"owner", (getter) PySSL_get_owner, (setter) PySSL_set_owner,
PySSL_get_owner_doc},
+ {"session", (getter) PySSL_get_session,
+ (setter) PySSL_set_session, PySSL_set_session_doc},
+ {"session_reused", (getter) PySSL_get_session_reused, NULL,
+ PySSL_get_session_reused_doc},
{NULL}, /* sentinel */
};
@@ -2323,6 +2561,33 @@ static PyTypeObject PySSLSocket_Type = {
* _SSLContext objects
*/
+static int
+_set_verify_mode(SSL_CTX *ctx, enum py_ssl_cert_requirements n)
+{
+ int mode;
+ int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
+
+ switch(n) {
+ case PY_SSL_CERT_NONE:
+ mode = SSL_VERIFY_NONE;
+ break;
+ case PY_SSL_CERT_OPTIONAL:
+ mode = SSL_VERIFY_PEER;
+ break;
+ case PY_SSL_CERT_REQUIRED:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError,
+ "invalid value for verify_mode");
+ return -1;
+ }
+ /* keep current verify cb */
+ verify_cb = SSL_CTX_get_verify_callback(ctx);
+ SSL_CTX_set_verify(ctx, mode, verify_cb);
+ return 0;
+}
+
/*[clinic input]
@classmethod
_ssl._SSLContext.__new__
@@ -2337,6 +2602,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
PySSLContext *self;
long options;
SSL_CTX *ctx = NULL;
+ int result;
#if defined(SSL_MODE_RELEASE_BUFFERS)
unsigned long libver;
#endif
@@ -2358,8 +2624,12 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
else if (proto_version == PY_SSL_VERSION_SSL2)
ctx = SSL_CTX_new(SSLv2_method());
#endif
- else if (proto_version == PY_SSL_VERSION_TLS)
+ else if (proto_version == PY_SSL_VERSION_TLS) /* SSLv23 */
ctx = SSL_CTX_new(TLS_method());
+ else if (proto_version == PY_SSL_VERSION_TLS_CLIENT)
+ ctx = SSL_CTX_new(TLS_client_method());
+ else if (proto_version == PY_SSL_VERSION_TLS_SERVER)
+ ctx = SSL_CTX_new(TLS_server_method());
else
proto_version = -1;
PySSL_END_ALLOW_THREADS
@@ -2392,16 +2662,57 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
self->set_hostname = NULL;
#endif
/* Don't check host name by default */
- self->check_hostname = 0;
+ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
+ self->check_hostname = 1;
+ if (_set_verify_mode(self->ctx, PY_SSL_CERT_REQUIRED) == -1) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ } else {
+ self->check_hostname = 0;
+ if (_set_verify_mode(self->ctx, PY_SSL_CERT_NONE) == -1) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ }
/* Defaults */
- SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
options = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
if (proto_version != PY_SSL_VERSION_SSL2)
options |= SSL_OP_NO_SSLv2;
if (proto_version != PY_SSL_VERSION_SSL3)
options |= SSL_OP_NO_SSLv3;
+ /* Minimal security flags for server and client side context.
+ * Client sockets ignore server-side parameters. */
+#ifdef SSL_OP_NO_COMPRESSION
+ options |= SSL_OP_NO_COMPRESSION;
+#endif
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+ options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+#endif
+#ifdef SSL_OP_SINGLE_DH_USE
+ options |= SSL_OP_SINGLE_DH_USE;
+#endif
+#ifdef SSL_OP_SINGLE_ECDH_USE
+ options |= SSL_OP_SINGLE_ECDH_USE;
+#endif
SSL_CTX_set_options(self->ctx, options);
+ /* A bare minimum cipher list without completly broken cipher suites.
+ * It's far from perfect but gives users a better head start. */
+ if (proto_version != PY_SSL_VERSION_SSL2) {
+ result = SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!eNULL:!MD5");
+ } else {
+ /* SSLv2 needs MD5 */
+ result = SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!eNULL");
+ }
+ if (result == 0) {
+ Py_DECREF(self);
+ ERR_clear_error();
+ PyErr_SetString(PySSLErrorObject,
+ "No cipher can be selected.");
+ return NULL;
+ }
+
#if defined(SSL_MODE_RELEASE_BUFFERS)
/* Set SSL_MODE_RELEASE_BUFFERS. This potentially greatly reduces memory
usage for no cost at all. However, don't do this for OpenSSL versions
@@ -2506,6 +2817,52 @@ _ssl__SSLContext_set_ciphers_impl(PySSLContext *self, const char *cipherlist)
Py_RETURN_NONE;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
+/*[clinic input]
+_ssl._SSLContext.get_ciphers
+[clinic start generated code]*/
+
+static PyObject *
+_ssl__SSLContext_get_ciphers_impl(PySSLContext *self)
+/*[clinic end generated code: output=a56e4d68a406dfc4 input=a2aadc9af89b79c5]*/
+{
+ SSL *ssl = NULL;
+ STACK_OF(SSL_CIPHER) *sk = NULL;
+ const SSL_CIPHER *cipher;
+ int i=0;
+ PyObject *result = NULL, *dct;
+
+ ssl = SSL_new(self->ctx);
+ if (ssl == NULL) {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ goto exit;
+ }
+ sk = SSL_get_ciphers(ssl);
+
+ result = PyList_New(sk_SSL_CIPHER_num(sk));
+ if (result == NULL) {
+ goto exit;
+ }
+
+ for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
+ cipher = sk_SSL_CIPHER_value(sk, i);
+ dct = cipher_to_dict(cipher);
+ if (dct == NULL) {
+ Py_CLEAR(result);
+ goto exit;
+ }
+ PyList_SET_ITEM(result, i, dct);
+ }
+
+ exit:
+ if (ssl != NULL)
+ SSL_free(ssl);
+ return result;
+
+}
+#endif
+
+
#ifdef OPENSSL_NPN_NEGOTIATED
static int
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
@@ -2662,28 +3019,16 @@ get_verify_mode(PySSLContext *self, void *c)
static int
set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
{
- int n, mode;
+ int n;
if (!PyArg_Parse(arg, "i", &n))
return -1;
- if (n == PY_SSL_CERT_NONE)
- mode = SSL_VERIFY_NONE;
- else if (n == PY_SSL_CERT_OPTIONAL)
- mode = SSL_VERIFY_PEER;
- else if (n == PY_SSL_CERT_REQUIRED)
- mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- else {
- PyErr_SetString(PyExc_ValueError,
- "invalid value for verify_mode");
- return -1;
- }
- if (mode == SSL_VERIFY_NONE && self->check_hostname) {
+ if (n == PY_SSL_CERT_NONE && self->check_hostname) {
PyErr_SetString(PyExc_ValueError,
"Cannot set verify_mode to CERT_NONE when "
"check_hostname is enabled.");
return -1;
}
- SSL_CTX_set_verify(self->ctx, mode, NULL);
- return 0;
+ return _set_verify_mode(self->ctx, n);
}
static PyObject *
@@ -3673,6 +4018,7 @@ static struct PyMethodDef context_methods[] = {
_SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
+ _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
{NULL, NULL} /* sentinel */
};
@@ -3938,6 +4284,194 @@ static PyTypeObject PySSLMemoryBIO_Type = {
};
+/*
+ * SSL Session object
+ */
+
+static void
+PySSLSession_dealloc(PySSLSession *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_XDECREF(self->ctx);
+ if (self->session != NULL) {
+ SSL_SESSION_free(self->session);
+ }
+ PyObject_GC_Del(self);
+}
+
+static PyObject *
+PySSLSession_richcompare(PyObject *left, PyObject *right, int op)
+{
+ int result;
+
+ if (left == NULL || right == NULL) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ if (!PySSLSession_Check(left) || !PySSLSession_Check(right)) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+
+ if (left == right) {
+ result = 0;
+ } else {
+ const unsigned char *left_id, *right_id;
+ unsigned int left_len, right_len;
+ left_id = SSL_SESSION_get_id(((PySSLSession *)left)->session,
+ &left_len);
+ right_id = SSL_SESSION_get_id(((PySSLSession *)right)->session,
+ &right_len);
+ if (left_len == right_len) {
+ result = memcmp(left_id, right_id, left_len);
+ } else {
+ result = 1;
+ }
+ }
+
+ switch (op) {
+ case Py_EQ:
+ if (result == 0) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ break;
+ case Py_NE:
+ if (result != 0) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ break;
+ case Py_LT:
+ case Py_LE:
+ case Py_GT:
+ case Py_GE:
+ Py_RETURN_NOTIMPLEMENTED;
+ break;
+ default:
+ PyErr_BadArgument();
+ return NULL;
+ }
+}
+
+static int
+PySSLSession_traverse(PySSLSession *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->ctx);
+ return 0;
+}
+
+static int
+PySSLSession_clear(PySSLSession *self)
+{
+ Py_CLEAR(self->ctx);
+ return 0;
+}
+
+
+static PyObject *
+PySSLSession_get_time(PySSLSession *self, void *closure) {
+ return PyLong_FromLong(SSL_SESSION_get_time(self->session));
+}
+
+PyDoc_STRVAR(PySSLSession_get_time_doc,
+"Session creation time (seconds since epoch).");
+
+
+static PyObject *
+PySSLSession_get_timeout(PySSLSession *self, void *closure) {
+ return PyLong_FromLong(SSL_SESSION_get_timeout(self->session));
+}
+
+PyDoc_STRVAR(PySSLSession_get_timeout_doc,
+"Session timeout (delta in seconds).");
+
+
+static PyObject *
+PySSLSession_get_ticket_lifetime_hint(PySSLSession *self, void *closure) {
+ unsigned long hint = SSL_SESSION_get_ticket_lifetime_hint(self->session);
+ return PyLong_FromUnsignedLong(hint);
+}
+
+PyDoc_STRVAR(PySSLSession_get_ticket_lifetime_hint_doc,
+"Ticket life time hint.");
+
+
+static PyObject *
+PySSLSession_get_session_id(PySSLSession *self, void *closure) {
+ const unsigned char *id;
+ unsigned int len;
+ id = SSL_SESSION_get_id(self->session, &len);
+ return PyBytes_FromStringAndSize((const char *)id, len);
+}
+
+PyDoc_STRVAR(PySSLSession_get_session_id_doc,
+"Session id");
+
+
+static PyObject *
+PySSLSession_get_has_ticket(PySSLSession *self, void *closure) {
+ if (SSL_SESSION_has_ticket(self->session)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+PyDoc_STRVAR(PySSLSession_get_has_ticket_doc,
+"Does the session contain a ticket?");
+
+
+static PyGetSetDef PySSLSession_getsetlist[] = {
+ {"has_ticket", (getter) PySSLSession_get_has_ticket, NULL,
+ PySSLSession_get_has_ticket_doc},
+ {"id", (getter) PySSLSession_get_session_id, NULL,
+ PySSLSession_get_session_id_doc},
+ {"ticket_lifetime_hint", (getter) PySSLSession_get_ticket_lifetime_hint,
+ NULL, PySSLSession_get_ticket_lifetime_hint_doc},
+ {"time", (getter) PySSLSession_get_time, NULL,
+ PySSLSession_get_time_doc},
+ {"timeout", (getter) PySSLSession_get_timeout, NULL,
+ PySSLSession_get_timeout_doc},
+ {NULL}, /* sentinel */
+};
+
+static PyTypeObject PySSLSession_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_ssl.Session", /*tp_name*/
+ sizeof(PySSLSession), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PySSLSession_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 | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ 0, /*tp_doc*/
+ (traverseproc)PySSLSession_traverse, /*tp_traverse*/
+ (inquiry)PySSLSession_clear, /*tp_clear*/
+ PySSLSession_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ PySSLSession_getsetlist, /*tp_getset*/
+};
+
+
/* helper routines for seeding the SSL PRNG */
/*[clinic input]
_ssl.RAND_add
@@ -4059,6 +4593,7 @@ _ssl_RAND_status_impl(PyObject *module)
}
#ifndef OPENSSL_NO_EGD
+/* LCOV_EXCL_START */
/*[clinic input]
_ssl.RAND_egd
path: object(converter="PyUnicode_FSConverter")
@@ -4084,6 +4619,7 @@ _ssl_RAND_egd_impl(PyObject *module, PyObject *path)
}
return PyLong_FromLong(bytes);
}
+/* LCOV_EXCL_STOP */
#endif /* OPENSSL_NO_EGD */
@@ -4542,13 +5078,12 @@ static int _setup_ssl_threads(void) {
if (_ssl_locks == NULL) {
_ssl_locks_count = CRYPTO_num_locks();
- _ssl_locks = PyMem_New(PyThread_type_lock, _ssl_locks_count);
+ _ssl_locks = PyMem_Calloc(_ssl_locks_count,
+ sizeof(PyThread_type_lock));
if (_ssl_locks == NULL) {
PyErr_NoMemory();
return 0;
}
- memset(_ssl_locks, 0,
- sizeof(PyThread_type_lock) * _ssl_locks_count);
for (i = 0; i < _ssl_locks_count; i++) {
_ssl_locks[i] = PyThread_allocate_lock();
if (_ssl_locks[i] == NULL) {
@@ -4623,6 +5158,9 @@ PyInit__ssl(void)
return NULL;
if (PyType_Ready(&PySSLMemoryBIO_Type) < 0)
return NULL;
+ if (PyType_Ready(&PySSLSession_Type) < 0)
+ return NULL;
+
m = PyModule_Create(&_sslmodule);
if (m == NULL)
@@ -4694,6 +5232,10 @@ PyInit__ssl(void)
if (PyDict_SetItemString(d, "MemoryBIO",
(PyObject *)&PySSLMemoryBIO_Type) != 0)
return NULL;
+ if (PyDict_SetItemString(d, "SSLSession",
+ (PyObject *)&PySSLSession_Type) != 0)
+ return NULL;
+
PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN",
PY_SSL_ERROR_ZERO_RETURN);
PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ",
@@ -4798,6 +5340,10 @@ PyInit__ssl(void)
PY_SSL_VERSION_TLS);
PyModule_AddIntConstant(m, "PROTOCOL_TLS",
PY_SSL_VERSION_TLS);
+ PyModule_AddIntConstant(m, "PROTOCOL_TLS_CLIENT",
+ PY_SSL_VERSION_TLS_CLIENT);
+ PyModule_AddIntConstant(m, "PROTOCOL_TLS_SERVER",
+ PY_SSL_VERSION_TLS_SERVER);
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
PY_SSL_VERSION_TLS1);
#if HAVE_TLSv1_2
@@ -4820,6 +5366,7 @@ PyInit__ssl(void)
PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
SSL_OP_CIPHER_SERVER_PREFERENCE);
PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE);
+ PyModule_AddIntConstant(m, "OP_NO_TICKET", SSL_OP_NO_TICKET);
#ifdef SSL_OP_SINGLE_ECDH_USE
PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
#endif