Issue #8550: Add first class SSLContext objects to the ssl module.

This commit is contained in:
Antoine Pitrou 2010-05-16 18:19:27 +00:00
parent 8eac60d9af
commit 152efa2ae2
7 changed files with 864 additions and 283 deletions

View file

@ -115,23 +115,29 @@ static unsigned int _ssl_locks_count = 0;
typedef struct {
PyObject_HEAD
PyObject *Socket; /* weakref to socket on which we're layered */
SSL_CTX* ctx;
SSL* ssl;
X509* peer_cert;
int shutdown_seen_zero;
SSL_CTX *ctx;
} PySSLContext;
} PySSLObject;
typedef struct {
PyObject_HEAD
PyObject *Socket; /* weakref to socket on which we're layered */
SSL *ssl;
X509 *peer_cert;
int shutdown_seen_zero;
} PySSLSocket;
static PyTypeObject PySSL_Type;
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args);
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args);
static PyTypeObject PySSLContext_Type;
static PyTypeObject PySSLSocket_Type;
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(PySSLObject *self, PyObject *args);
static PyObject *PySSL_cipher(PySSLObject *self);
static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args);
static PyObject *PySSL_cipher(PySSLSocket *self);
#define PySSLObject_Check(v) (Py_TYPE(v) == &PySSL_Type)
#define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type)
#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type)
typedef enum {
SOCKET_IS_NONBLOCKING,
@ -154,7 +160,7 @@ typedef enum {
*/
static PyObject *
PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno)
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
{
PyObject *v;
char buf[2048];
@ -258,126 +264,28 @@ _setSSLError (char *errstr, int errcode, char *filename, int lineno) {
return NULL;
}
static PySSLObject *
newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
enum py_ssl_server_or_client socket_type,
enum py_ssl_cert_requirements certreq,
enum py_ssl_version proto_version,
char *cacerts_file, char *ciphers)
static PySSLSocket *
newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
enum py_ssl_server_or_client socket_type)
{
PySSLObject *self;
char *errstr = NULL;
int ret;
int verification_mode;
PySSLSocket *self;
self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */
self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
if (self == NULL)
return NULL;
self->peer_cert = NULL;
self->ssl = NULL;
self->ctx = NULL;
self->Socket = NULL;
/* Make sure the SSL error state is initialized */
(void) ERR_get_state();
ERR_clear_error();
if ((key_file && !cert_file) || (!key_file && cert_file)) {
errstr = ERRSTR("Both the key & certificate files "
"must be specified");
goto fail;
}
if ((socket_type == PY_SSL_SERVER) &&
((key_file == NULL) || (cert_file == NULL))) {
errstr = ERRSTR("Both the key & certificate files "
"must be specified for server-side operation");
goto fail;
}
PySSL_BEGIN_ALLOW_THREADS
if (proto_version == PY_SSL_VERSION_TLS1)
self->ctx = SSL_CTX_new(TLSv1_method()); /* Set up context */
else if (proto_version == PY_SSL_VERSION_SSL3)
self->ctx = SSL_CTX_new(SSLv3_method()); /* Set up context */
else if (proto_version == PY_SSL_VERSION_SSL2)
self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */
else if (proto_version == PY_SSL_VERSION_SSL23)
self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */
self->ssl = SSL_new(ctx);
PySSL_END_ALLOW_THREADS
if (self->ctx == NULL) {
errstr = ERRSTR("Invalid SSL protocol variant specified.");
goto fail;
}
if (ciphers != NULL) {
ret = SSL_CTX_set_cipher_list(self->ctx, ciphers);
if (ret == 0) {
errstr = ERRSTR("No cipher can be selected.");
goto fail;
}
}
if (certreq != PY_SSL_CERT_NONE) {
if (cacerts_file == NULL) {
errstr = ERRSTR("No root certificates specified for "
"verification of other-side certificates.");
goto fail;
} else {
PySSL_BEGIN_ALLOW_THREADS
ret = SSL_CTX_load_verify_locations(self->ctx,
cacerts_file,
NULL);
PySSL_END_ALLOW_THREADS
if (ret != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
goto fail;
}
}
}
if (key_file) {
PySSL_BEGIN_ALLOW_THREADS
ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
SSL_FILETYPE_PEM);
PySSL_END_ALLOW_THREADS
if (ret != 1) {
_setSSLError(NULL, ret, __FILE__, __LINE__);
goto fail;
}
PySSL_BEGIN_ALLOW_THREADS
ret = SSL_CTX_use_certificate_chain_file(self->ctx,
cert_file);
PySSL_END_ALLOW_THREADS
if (ret != 1) {
/*
fprintf(stderr, "ret is %d, errcode is %lu, %lu, with file \"%s\"\n",
ret, ERR_peek_error(), ERR_peek_last_error(), cert_file);
*/
if (ERR_peek_last_error() != 0) {
_setSSLError(NULL, ret, __FILE__, __LINE__);
goto fail;
}
}
}
/* ssl compatibility */
SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
verification_mode = SSL_VERIFY_NONE;
if (certreq == PY_SSL_CERT_OPTIONAL)
verification_mode = SSL_VERIFY_PEER;
else if (certreq == PY_SSL_CERT_REQUIRED)
verification_mode = (SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
SSL_CTX_set_verify(self->ctx, verification_mode,
NULL); /* set verify lvl */
PySSL_BEGIN_ALLOW_THREADS
self->ssl = SSL_new(self->ctx); /* New ssl struct */
PySSL_END_ALLOW_THREADS
SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */
SSL_set_fd(self->ssl, sock->sock_fd);
#ifdef SSL_MODE_AUTO_RETRY
SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
#endif
@ -385,8 +293,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
/* 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) {
/* Set both the read and write BIO's to non-blocking mode */
if (sock->sock_timeout >= 0.0) {
BIO_set_nbio(SSL_get_rbio(self->ssl), 1);
BIO_set_nbio(SSL_get_wbio(self->ssl), 1);
}
@ -398,57 +305,13 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
SSL_set_accept_state(self->ssl);
PySSL_END_ALLOW_THREADS
self->Socket = PyWeakref_NewRef((PyObject *) Sock, Py_None);
self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
return self;
fail:
if (errstr)
PyErr_SetString(PySSLErrorObject, errstr);
Py_DECREF(self);
return NULL;
}
static PyObject *
PySSL_sslwrap(PyObject *self, PyObject *args)
{
PySocketSockObject *Sock;
int server_side = 0;
int verification_mode = PY_SSL_CERT_NONE;
int protocol = PY_SSL_VERSION_SSL23;
char *key_file = NULL;
char *cert_file = NULL;
char *cacerts_file = NULL;
char *ciphers = NULL;
if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap",
PySocketModule.Sock_Type,
&Sock,
&server_side,
&key_file, &cert_file,
&verification_mode, &protocol,
&cacerts_file, &ciphers))
return NULL;
/*
fprintf(stderr,
"server_side is %d, keyfile %p, certfile %p, verify_mode %d, "
"protocol %d, certs %p\n",
server_side, key_file, cert_file, verification_mode,
protocol, cacerts_file);
*/
return (PyObject *) newPySSLObject(Sock, key_file, cert_file,
server_side, verification_mode,
protocol, cacerts_file,
ciphers);
}
PyDoc_STRVAR(ssl_doc,
"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n"
" cacertsfile, ciphers]) -> sslobject");
/* SSL object methods */
static PyObject *PySSL_SSLdo_handshake(PySSLObject *self)
static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self)
{
int ret;
int err;
@ -986,7 +849,7 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) {
static PyObject *
PySSL_peercert(PySSLObject *self, PyObject *args)
PySSL_peercert(PySSLSocket *self, PyObject *args)
{
PyObject *retval = NULL;
int len;
@ -1017,8 +880,7 @@ PySSL_peercert(PySSLObject *self, PyObject *args)
return retval;
} else {
verification = SSL_CTX_get_verify_mode(self->ctx);
verification = SSL_CTX_get_verify_mode(SSL_get_SSL_CTX(self->ssl));
if ((verification & SSL_VERIFY_PEER) == 0)
return PyDict_New();
else
@ -1038,7 +900,7 @@ 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 (PySSLObject *self) {
static PyObject *PySSL_cipher (PySSLSocket *self) {
PyObject *retval, *v;
SSL_CIPHER *current;
@ -1084,14 +946,12 @@ static PyObject *PySSL_cipher (PySSLObject *self) {
return NULL;
}
static void PySSL_dealloc(PySSLObject *self)
static void PySSL_dealloc(PySSLSocket *self)
{
if (self->peer_cert) /* Possible not to have one? */
X509_free (self->peer_cert);
if (self->ssl)
SSL_free(self->ssl);
if (self->ctx)
SSL_CTX_free(self->ctx);
Py_XDECREF(self->Socket);
PyObject_Del(self);
}
@ -1166,7 +1026,7 @@ normal_return:
return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
}
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args)
{
Py_buffer buf;
int len;
@ -1250,7 +1110,7 @@ PyDoc_STRVAR(PySSL_SSLwrite_doc,
Writes the string s into the SSL object. Returns the number\n\
of bytes written.");
static PyObject *PySSL_SSLpending(PySSLObject *self)
static PyObject *PySSL_SSLpending(PySSLSocket *self)
{
int count = 0;
@ -1269,7 +1129,7 @@ PyDoc_STRVAR(PySSL_SSLpending_doc,
Returns the number of already decrypted bytes available for read,\n\
pending on the connection.\n");
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args)
{
PyObject *dest = NULL;
Py_buffer buf;
@ -1392,7 +1252,7 @@ PyDoc_STRVAR(PySSL_SSLread_doc,
\n\
Read up to len bytes from the SSL socket.");
static PyObject *PySSL_SSLshutdown(PySSLObject *self)
static PyObject *PySSL_SSLshutdown(PySSLSocket *self)
{
int err, ssl_err, sockstate, nonblocking;
int zeros = 0;
@ -1497,10 +1357,10 @@ static PyMethodDef PySSLMethods[] = {
{NULL, NULL}
};
static PyTypeObject PySSL_Type = {
static PyTypeObject PySSLSocket_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"ssl.SSLContext", /*tp_name*/
sizeof(PySSLObject), /*tp_basicsize*/
"_ssl._SSLSocket", /*tp_name*/
sizeof(PySSLSocket), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PySSL_dealloc, /*tp_dealloc*/
@ -1529,6 +1389,306 @@ static PyTypeObject PySSL_Type = {
PySSLMethods, /*tp_methods*/
};
/*
* _SSLContext objects
*/
static PyObject *
context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"protocol", NULL};
PySSLContext *self;
int proto_version = PY_SSL_VERSION_SSL23;
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());
else if (proto_version == PY_SSL_VERSION_SSL3)
ctx = SSL_CTX_new(SSLv3_method());
else if (proto_version == PY_SSL_VERSION_SSL2)
ctx = SSL_CTX_new(SSLv2_method());
else if (proto_version == PY_SSL_VERSION_SSL23)
ctx = SSL_CTX_new(SSLv23_method());
else
proto_version = -1;
PySSL_END_ALLOW_THREADS
if (proto_version == -1) {
PyErr_SetString(PyExc_ValueError,
"invalid protocol version");
return NULL;
}
if (ctx == NULL) {
PyErr_SetString(PySSLErrorObject,
"failed to allocate SSL context");
return NULL;
}
assert(type != NULL && type->tp_alloc != NULL);
self = (PySSLContext *) type->tp_alloc(type, 0);
if (self == NULL) {
SSL_CTX_free(ctx);
return NULL;
}
self->ctx = ctx;
/* Defaults */
SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
return (PyObject *)self;
}
static void
context_dealloc(PySSLContext *self)
{
SSL_CTX_free(self->ctx);
Py_TYPE(self)->tp_free(self);
}
static PyObject *
set_ciphers(PySSLContext *self, PyObject *args)
{
int ret;
const char *cipherlist;
if (!PyArg_ParseTuple(args, "s:set_ciphers", &cipherlist))
return NULL;
ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist);
if (ret == 0) {
PyErr_SetString(PySSLErrorObject,
"No cipher can be selected.");
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
get_verify_mode(PySSLContext *self, void *c)
{
switch (SSL_CTX_get_verify_mode(self->ctx)) {
case SSL_VERIFY_NONE:
return PyLong_FromLong(PY_SSL_CERT_NONE);
case SSL_VERIFY_PEER:
return PyLong_FromLong(PY_SSL_CERT_OPTIONAL);
case SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
return PyLong_FromLong(PY_SSL_CERT_REQUIRED);
}
PyErr_SetString(PySSLErrorObject,
"invalid return value from SSL_CTX_get_verify_mode");
return NULL;
}
static int
set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
{
int n, mode;
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;
}
SSL_CTX_set_verify(self->ctx, mode, NULL);
return 0;
}
static PyObject *
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"certfile", "keyfile", NULL};
PyObject *certfile, *keyfile = NULL;
PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL;
int r;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"O|O:load_cert_chain", kwlist,
&certfile, &keyfile))
return NULL;
if (keyfile == Py_None)
keyfile = NULL;
if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) {
PyErr_SetString(PyExc_TypeError,
"certfile should be a valid filesystem path");
return NULL;
}
if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) {
PyErr_SetString(PyExc_TypeError,
"keyfile should be a valid filesystem path");
goto error;
}
PySSL_BEGIN_ALLOW_THREADS
r = SSL_CTX_use_certificate_chain_file(self->ctx,
PyBytes_AS_STRING(certfile_bytes));
PySSL_END_ALLOW_THREADS
if (r != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
goto error;
}
PySSL_BEGIN_ALLOW_THREADS
r = SSL_CTX_use_RSAPrivateKey_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);
if (r != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
return NULL;
}
PySSL_BEGIN_ALLOW_THREADS
r = SSL_CTX_check_private_key(self->ctx);
PySSL_END_ALLOW_THREADS
if (r != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
return NULL;
}
Py_RETURN_NONE;
error:
Py_XDECREF(keyfile_bytes);
Py_XDECREF(certfile_bytes);
return NULL;
}
static PyObject *
load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"cafile", "capath", NULL};
PyObject *cafile = NULL, *capath = NULL;
PyObject *cafile_bytes = NULL, *capath_bytes = NULL;
const char *cafile_buf = NULL, *capath_buf = NULL;
int r;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"|OO:load_verify_locations", kwlist,
&cafile, &capath))
return NULL;
if (cafile == Py_None)
cafile = NULL;
if (capath == Py_None)
capath = NULL;
if (cafile == NULL && capath == NULL) {
PyErr_SetString(PyExc_TypeError,
"cafile and capath cannot be both omitted");
return NULL;
}
if (cafile && !PyUnicode_FSConverter(cafile, &cafile_bytes)) {
PyErr_SetString(PyExc_TypeError,
"cafile should be a valid filesystem path");
return NULL;
}
if (capath && !PyUnicode_FSConverter(capath, &capath_bytes)) {
Py_DECREF(cafile_bytes);
PyErr_SetString(PyExc_TypeError,
"capath should be a valid filesystem path");
return NULL;
}
if (cafile)
cafile_buf = PyBytes_AS_STRING(cafile_bytes);
if (capath)
capath_buf = PyBytes_AS_STRING(capath_bytes);
PySSL_BEGIN_ALLOW_THREADS
r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
PySSL_END_ALLOW_THREADS
Py_XDECREF(cafile_bytes);
Py_XDECREF(capath_bytes);
if (r != 1) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
return NULL;
}
Py_RETURN_NONE;
}
static PyObject *
context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
{
char *kwlist[] = {"sock", "server_side", NULL};
PySocketSockObject *sock;
int server_side = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i:_wrap_socket", kwlist,
PySocketModule.Sock_Type,
&sock, &server_side))
return NULL;
return (PyObject *) newPySSLSocket(self->ctx, sock, server_side);
}
static PyGetSetDef context_getsetlist[] = {
{"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},
{"load_cert_chain", (PyCFunction) load_cert_chain,
METH_VARARGS | METH_KEYWORDS, NULL},
{"load_verify_locations", (PyCFunction) load_verify_locations,
METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL} /* sentinel */
};
static PyTypeObject PySSLContext_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ssl._SSLContext", /*tp_name*/
sizeof(PySSLContext), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)context_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_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
context_methods, /*tp_methods*/
0, /*tp_members*/
context_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*/
context_new, /*tp_new*/
};
#ifdef HAVE_OPENSSL_RAND
/* helper routines for seeding the SSL PRNG */
@ -1598,8 +1758,6 @@ fails or if it does provide enough data to seed PRNG.");
/* List of functions exported by this module. */
static PyMethodDef PySSL_methods[] = {
{"sslwrap", PySSL_sslwrap,
METH_VARARGS, ssl_doc},
{"_test_decode_cert", PySSL_test_decode_certificate,
METH_VARARGS},
#ifdef HAVE_OPENSSL_RAND
@ -1708,7 +1866,9 @@ PyInit__ssl(void)
unsigned int major, minor, fix, patch, status;
PySocketModule_APIObject *socket_api;
if (PyType_Ready(&PySSL_Type) < 0)
if (PyType_Ready(&PySSLContext_Type) < 0)
return NULL;
if (PyType_Ready(&PySSLSocket_Type) < 0)
return NULL;
m = PyModule_Create(&_sslmodule);
@ -1741,8 +1901,11 @@ PyInit__ssl(void)
return NULL;
if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
return NULL;
if (PyDict_SetItemString(d, "SSLType",
(PyObject *)&PySSL_Type) != 0)
if (PyDict_SetItemString(d, "_SSLContext",
(PyObject *)&PySSLContext_Type) != 0)
return NULL;
if (PyDict_SetItemString(d, "_SSLSocket",
(PyObject *)&PySSLSocket_Type) != 0)
return NULL;
PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN",
PY_SSL_ERROR_ZERO_RETURN);