summaryrefslogtreecommitdiffstats
path: root/Modules/_ssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_ssl.c')
-rw-r--r--Modules/_ssl.c822
1 files changed, 748 insertions, 74 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 0a84118..1104a4e 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -18,16 +18,21 @@
#ifdef WITH_THREAD
#include "pythread.h"
+#define PySSL_BEGIN_ALLOW_THREADS_S(save) \
+ do { if (_ssl_locks_count>0) { (save) = PyEval_SaveThread(); } } while (0)
+#define PySSL_END_ALLOW_THREADS_S(save) \
+ do { if (_ssl_locks_count>0) { PyEval_RestoreThread(save); } } while (0)
#define PySSL_BEGIN_ALLOW_THREADS { \
PyThreadState *_save = NULL; \
- if (_ssl_locks_count>0) {_save = PyEval_SaveThread();}
-#define PySSL_BLOCK_THREADS if (_ssl_locks_count>0){PyEval_RestoreThread(_save)};
-#define PySSL_UNBLOCK_THREADS if (_ssl_locks_count>0){_save = PyEval_SaveThread()};
-#define PySSL_END_ALLOW_THREADS if (_ssl_locks_count>0){PyEval_RestoreThread(_save);} \
- }
+ PySSL_BEGIN_ALLOW_THREADS_S(_save);
+#define PySSL_BLOCK_THREADS PySSL_END_ALLOW_THREADS_S(_save);
+#define PySSL_UNBLOCK_THREADS PySSL_BEGIN_ALLOW_THREADS_S(_save);
+#define PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS_S(_save); }
#else /* no WITH_THREAD */
+#define PySSL_BEGIN_ALLOW_THREADS_S(save)
+#define PySSL_END_ALLOW_THREADS_S(save)
#define PySSL_BEGIN_ALLOW_THREADS
#define PySSL_BLOCK_THREADS
#define PySSL_UNBLOCK_THREADS
@@ -71,6 +76,16 @@ enum py_ssl_version {
PY_SSL_VERSION_TLS1
};
+struct py_ssl_error_code {
+ const char *mnemonic;
+ int library, reason;
+};
+
+struct py_ssl_library_code {
+ const char *library;
+ int code;
+};
+
/* Include symbols from _socket module */
#include "socketmodule.h"
@@ -92,8 +107,21 @@ static PySocketModule_APIObject PySocketModule;
#include "openssl/err.h"
#include "openssl/rand.h"
+/* Include generated data (error codes) */
+#include "_ssl_data.h"
+
/* SSL error object */
static PyObject *PySSLErrorObject;
+static PyObject *PySSLZeroReturnErrorObject;
+static PyObject *PySSLWantReadErrorObject;
+static PyObject *PySSLWantWriteErrorObject;
+static PyObject *PySSLSyscallErrorObject;
+static PyObject *PySSLEOFErrorObject;
+
+/* Error mappings */
+static PyObject *err_codes_to_names;
+static PyObject *err_names_to_codes;
+static PyObject *lib_codes_to_names;
#ifdef WITH_THREAD
@@ -124,9 +152,35 @@ static unsigned int _ssl_locks_count = 0;
# undef HAVE_SSL_CTX_CLEAR_OPTIONS
#endif
+/* In case of 'tls-unique' it will be 12 bytes for TLS, 36 bytes for
+ * 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
+
+
typedef struct {
PyObject_HEAD
SSL_CTX *ctx;
+#ifdef OPENSSL_NPN_NEGOTIATED
+ char *npn_protocols;
+ int npn_protocols_len;
+#endif
} PySSLContext;
typedef struct {
@@ -135,6 +189,7 @@ typedef struct {
SSL *ssl;
X509 *peer_cert;
int shutdown_seen_zero;
+ enum py_ssl_server_or_client socket_type;
} PySSLSocket;
static PyTypeObject PySSLContext_Type;
@@ -165,36 +220,152 @@ typedef enum {
#define ERRSTR1(x,y,z) (x ":" y ": " z)
#define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x)
-/* XXX It might be helpful to augment the error message generated
- below with the name of the SSL function that generated the error.
- I expect it's obvious most of the time.
-*/
+
+/*
+ * SSL errors.
+ */
+
+PyDoc_STRVAR(SSLError_doc,
+"An error occurred in the SSL implementation.");
+
+PyDoc_STRVAR(SSLZeroReturnError_doc,
+"SSL/TLS session closed cleanly.");
+
+PyDoc_STRVAR(SSLWantReadError_doc,
+"Non-blocking SSL socket needs to read more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLWantWriteError_doc,
+"Non-blocking SSL socket needs to write more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLSyscallError_doc,
+"System error when attempting SSL operation.");
+
+PyDoc_STRVAR(SSLEOFError_doc,
+"SSL/TLS connection terminated abruptly.");
+
+static PyObject *
+SSLError_str(PyOSErrorObject *self)
+{
+ if (self->strerror != NULL && PyUnicode_Check(self->strerror)) {
+ Py_INCREF(self->strerror);
+ return self->strerror;
+ }
+ else
+ return PyObject_Str(self->args);
+}
+
+static PyType_Slot sslerror_type_slots[] = {
+ {Py_tp_base, NULL}, /* Filled out in module init as it's not a constant */
+ {Py_tp_doc, SSLError_doc},
+ {Py_tp_str, SSLError_str},
+ {0, 0},
+};
+
+static PyType_Spec sslerror_type_spec = {
+ "ssl.SSLError",
+ sizeof(PyOSErrorObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ sslerror_type_slots
+};
+
+static void
+fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
+ int lineno, unsigned long errcode)
+{
+ PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL;
+ PyObject *init_value, *msg, *key;
+ _Py_IDENTIFIER(reason);
+ _Py_IDENTIFIER(library);
+
+ if (errcode != 0) {
+ int lib, reason;
+
+ lib = ERR_GET_LIB(errcode);
+ reason = ERR_GET_REASON(errcode);
+ key = Py_BuildValue("ii", lib, reason);
+ if (key == NULL)
+ goto fail;
+ reason_obj = PyDict_GetItem(err_codes_to_names, key);
+ Py_DECREF(key);
+ if (reason_obj == NULL) {
+ /* XXX if reason < 100, it might reflect a library number (!!) */
+ PyErr_Clear();
+ }
+ key = PyLong_FromLong(lib);
+ if (key == NULL)
+ goto fail;
+ lib_obj = PyDict_GetItem(lib_codes_to_names, key);
+ Py_DECREF(key);
+ if (lib_obj == NULL) {
+ PyErr_Clear();
+ }
+ if (errstr == NULL)
+ errstr = ERR_reason_error_string(errcode);
+ }
+ if (errstr == NULL)
+ errstr = "unknown error";
+
+ if (reason_obj && lib_obj)
+ msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)",
+ lib_obj, reason_obj, errstr, lineno);
+ else if (lib_obj)
+ msg = PyUnicode_FromFormat("[%S] %s (_ssl.c:%d)",
+ lib_obj, errstr, lineno);
+ else
+ msg = PyUnicode_FromFormat("%s (_ssl.c:%d)", errstr, lineno);
+
+ if (msg == NULL)
+ goto fail;
+ init_value = Py_BuildValue("iN", ssl_errno, msg);
+ err_value = PyObject_CallObject(type, init_value);
+ Py_DECREF(init_value);
+ if (err_value == NULL)
+ goto fail;
+ if (reason_obj == NULL)
+ reason_obj = Py_None;
+ if (_PyObject_SetAttrId(err_value, &PyId_reason, reason_obj))
+ goto fail;
+ if (lib_obj == NULL)
+ lib_obj = Py_None;
+ if (_PyObject_SetAttrId(err_value, &PyId_library, lib_obj))
+ goto fail;
+ PyErr_SetObject(type, err_value);
+fail:
+ Py_XDECREF(err_value);
+}
static PyObject *
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
{
- PyObject *v;
- char buf[2048];
- char *errstr;
+ PyObject *type = PySSLErrorObject;
+ char *errstr = NULL;
int err;
enum py_ssl_error p = PY_SSL_ERROR_NONE;
+ unsigned long e = 0;
assert(ret <= 0);
+ e = ERR_peek_last_error();
if (obj->ssl != NULL) {
err = SSL_get_error(obj->ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
- errstr = "TLS/SSL connection has been closed";
+ errstr = "TLS/SSL connection has been closed (EOF)";
+ type = PySSLZeroReturnErrorObject;
p = PY_SSL_ERROR_ZERO_RETURN;
break;
case SSL_ERROR_WANT_READ:
errstr = "The operation did not complete (read)";
+ type = PySSLWantReadErrorObject;
p = PY_SSL_ERROR_WANT_READ;
break;
case SSL_ERROR_WANT_WRITE:
p = PY_SSL_ERROR_WANT_WRITE;
+ type = PySSLWantWriteErrorObject;
errstr = "The operation did not complete (write)";
break;
case SSL_ERROR_WANT_X509_LOOKUP:
@@ -207,80 +378,64 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
break;
case SSL_ERROR_SYSCALL:
{
- unsigned long e = ERR_get_error();
if (e == 0) {
PySocketSockObject *s
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
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) {
/* underlying BIO reported an I/O error */
Py_INCREF(s);
ERR_clear_error();
- v = s->errorhandler();
+ s->errorhandler();
Py_DECREF(s);
- return v;
+ return NULL;
} else { /* possible? */
p = PY_SSL_ERROR_SYSCALL;
+ type = PySSLSyscallErrorObject;
errstr = "Some I/O error occurred";
}
} else {
p = PY_SSL_ERROR_SYSCALL;
- /* XXX Protected by global interpreter lock */
- errstr = ERR_error_string(e, NULL);
}
break;
}
case SSL_ERROR_SSL:
{
- unsigned long e = ERR_get_error();
p = PY_SSL_ERROR_SSL;
- if (e != 0)
- /* XXX Protected by global interpreter lock */
- errstr = ERR_error_string(e, NULL);
- else { /* possible? */
+ if (e == 0)
+ /* possible? */
errstr = "A failure in the SSL library occurred";
- }
break;
}
default:
p = PY_SSL_ERROR_INVALID_ERROR_CODE;
errstr = "Invalid error code";
}
- } else {
- errstr = ERR_error_string(ERR_peek_last_error(), NULL);
}
- PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
+ fill_and_set_sslerror(type, p, errstr, lineno, e);
ERR_clear_error();
- v = Py_BuildValue("(is)", p, buf);
- if (v != NULL) {
- PyErr_SetObject(PySSLErrorObject, v);
- Py_DECREF(v);
- }
return NULL;
}
static PyObject *
_setSSLError (char *errstr, int errcode, char *filename, int lineno) {
- char buf[2048];
- PyObject *v;
-
- if (errstr == NULL) {
+ if (errstr == NULL)
errcode = ERR_peek_last_error();
- errstr = ERR_error_string(errcode, NULL);
- }
- PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
+ else
+ errcode = 0;
+ fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode);
ERR_clear_error();
- v = Py_BuildValue("(is)", errcode, buf);
- if (v != NULL) {
- PyErr_SetObject(PySSLErrorObject, v);
- Py_DECREF(v);
- }
return NULL;
}
+/*
+ * SSL objects
+ */
+
static PySSLSocket *
newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
enum py_ssl_server_or_client socket_type,
@@ -328,6 +483,7 @@ newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
SSL_set_accept_state(self->ssl);
PySSL_END_ALLOW_THREADS
+ self->socket_type = socket_type;
self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
return self;
}
@@ -356,7 +512,6 @@ static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self)
/* Actually negotiate SSL connection */
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */
- sockstate = 0;
do {
PySSL_BEGIN_ALLOW_THREADS
ret = SSL_do_handshake(self->ssl);
@@ -882,19 +1037,15 @@ PySSL_peercert(PySSLSocket *self, PyObject *args)
PyObject *retval = NULL;
int len;
int verification;
- PyObject *binary_mode = Py_None;
- int b;
+ int binary_mode = 0;
- if (!PyArg_ParseTuple(args, "|O:peer_certificate", &binary_mode))
+ if (!PyArg_ParseTuple(args, "|p:peer_certificate", &binary_mode))
return NULL;
if (!self->peer_cert)
Py_RETURN_NONE;
- b = PyObject_IsTrue(binary_mode);
- if (b < 0)
- return NULL;
- if (b) {
+ if (binary_mode) {
/* return cert in DER-encoded format */
unsigned char *bytes_buf = NULL;
@@ -980,6 +1131,39 @@ static PyObject *PySSL_cipher (PySSLSocket *self) {
return NULL;
}
+#ifdef OPENSSL_NPN_NEGOTIATED
+static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) {
+ const unsigned char *out;
+ unsigned int outlen;
+
+ SSL_get0_next_proto_negotiated(self->ssl,
+ &out, &outlen);
+
+ if (out == NULL)
+ Py_RETURN_NONE;
+ return PyUnicode_FromStringAndSize((char *) out, outlen);
+}
+#endif
+
+static PyObject *PySSL_compression(PySSLSocket *self) {
+#ifdef OPENSSL_NO_COMP
+ Py_RETURN_NONE;
+#else
+ const COMP_METHOD *comp_method;
+ const char *short_name;
+
+ if (self->ssl == NULL)
+ Py_RETURN_NONE;
+ comp_method = SSL_get_current_compression(self->ssl);
+ if (comp_method == NULL || comp_method->type == NID_undef)
+ Py_RETURN_NONE;
+ short_name = OBJ_nid2sn(comp_method->type);
+ if (short_name == NULL)
+ Py_RETURN_NONE;
+ return PyUnicode_DecodeFSDefault(short_name);
+#endif
+}
+
static void PySSL_dealloc(PySSLSocket *self)
{
if (self->peer_cert) /* Possible not to have one? */
@@ -1100,7 +1284,6 @@ static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args)
goto error;
}
do {
- err = 0;
PySSL_BEGIN_ALLOW_THREADS
len = SSL_write(self->ssl, buf.buf, buf.len);
err = SSL_get_error(self->ssl, len);
@@ -1236,7 +1419,6 @@ static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args)
}
}
do {
- err = 0;
PySSL_BEGIN_ALLOW_THREADS
count = SSL_read(self->ssl, mem, len);
err = SSL_get_error(self->ssl, count);
@@ -1388,6 +1570,41 @@ PyDoc_STRVAR(PySSL_SSLshutdown_doc,
Does the SSL shutdown handshake with the remote end, and returns\n\
the underlying socket object.");
+#if HAVE_OPENSSL_FINISHED
+static PyObject *
+PySSL_tls_unique_cb(PySSLSocket *self)
+{
+ PyObject *retval = NULL;
+ char buf[PySSL_CB_MAXLEN];
+ int len;
+
+ if (SSL_session_reused(self->ssl) ^ !self->socket_type) {
+ /* if session is resumed XOR we are the client */
+ len = SSL_get_finished(self->ssl, buf, PySSL_CB_MAXLEN);
+ }
+ else {
+ /* if a new session XOR we are the server */
+ len = SSL_get_peer_finished(self->ssl, buf, PySSL_CB_MAXLEN);
+ }
+
+ /* It cannot be negative in current OpenSSL version as of July 2011 */
+ assert(len >= 0);
+ if (len == 0)
+ Py_RETURN_NONE;
+
+ retval = PyBytes_FromStringAndSize(buf, len);
+
+ 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 PyMethodDef PySSLMethods[] = {
{"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS},
@@ -1400,8 +1617,16 @@ static PyMethodDef PySSLMethods[] = {
{"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
{NULL, NULL}
};
@@ -1505,6 +1730,9 @@ static void
context_dealloc(PySSLContext *self)
{
SSL_CTX_free(self->ctx);
+#ifdef OPENSSL_NPN_NEGOTIATED
+ PyMem_Free(self->npn_protocols);
+#endif
Py_TYPE(self)->tp_free(self);
}
@@ -1529,6 +1757,87 @@ set_ciphers(PySSLContext *self, PyObject *args)
Py_RETURN_NONE;
}
+#ifdef OPENSSL_NPN_NEGOTIATED
+/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */
+static int
+_advertiseNPN_cb(SSL *s,
+ const unsigned char **data, unsigned int *len,
+ void *args)
+{
+ PySSLContext *ssl_ctx = (PySSLContext *) args;
+
+ if (ssl_ctx->npn_protocols == NULL) {
+ *data = (unsigned char *) "";
+ *len = 0;
+ } else {
+ *data = (unsigned char *) ssl_ctx->npn_protocols;
+ *len = ssl_ctx->npn_protocols_len;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+/* this callback gets passed to SSL_CTX_set_next_proto_select_cb */
+static int
+_selectNPN_cb(SSL *s,
+ unsigned char **out, unsigned char *outlen,
+ 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;
+}
+#endif
+
+static PyObject *
+_set_npn_protocols(PySSLContext *self, PyObject *args)
+{
+#ifdef OPENSSL_NPN_NEGOTIATED
+ Py_buffer protos;
+
+ if (!PyArg_ParseTuple(args, "y*:set_npn_protocols", &protos))
+ return NULL;
+
+ self->npn_protocols = PyMem_Malloc(protos.len);
+ if (self->npn_protocols == NULL) {
+ PyBuffer_Release(&protos);
+ return PyErr_NoMemory();
+ }
+ 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 */
+ SSL_CTX_set_next_protos_advertised_cb(self->ctx,
+ _advertiseNPN_cb,
+ self);
+ SSL_CTX_set_next_proto_select_cb(self->ctx,
+ _selectNPN_cb,
+ self);
+
+ PyBuffer_Release(&protos);
+ Py_RETURN_NONE;
+#else
+ PyErr_SetString(PyExc_NotImplementedError,
+ "The NPN extension requires OpenSSL 1.0.1 or later.");
+ return NULL;
+#endif
+}
+
static PyObject *
get_verify_mode(PySSLContext *self, void *c)
{
@@ -1595,19 +1904,118 @@ set_options(PySSLContext *self, PyObject *arg, void *c)
return 0;
}
+typedef struct {
+ PyThreadState *thread_state;
+ PyObject *callable;
+ char *password;
+ Py_ssize_t size;
+ int error;
+} _PySSLPasswordInfo;
+
+static int
+_pwinfo_set(_PySSLPasswordInfo *pw_info, PyObject* password,
+ const char *bad_type_error)
+{
+ /* Set the password and size fields of a _PySSLPasswordInfo struct
+ from a unicode, bytes, or byte array object.
+ The password field will be dynamically allocated and must be freed
+ by the caller */
+ PyObject *password_bytes = NULL;
+ const char *data = NULL;
+ Py_ssize_t size;
+
+ if (PyUnicode_Check(password)) {
+ password_bytes = PyUnicode_AsEncodedString(password, NULL, NULL);
+ if (!password_bytes) {
+ goto error;
+ }
+ data = PyBytes_AS_STRING(password_bytes);
+ size = PyBytes_GET_SIZE(password_bytes);
+ } else if (PyBytes_Check(password)) {
+ data = PyBytes_AS_STRING(password);
+ size = PyBytes_GET_SIZE(password);
+ } else if (PyByteArray_Check(password)) {
+ data = PyByteArray_AS_STRING(password);
+ size = PyByteArray_GET_SIZE(password);
+ } else {
+ PyErr_SetString(PyExc_TypeError, bad_type_error);
+ goto error;
+ }
+
+ free(pw_info->password);
+ pw_info->password = malloc(size);
+ if (!pw_info->password) {
+ PyErr_SetString(PyExc_MemoryError,
+ "unable to allocate password buffer");
+ goto error;
+ }
+ memcpy(pw_info->password, data, size);
+ pw_info->size = size;
+
+ Py_XDECREF(password_bytes);
+ return 1;
+
+error:
+ Py_XDECREF(password_bytes);
+ return 0;
+}
+
+static int
+_password_callback(char *buf, int size, int rwflag, void *userdata)
+{
+ _PySSLPasswordInfo *pw_info = (_PySSLPasswordInfo*) userdata;
+ PyObject *fn_ret = NULL;
+
+ PySSL_END_ALLOW_THREADS_S(pw_info->thread_state);
+
+ if (pw_info->callable) {
+ fn_ret = PyObject_CallFunctionObjArgs(pw_info->callable, NULL);
+ if (!fn_ret) {
+ /* TODO: It would be nice to move _ctypes_add_traceback() into the
+ core python API, so we could use it to add a frame here */
+ goto error;
+ }
+
+ if (!_pwinfo_set(pw_info, fn_ret,
+ "password callback must return a string")) {
+ goto error;
+ }
+ Py_CLEAR(fn_ret);
+ }
+
+ if (pw_info->size > size) {
+ PyErr_Format(PyExc_ValueError,
+ "password cannot be longer than %d bytes", size);
+ goto error;
+ }
+
+ PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state);
+ memcpy(buf, pw_info->password, pw_info->size);
+ return pw_info->size;
+
+error:
+ Py_XDECREF(fn_ret);
+ PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state);
+ pw_info->error = 1;
+ return -1;
+}
+
static PyObject *
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
{
- char *kwlist[] = {"certfile", "keyfile", NULL};
- PyObject *certfile, *keyfile = NULL;
+ 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;
+ _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 };
int r;
errno = 0;
ERR_clear_error();
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O|O:load_cert_chain", kwlist,
- &certfile, &keyfile))
+ "O|OO:load_cert_chain", kwlist,
+ &certfile, &keyfile, &password))
return NULL;
if (keyfile == Py_None)
keyfile = NULL;
@@ -1621,12 +2029,26 @@ load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
"keyfile should be a valid filesystem path");
goto error;
}
- PySSL_BEGIN_ALLOW_THREADS
+ if (password && password != Py_None) {
+ if (PyCallable_Check(password)) {
+ pw_info.callable = password;
+ } else if (!_pwinfo_set(&pw_info, password,
+ "password should be a string or callable")) {
+ goto error;
+ }
+ SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback);
+ SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info);
+ }
+ PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
r = SSL_CTX_use_certificate_chain_file(self->ctx,
PyBytes_AS_STRING(certfile_bytes));
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
if (r != 1) {
- if (errno != 0) {
+ if (pw_info.error) {
+ ERR_clear_error();
+ /* the password callback has already set the error information */
+ }
+ else if (errno != 0) {
ERR_clear_error();
PyErr_SetFromErrno(PyExc_IOError);
}
@@ -1635,33 +2057,43 @@ load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
}
goto error;
}
- PySSL_BEGIN_ALLOW_THREADS
+ PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
r = SSL_CTX_use_PrivateKey_file(self->ctx,
PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
SSL_FILETYPE_PEM);
- PySSL_END_ALLOW_THREADS
- Py_XDECREF(keyfile_bytes);
- Py_XDECREF(certfile_bytes);
+ PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
+ Py_CLEAR(keyfile_bytes);
+ Py_CLEAR(certfile_bytes);
if (r != 1) {
- if (errno != 0) {
+ if (pw_info.error) {
+ ERR_clear_error();
+ /* the password callback has already set the error information */
+ }
+ else if (errno != 0) {
ERR_clear_error();
PyErr_SetFromErrno(PyExc_IOError);
}
else {
_setSSLError(NULL, 0, __FILE__, __LINE__);
}
- return NULL;
+ goto error;
}
- PySSL_BEGIN_ALLOW_THREADS
+ PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
r = SSL_CTX_check_private_key(self->ctx);
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
if (r != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
- return NULL;
+ goto error;
}
+ SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata);
+ free(pw_info.password);
Py_RETURN_NONE;
error:
+ SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata);
+ free(pw_info.password);
Py_XDECREF(keyfile_bytes);
Py_XDECREF(certfile_bytes);
return NULL;
@@ -1724,6 +2156,38 @@ load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
}
static PyObject *
+load_dh_params(PySSLContext *self, PyObject *filepath)
+{
+ FILE *f;
+ DH *dh;
+
+ f = _Py_fopen(filepath, "rb");
+ if (f == NULL) {
+ if (!PyErr_Occurred())
+ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
+ return NULL;
+ }
+ errno = 0;
+ PySSL_BEGIN_ALLOW_THREADS
+ dh = PEM_read_DHparams(f, NULL, NULL, NULL);
+ PySSL_END_ALLOW_THREADS
+ if (dh == NULL) {
+ if (errno != 0) {
+ ERR_clear_error();
+ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
+ }
+ else {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ }
+ return NULL;
+ }
+ if (SSL_CTX_set_tmp_dh(self->ctx, dh) == 0)
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ DH_free(dh);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"sock", "server_side", "server_hostname", NULL};
@@ -1808,6 +2272,35 @@ set_default_verify_paths(PySSLContext *self, PyObject *unused)
Py_RETURN_NONE;
}
+#ifndef OPENSSL_NO_ECDH
+static PyObject *
+set_ecdh_curve(PySSLContext *self, PyObject *name)
+{
+ PyObject *name_bytes;
+ int nid;
+ EC_KEY *key;
+
+ if (!PyUnicode_FSConverter(name, &name_bytes))
+ return NULL;
+ assert(PyBytes_Check(name_bytes));
+ nid = OBJ_sn2nid(PyBytes_AS_STRING(name_bytes));
+ Py_DECREF(name_bytes);
+ if (nid == 0) {
+ PyErr_Format(PyExc_ValueError,
+ "unknown elliptic curve name %R", name);
+ return NULL;
+ }
+ key = EC_KEY_new_by_curve_name(nid);
+ if (key == NULL) {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ return NULL;
+ }
+ SSL_CTX_set_tmp_ecdh(self->ctx, key);
+ EC_KEY_free(key);
+ Py_RETURN_NONE;
+}
+#endif
+
static PyGetSetDef context_getsetlist[] = {
{"options", (getter) get_options,
(setter) set_options, NULL},
@@ -1821,14 +2314,22 @@ static struct PyMethodDef context_methods[] = {
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
{NULL, NULL} /* sentinel */
};
@@ -1899,6 +2400,69 @@ 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)
+{
+ int ok;
+ PyObject *bytes;
+ unsigned long err;
+ const char *errstr;
+ PyObject *v;
+
+ bytes = PyBytes_FromStringAndSize(NULL, len);
+ if (bytes == NULL)
+ return NULL;
+ if (pseudo) {
+ ok = RAND_pseudo_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len);
+ if (ok == 0 || ok == 1)
+ return Py_BuildValue("NO", bytes, ok == 1 ? Py_True : Py_False);
+ }
+ else {
+ ok = RAND_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len);
+ if (ok == 1)
+ return bytes;
+ }
+ Py_DECREF(bytes);
+
+ err = ERR_get_error();
+ errstr = ERR_reason_error_string(err);
+ v = Py_BuildValue("(ks)", err, errstr);
+ if (v != NULL) {
+ PyErr_SetObject(PySSLErrorObject, v);
+ Py_DECREF(v);
+ }
+ return NULL;
+}
+
+static PyObject *
+PySSL_RAND_bytes(PyObject *self, PyObject *args)
+{
+ int len;
+ if (!PyArg_ParseTuple(args, "i:RAND_bytes", &len))
+ return NULL;
+ return PySSL_RAND(len, 0);
+}
+
+PyDoc_STRVAR(PySSL_RAND_bytes_doc,
+"RAND_bytes(n) -> bytes\n\
+\n\
+Generate n cryptographically strong pseudo-random bytes.");
+
+static PyObject *
+PySSL_RAND_pseudo_bytes(PyObject *self, PyObject *args)
+{
+ int len;
+ if (!PyArg_ParseTuple(args, "i:RAND_pseudo_bytes", &len))
+ return NULL;
+ return PySSL_RAND(len, 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.");
+
+static PyObject *
PySSL_RAND_status(PyObject *self)
{
return PyLong_FromLong(RAND_status());
@@ -1951,6 +2515,10 @@ static PyMethodDef PySSL_methods[] = {
#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},
{"RAND_egd", PySSL_RAND_egd, METH_VARARGS,
PySSL_RAND_egd_doc},
{"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS,
@@ -2071,6 +2639,8 @@ PyInit__ssl(void)
unsigned long libver;
unsigned int major, minor, fix, patch, status;
PySocketModule_APIObject *socket_api;
+ struct py_ssl_error_code *errcode;
+ struct py_ssl_library_code *libcode;
if (PyType_Ready(&PySSLContext_Type) < 0)
return NULL;
@@ -2100,12 +2670,38 @@ PyInit__ssl(void)
OpenSSL_add_all_algorithms();
/* Add symbols to module dict */
- PySSLErrorObject = PyErr_NewException("ssl.SSLError",
- PySocketModule.error,
- NULL);
+ sslerror_type_slots[0].pfunc = PyExc_OSError;
+ PySSLErrorObject = PyType_FromSpec(&sslerror_type_spec);
if (PySSLErrorObject == NULL)
return NULL;
- if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
+
+ PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
+ PySSLErrorObject, NULL);
+ PySSLWantReadErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLWantReadError", SSLWantReadError_doc,
+ PySSLErrorObject, NULL);
+ PySSLWantWriteErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLWantWriteError", SSLWantWriteError_doc,
+ PySSLErrorObject, NULL);
+ PySSLSyscallErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLSyscallError", SSLSyscallError_doc,
+ PySSLErrorObject, NULL);
+ PySSLEOFErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLEOFError", SSLEOFError_doc,
+ PySSLErrorObject, NULL);
+ if (PySSLZeroReturnErrorObject == NULL
+ || PySSLWantReadErrorObject == NULL
+ || PySSLWantWriteErrorObject == NULL
+ || PySSLSyscallErrorObject == NULL
+ || PySSLEOFErrorObject == NULL)
+ return NULL;
+ if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLSyscallError", PySSLSyscallErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLEOFError", PySSLEOFErrorObject) != 0)
return NULL;
if (PyDict_SetItemString(d, "_SSLContext",
(PyObject *)&PySSLContext_Type) != 0)
@@ -2158,6 +2754,16 @@ PyInit__ssl(void)
PyModule_AddIntConstant(m, "OP_NO_SSLv2", SSL_OP_NO_SSLv2);
PyModule_AddIntConstant(m, "OP_NO_SSLv3", SSL_OP_NO_SSLv3);
PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1);
+ PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
+ SSL_OP_CIPHER_SERVER_PREFERENCE);
+ PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE);
+#ifdef SSL_OP_SINGLE_ECDH_USE
+ PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE);
+#endif
+#ifdef SSL_OP_NO_COMPRESSION
+ PyModule_AddIntConstant(m, "OP_NO_COMPRESSION",
+ SSL_OP_NO_COMPRESSION);
+#endif
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
r = Py_True;
@@ -2167,6 +2773,74 @@ 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);
+
+#ifdef OPENSSL_NO_ECDH
+ r = Py_False;
+#else
+ r = Py_True;
+#endif
+ Py_INCREF(r);
+ PyModule_AddObject(m, "HAS_ECDH", r);
+
+#ifdef OPENSSL_NPN_NEGOTIATED
+ r = Py_True;
+#else
+ r = Py_False;
+#endif
+ Py_INCREF(r);
+ PyModule_AddObject(m, "HAS_NPN", r);
+
+ /* Mappings for error codes */
+ err_codes_to_names = PyDict_New();
+ err_names_to_codes = PyDict_New();
+ if (err_codes_to_names == NULL || err_names_to_codes == NULL)
+ return NULL;
+ errcode = error_codes;
+ while (errcode->mnemonic != NULL) {
+ PyObject *mnemo, *key;
+ mnemo = PyUnicode_FromString(errcode->mnemonic);
+ key = Py_BuildValue("ii", errcode->library, errcode->reason);
+ if (mnemo == NULL || key == NULL)
+ return NULL;
+ if (PyDict_SetItem(err_codes_to_names, key, mnemo))
+ return NULL;
+ if (PyDict_SetItem(err_names_to_codes, mnemo, key))
+ return NULL;
+ Py_DECREF(key);
+ Py_DECREF(mnemo);
+ errcode++;
+ }
+ if (PyModule_AddObject(m, "err_codes_to_names", err_codes_to_names))
+ return NULL;
+ if (PyModule_AddObject(m, "err_names_to_codes", err_names_to_codes))
+ return NULL;
+
+ lib_codes_to_names = PyDict_New();
+ if (lib_codes_to_names == NULL)
+ return NULL;
+ libcode = library_codes;
+ while (libcode->library != NULL) {
+ PyObject *mnemo, *key;
+ key = PyLong_FromLong(libcode->code);
+ mnemo = PyUnicode_FromString(libcode->library);
+ if (key == NULL || mnemo == NULL)
+ return NULL;
+ if (PyDict_SetItem(lib_codes_to_names, key, mnemo))
+ return NULL;
+ Py_DECREF(key);
+ Py_DECREF(mnemo);
+ libcode++;
+ }
+ if (PyModule_AddObject(m, "lib_codes_to_names", lib_codes_to_names))
+ return NULL;
+
/* OpenSSL version */
/* SSLeay() gives us the version of the library linked against,
which could be different from the headers version.