/* SSL socket module | |
SSL support based on patches by Brian E Gallew and Laszlo Kovacs. | |
Re-worked a bit by Bill Janssen to add server-side support and | |
certificate decoding. Chris Stawarz contributed some non-blocking | |
patches. | |
This module is imported by ssl.py. It should *not* be used | |
directly. | |
XXX should partial writes be enabled, SSL_MODE_ENABLE_PARTIAL_WRITE? | |
XXX integrate several "shutdown modes" as suggested in | |
http://bugs.python.org/issue8108#msg102867 ? | |
*/ | |
#include "Python.h" | |
#ifdef WITH_THREAD | |
#include "pythread.h" | |
#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);} \ | |
} | |
#else /* no WITH_THREAD */ | |
#define PySSL_BEGIN_ALLOW_THREADS | |
#define PySSL_BLOCK_THREADS | |
#define PySSL_UNBLOCK_THREADS | |
#define PySSL_END_ALLOW_THREADS | |
#endif | |
enum py_ssl_error { | |
/* these mirror ssl.h */ | |
PY_SSL_ERROR_NONE, | |
PY_SSL_ERROR_SSL, | |
PY_SSL_ERROR_WANT_READ, | |
PY_SSL_ERROR_WANT_WRITE, | |
PY_SSL_ERROR_WANT_X509_LOOKUP, | |
PY_SSL_ERROR_SYSCALL, /* look at error stack/return value/errno */ | |
PY_SSL_ERROR_ZERO_RETURN, | |
PY_SSL_ERROR_WANT_CONNECT, | |
/* start of non ssl.h errorcodes */ | |
PY_SSL_ERROR_EOF, /* special case of SSL_ERROR_SYSCALL */ | |
PY_SSL_ERROR_INVALID_ERROR_CODE | |
}; | |
enum py_ssl_server_or_client { | |
PY_SSL_CLIENT, | |
PY_SSL_SERVER | |
}; | |
enum py_ssl_cert_requirements { | |
PY_SSL_CERT_NONE, | |
PY_SSL_CERT_OPTIONAL, | |
PY_SSL_CERT_REQUIRED | |
}; | |
enum py_ssl_version { | |
#ifndef OPENSSL_NO_SSL2 | |
PY_SSL_VERSION_SSL2, | |
#endif | |
PY_SSL_VERSION_SSL3=1, | |
PY_SSL_VERSION_SSL23, | |
PY_SSL_VERSION_TLS1 | |
}; | |
/* Include symbols from _socket module */ | |
#include "socketmodule.h" | |
#if defined(HAVE_POLL_H) | |
#include <poll.h> | |
#elif defined(HAVE_SYS_POLL_H) | |
#include <sys/poll.h> | |
#endif | |
/* Include OpenSSL header files */ | |
#include "openssl/rsa.h" | |
#include "openssl/crypto.h" | |
#include "openssl/x509.h" | |
#include "openssl/x509v3.h" | |
#include "openssl/pem.h" | |
#include "openssl/ssl.h" | |
#include "openssl/err.h" | |
#include "openssl/rand.h" | |
/* SSL error object */ | |
static PyObject *PySSLErrorObject; | |
#ifdef WITH_THREAD | |
/* serves as a flag to see whether we've initialized the SSL thread support. */ | |
/* 0 means no, greater than 0 means yes */ | |
static unsigned int _ssl_locks_count = 0; | |
#endif /* def WITH_THREAD */ | |
/* SSL socket object */ | |
#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 | |
typedef struct { | |
PyObject_HEAD | |
PySocketSockObject *Socket; /* Socket on which we're layered */ | |
SSL_CTX* ctx; | |
SSL* ssl; | |
X509* peer_cert; | |
char server[X509_NAME_MAXLEN]; | |
char issuer[X509_NAME_MAXLEN]; | |
int shutdown_seen_zero; | |
} PySSLObject; | |
static PyTypeObject PySSL_Type; | |
static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args); | |
static PyObject *PySSL_SSLread(PySSLObject *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); | |
#define PySSLObject_Check(v) (Py_TYPE(v) == &PySSL_Type) | |
typedef enum { | |
SOCKET_IS_NONBLOCKING, | |
SOCKET_IS_BLOCKING, | |
SOCKET_HAS_TIMED_OUT, | |
SOCKET_HAS_BEEN_CLOSED, | |
SOCKET_TOO_LARGE_FOR_SELECT, | |
SOCKET_OPERATION_OK | |
} 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) | |
/* 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. | |
*/ | |
static PyObject * | |
PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno) | |
{ | |
PyObject *v; | |
char buf[2048]; | |
char *errstr; | |
int err; | |
enum py_ssl_error p = PY_SSL_ERROR_NONE; | |
assert(ret <= 0); | |
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"; | |
p = PY_SSL_ERROR_ZERO_RETURN; | |
break; | |
case SSL_ERROR_WANT_READ: | |
errstr = "The operation did not complete (read)"; | |
p = PY_SSL_ERROR_WANT_READ; | |
break; | |
case SSL_ERROR_WANT_WRITE: | |
p = PY_SSL_ERROR_WANT_WRITE; | |
errstr = "The operation did not complete (write)"; | |
break; | |
case SSL_ERROR_WANT_X509_LOOKUP: | |
p = PY_SSL_ERROR_WANT_X509_LOOKUP; | |
errstr = "The operation did not complete (X509 lookup)"; | |
break; | |
case SSL_ERROR_WANT_CONNECT: | |
p = PY_SSL_ERROR_WANT_CONNECT; | |
errstr = "The operation did not complete (connect)"; | |
break; | |
case SSL_ERROR_SYSCALL: | |
{ | |
unsigned long e = ERR_get_error(); | |
if (e == 0) { | |
if (ret == 0 || !obj->Socket) { | |
p = PY_SSL_ERROR_EOF; | |
errstr = "EOF occurred in violation of protocol"; | |
} else if (ret == -1) { | |
/* underlying BIO reported an I/O error */ | |
ERR_clear_error(); | |
return obj->Socket->errorhandler(); | |
} else { /* possible? */ | |
p = PY_SSL_ERROR_SYSCALL; | |
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? */ | |
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); | |
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) { | |
errcode = ERR_peek_last_error(); | |
errstr = ERR_error_string(errcode, NULL); | |
} | |
PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr); | |
ERR_clear_error(); | |
v = Py_BuildValue("(is)", errcode, buf); | |
if (v != NULL) { | |
PyErr_SetObject(PySSLErrorObject, v); | |
Py_DECREF(v); | |
} | |
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) | |
{ | |
PySSLObject *self; | |
char *errstr = NULL; | |
int ret; | |
int verification_mode; | |
self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */ | |
if (self == NULL) | |
return NULL; | |
memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN); | |
memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN); | |
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 */ | |
#ifndef OPENSSL_NO_SSL2 | |
else if (proto_version == PY_SSL_VERSION_SSL2) | |
self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */ | |
#endif | |
else if (proto_version == PY_SSL_VERSION_SSL23) | |
self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ | |
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 */ | |
#ifdef SSL_MODE_AUTO_RETRY | |
SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY); | |
#endif | |
/* 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 */ | |
BIO_set_nbio(SSL_get_rbio(self->ssl), 1); | |
BIO_set_nbio(SSL_get_wbio(self->ssl), 1); | |
} | |
PySSL_BEGIN_ALLOW_THREADS | |
if (socket_type == PY_SSL_CLIENT) | |
SSL_set_connect_state(self->ssl); | |
else | |
SSL_set_accept_state(self->ssl); | |
PySSL_END_ALLOW_THREADS | |
self->Socket = Sock; | |
Py_INCREF(self->Socket); | |
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) | |
{ | |
int ret; | |
int err; | |
int sockstate, nonblocking; | |
/* just in case the blocking state of the socket has been changed */ | |
nonblocking = (self->Socket->sock_timeout >= 0.0); | |
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); | |
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); | |
/* Actually negotiate SSL connection */ | |
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */ | |
do { | |
PySSL_BEGIN_ALLOW_THREADS | |
ret = SSL_do_handshake(self->ssl); | |
err = SSL_get_error(self->ssl, ret); | |
PySSL_END_ALLOW_THREADS | |
if(PyErr_CheckSignals()) { | |
return NULL; | |
} | |
if (err == SSL_ERROR_WANT_READ) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); | |
} else if (err == SSL_ERROR_WANT_WRITE) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); | |
} else { | |
sockstate = SOCKET_OPERATION_OK; | |
} | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
PyErr_SetString(PySSLErrorObject, | |
ERRSTR("The handshake operation timed out")); | |
return NULL; | |
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { | |
PyErr_SetString(PySSLErrorObject, | |
ERRSTR("Underlying socket has been closed.")); | |
return NULL; | |
} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { | |
PyErr_SetString(PySSLErrorObject, | |
ERRSTR("Underlying socket too large for select().")); | |
return NULL; | |
} else if (sockstate == SOCKET_IS_NONBLOCKING) { | |
break; | |
} | |
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); | |
if (ret < 1) | |
return PySSL_SetError(self, ret, __FILE__, __LINE__); | |
if (self->peer_cert) | |
X509_free (self->peer_cert); | |
PySSL_BEGIN_ALLOW_THREADS | |
if ((self->peer_cert = SSL_get_peer_certificate(self->ssl))) { | |
X509_NAME_oneline(X509_get_subject_name(self->peer_cert), | |
self->server, X509_NAME_MAXLEN); | |
X509_NAME_oneline(X509_get_issuer_name(self->peer_cert), | |
self->issuer, X509_NAME_MAXLEN); | |
} | |
PySSL_END_ALLOW_THREADS | |
Py_INCREF(Py_None); | |
return Py_None; | |
} | |
static PyObject * | |
PySSL_server(PySSLObject *self) | |
{ | |
return PyString_FromString(self->server); | |
} | |
static PyObject * | |
PySSL_issuer(PySSLObject *self) | |
{ | |
return PyString_FromString(self->issuer); | |
} | |
static PyObject * | |
_create_tuple_for_attribute (ASN1_OBJECT *name, ASN1_STRING *value) { | |
char namebuf[X509_NAME_MAXLEN]; | |
int buflen; | |
PyObject *name_obj; | |
PyObject *value_obj; | |
PyObject *attr; | |
unsigned char *valuebuf = NULL; | |
buflen = OBJ_obj2txt(namebuf, sizeof(namebuf), name, 0); | |
if (buflen < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
goto fail; | |
} | |
name_obj = PyString_FromStringAndSize(namebuf, buflen); | |
if (name_obj == NULL) | |
goto fail; | |
buflen = ASN1_STRING_to_UTF8(&valuebuf, value); | |
if (buflen < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
Py_DECREF(name_obj); | |
goto fail; | |
} | |
value_obj = PyUnicode_DecodeUTF8((char *) valuebuf, | |
buflen, "strict"); | |
OPENSSL_free(valuebuf); | |
if (value_obj == NULL) { | |
Py_DECREF(name_obj); | |
goto fail; | |
} | |
attr = PyTuple_New(2); | |
if (attr == NULL) { | |
Py_DECREF(name_obj); | |
Py_DECREF(value_obj); | |
goto fail; | |
} | |
PyTuple_SET_ITEM(attr, 0, name_obj); | |
PyTuple_SET_ITEM(attr, 1, value_obj); | |
return attr; | |
fail: | |
return NULL; | |
} | |
static PyObject * | |
_create_tuple_for_X509_NAME (X509_NAME *xname) | |
{ | |
PyObject *dn = NULL; /* tuple which represents the "distinguished name" */ | |
PyObject *rdn = NULL; /* tuple to hold a "relative distinguished name" */ | |
PyObject *rdnt; | |
PyObject *attr = NULL; /* tuple to hold an attribute */ | |
int entry_count = X509_NAME_entry_count(xname); | |
X509_NAME_ENTRY *entry; | |
ASN1_OBJECT *name; | |
ASN1_STRING *value; | |
int index_counter; | |
int rdn_level = -1; | |
int retcode; | |
dn = PyList_New(0); | |
if (dn == NULL) | |
return NULL; | |
/* now create another tuple to hold the top-level RDN */ | |
rdn = PyList_New(0); | |
if (rdn == NULL) | |
goto fail0; | |
for (index_counter = 0; | |
index_counter < entry_count; | |
index_counter++) | |
{ | |
entry = X509_NAME_get_entry(xname, index_counter); | |
/* check to see if we've gotten to a new RDN */ | |
if (rdn_level >= 0) { | |
if (rdn_level != entry->set) { | |
/* yes, new RDN */ | |
/* add old RDN to DN */ | |
rdnt = PyList_AsTuple(rdn); | |
Py_DECREF(rdn); | |
if (rdnt == NULL) | |
goto fail0; | |
retcode = PyList_Append(dn, rdnt); | |
Py_DECREF(rdnt); | |
if (retcode < 0) | |
goto fail0; | |
/* create new RDN */ | |
rdn = PyList_New(0); | |
if (rdn == NULL) | |
goto fail0; | |
} | |
} | |
rdn_level = entry->set; | |
/* now add this attribute to the current RDN */ | |
name = X509_NAME_ENTRY_get_object(entry); | |
value = X509_NAME_ENTRY_get_data(entry); | |
attr = _create_tuple_for_attribute(name, value); | |
/* | |
fprintf(stderr, "RDN level %d, attribute %s: %s\n", | |
entry->set, | |
PyString_AS_STRING(PyTuple_GET_ITEM(attr, 0)), | |
PyString_AS_STRING(PyTuple_GET_ITEM(attr, 1))); | |
*/ | |
if (attr == NULL) | |
goto fail1; | |
retcode = PyList_Append(rdn, attr); | |
Py_DECREF(attr); | |
if (retcode < 0) | |
goto fail1; | |
} | |
/* now, there's typically a dangling RDN */ | |
if ((rdn != NULL) && (PyList_Size(rdn) > 0)) { | |
rdnt = PyList_AsTuple(rdn); | |
Py_DECREF(rdn); | |
if (rdnt == NULL) | |
goto fail0; | |
retcode = PyList_Append(dn, rdnt); | |
Py_DECREF(rdnt); | |
if (retcode < 0) | |
goto fail0; | |
} | |
/* convert list to tuple */ | |
rdnt = PyList_AsTuple(dn); | |
Py_DECREF(dn); | |
if (rdnt == NULL) | |
return NULL; | |
return rdnt; | |
fail1: | |
Py_XDECREF(rdn); | |
fail0: | |
Py_XDECREF(dn); | |
return NULL; | |
} | |
static PyObject * | |
_get_peer_alt_names (X509 *certificate) { | |
/* this code follows the procedure outlined in | |
OpenSSL's crypto/x509v3/v3_prn.c:X509v3_EXT_print() | |
function to extract the STACK_OF(GENERAL_NAME), | |
then iterates through the stack to add the | |
names. */ | |
int i, j; | |
PyObject *peer_alt_names = Py_None; | |
PyObject *v, *t; | |
X509_EXTENSION *ext = NULL; | |
GENERAL_NAMES *names = NULL; | |
GENERAL_NAME *name; | |
const X509V3_EXT_METHOD *method; | |
BIO *biobuf = NULL; | |
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; | |
/* get a memory buffer */ | |
biobuf = BIO_new(BIO_s_mem()); | |
i = 0; | |
while ((i = X509_get_ext_by_NID( | |
certificate, NID_subject_alt_name, i)) >= 0) { | |
if (peer_alt_names == Py_None) { | |
peer_alt_names = PyList_New(0); | |
if (peer_alt_names == NULL) | |
goto fail; | |
} | |
/* now decode the altName */ | |
ext = X509_get_ext(certificate, i); | |
if(!(method = X509V3_EXT_get(ext))) { | |
PyErr_SetString(PySSLErrorObject, | |
ERRSTR("No method for internalizing subjectAltName!")); | |
goto fail; | |
} | |
p = ext->value->data; | |
if (method->it) | |
names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, | |
&p, | |
ext->value->length, | |
ASN1_ITEM_ptr(method->it))); | |
else | |
names = (GENERAL_NAMES*) (method->d2i(NULL, | |
&p, | |
ext->value->length)); | |
for(j = 0; j < sk_GENERAL_NAME_num(names); j++) { | |
/* get a rendering of each name in the set of names */ | |
name = sk_GENERAL_NAME_value(names, j); | |
if (name->type == GEN_DIRNAME) { | |
/* we special-case DirName as a tuple of tuples of attributes */ | |
t = PyTuple_New(2); | |
if (t == NULL) { | |
goto fail; | |
} | |
v = PyString_FromString("DirName"); | |
if (v == NULL) { | |
Py_DECREF(t); | |
goto fail; | |
} | |
PyTuple_SET_ITEM(t, 0, v); | |
v = _create_tuple_for_X509_NAME (name->d.dirn); | |
if (v == NULL) { | |
Py_DECREF(t); | |
goto fail; | |
} | |
PyTuple_SET_ITEM(t, 1, v); | |
} else { | |
/* for everything else, we use the OpenSSL print form */ | |
(void) BIO_reset(biobuf); | |
GENERAL_NAME_print(biobuf, name); | |
len = BIO_gets(biobuf, buf, sizeof(buf)-1); | |
if (len < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
goto fail; | |
} | |
vptr = strchr(buf, ':'); | |
if (vptr == NULL) | |
goto fail; | |
t = PyTuple_New(2); | |
if (t == NULL) | |
goto fail; | |
v = PyString_FromStringAndSize(buf, (vptr - buf)); | |
if (v == NULL) { | |
Py_DECREF(t); | |
goto fail; | |
} | |
PyTuple_SET_ITEM(t, 0, v); | |
v = PyString_FromStringAndSize((vptr + 1), (len - (vptr - buf + 1))); | |
if (v == NULL) { | |
Py_DECREF(t); | |
goto fail; | |
} | |
PyTuple_SET_ITEM(t, 1, v); | |
} | |
/* and add that rendering to the list */ | |
if (PyList_Append(peer_alt_names, t) < 0) { | |
Py_DECREF(t); | |
goto fail; | |
} | |
Py_DECREF(t); | |
} | |
} | |
BIO_free(biobuf); | |
if (peer_alt_names != Py_None) { | |
v = PyList_AsTuple(peer_alt_names); | |
Py_DECREF(peer_alt_names); | |
return v; | |
} else { | |
return peer_alt_names; | |
} | |
fail: | |
if (biobuf != NULL) | |
BIO_free(biobuf); | |
if (peer_alt_names != Py_None) { | |
Py_XDECREF(peer_alt_names); | |
} | |
return NULL; | |
} | |
static PyObject * | |
_decode_certificate (X509 *certificate, int verbose) { | |
PyObject *retval = NULL; | |
BIO *biobuf = NULL; | |
PyObject *peer; | |
PyObject *peer_alt_names = NULL; | |
PyObject *issuer; | |
PyObject *version; | |
PyObject *sn_obj; | |
ASN1_INTEGER *serialNumber; | |
char buf[2048]; | |
int len; | |
ASN1_TIME *notBefore, *notAfter; | |
PyObject *pnotBefore, *pnotAfter; | |
retval = PyDict_New(); | |
if (retval == NULL) | |
return NULL; | |
peer = _create_tuple_for_X509_NAME( | |
X509_get_subject_name(certificate)); | |
if (peer == NULL) | |
goto fail0; | |
if (PyDict_SetItemString(retval, (const char *) "subject", peer) < 0) { | |
Py_DECREF(peer); | |
goto fail0; | |
} | |
Py_DECREF(peer); | |
if (verbose) { | |
issuer = _create_tuple_for_X509_NAME( | |
X509_get_issuer_name(certificate)); | |
if (issuer == NULL) | |
goto fail0; | |
if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) { | |
Py_DECREF(issuer); | |
goto fail0; | |
} | |
Py_DECREF(issuer); | |
version = PyInt_FromLong(X509_get_version(certificate) + 1); | |
if (PyDict_SetItemString(retval, "version", version) < 0) { | |
Py_DECREF(version); | |
goto fail0; | |
} | |
Py_DECREF(version); | |
} | |
/* get a memory buffer */ | |
biobuf = BIO_new(BIO_s_mem()); | |
if (verbose) { | |
(void) BIO_reset(biobuf); | |
serialNumber = X509_get_serialNumber(certificate); | |
/* should not exceed 20 octets, 160 bits, so buf is big enough */ | |
i2a_ASN1_INTEGER(biobuf, serialNumber); | |
len = BIO_gets(biobuf, buf, sizeof(buf)-1); | |
if (len < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
goto fail1; | |
} | |
sn_obj = PyString_FromStringAndSize(buf, len); | |
if (sn_obj == NULL) | |
goto fail1; | |
if (PyDict_SetItemString(retval, "serialNumber", sn_obj) < 0) { | |
Py_DECREF(sn_obj); | |
goto fail1; | |
} | |
Py_DECREF(sn_obj); | |
(void) BIO_reset(biobuf); | |
notBefore = X509_get_notBefore(certificate); | |
ASN1_TIME_print(biobuf, notBefore); | |
len = BIO_gets(biobuf, buf, sizeof(buf)-1); | |
if (len < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
goto fail1; | |
} | |
pnotBefore = PyString_FromStringAndSize(buf, len); | |
if (pnotBefore == NULL) | |
goto fail1; | |
if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) { | |
Py_DECREF(pnotBefore); | |
goto fail1; | |
} | |
Py_DECREF(pnotBefore); | |
} | |
(void) BIO_reset(biobuf); | |
notAfter = X509_get_notAfter(certificate); | |
ASN1_TIME_print(biobuf, notAfter); | |
len = BIO_gets(biobuf, buf, sizeof(buf)-1); | |
if (len < 0) { | |
_setSSLError(NULL, 0, __FILE__, __LINE__); | |
goto fail1; | |
} | |
pnotAfter = PyString_FromStringAndSize(buf, len); | |
if (pnotAfter == NULL) | |
goto fail1; | |
if (PyDict_SetItemString(retval, "notAfter", pnotAfter) < 0) { | |
Py_DECREF(pnotAfter); | |
goto fail1; | |
} | |
Py_DECREF(pnotAfter); | |
/* Now look for subjectAltName */ | |
peer_alt_names = _get_peer_alt_names(certificate); | |
if (peer_alt_names == NULL) | |
goto fail1; | |
else if (peer_alt_names != Py_None) { | |
if (PyDict_SetItemString(retval, "subjectAltName", | |
peer_alt_names) < 0) { | |
Py_DECREF(peer_alt_names); | |
goto fail1; | |
} | |
Py_DECREF(peer_alt_names); | |
} | |
BIO_free(biobuf); | |
return retval; | |
fail1: | |
if (biobuf != NULL) | |
BIO_free(biobuf); | |
fail0: | |
Py_XDECREF(retval); | |
return NULL; | |
} | |
static PyObject * | |
PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { | |
PyObject *retval = NULL; | |
char *filename = NULL; | |
X509 *x=NULL; | |
BIO *cert; | |
int verbose = 1; | |
if (!PyArg_ParseTuple(args, "s|i:test_decode_certificate", &filename, &verbose)) | |
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,filename) <= 0) { | |
PyErr_SetString(PySSLErrorObject, "Can't open file"); | |
goto fail0; | |
} | |
x = PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL); | |
if (x == NULL) { | |
PyErr_SetString(PySSLErrorObject, "Error decoding PEM-encoded file"); | |
goto fail0; | |
} | |
retval = _decode_certificate(x, verbose); | |
X509_free(x); | |
fail0: | |
if (cert != NULL) BIO_free(cert); | |
return retval; | |
} | |
static PyObject * | |
PySSL_peercert(PySSLObject *self, PyObject *args) | |
{ | |
PyObject *retval = NULL; | |
int len; | |
int verification; | |
PyObject *binary_mode = Py_None; | |
if (!PyArg_ParseTuple(args, "|O:peer_certificate", &binary_mode)) | |
return NULL; | |
if (!self->peer_cert) | |
Py_RETURN_NONE; | |
if (PyObject_IsTrue(binary_mode)) { | |
/* return cert in DER-encoded format */ | |
unsigned char *bytes_buf = NULL; | |
bytes_buf = NULL; | |
len = i2d_X509(self->peer_cert, &bytes_buf); | |
if (len < 0) { | |
PySSL_SetError(self, len, __FILE__, __LINE__); | |
return NULL; | |
} | |
retval = PyString_FromStringAndSize((const char *) bytes_buf, len); | |
OPENSSL_free(bytes_buf); | |
return retval; | |
} else { | |
verification = SSL_CTX_get_verify_mode(self->ctx); | |
if ((verification & SSL_VERIFY_PEER) == 0) | |
return PyDict_New(); | |
else | |
return _decode_certificate (self->peer_cert, 0); | |
} | |
} | |
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 (PySSLObject *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); | |
if (retval == NULL) | |
return NULL; | |
cipher_name = (char *) SSL_CIPHER_get_name(current); | |
if (cipher_name == NULL) { | |
Py_INCREF(Py_None); | |
PyTuple_SET_ITEM(retval, 0, Py_None); | |
} else { | |
v = PyString_FromString(cipher_name); | |
if (v == NULL) | |
goto fail0; | |
PyTuple_SET_ITEM(retval, 0, v); | |
} | |
cipher_protocol = SSL_CIPHER_get_version(current); | |
if (cipher_protocol == NULL) { | |
Py_INCREF(Py_None); | |
PyTuple_SET_ITEM(retval, 1, Py_None); | |
} else { | |
v = PyString_FromString(cipher_protocol); | |
if (v == NULL) | |
goto fail0; | |
PyTuple_SET_ITEM(retval, 1, v); | |
} | |
v = PyInt_FromLong(SSL_CIPHER_get_bits(current, NULL)); | |
if (v == NULL) | |
goto fail0; | |
PyTuple_SET_ITEM(retval, 2, v); | |
return retval; | |
fail0: | |
Py_DECREF(retval); | |
return NULL; | |
} | |
static void PySSL_dealloc(PySSLObject *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); | |
} | |
/* If the socket has a timeout, do a select()/poll() on the socket. | |
The argument writing indicates the direction. | |
Returns one of the possibilities in the timeout_state enum (above). | |
*/ | |
static int | |
check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) | |
{ | |
fd_set fds; | |
struct timeval tv; | |
int rc; | |
/* 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) | |
return SOCKET_IS_NONBLOCKING; | |
/* Guard against closed socket */ | |
if (s->sock_fd < 0) | |
return SOCKET_HAS_BEEN_CLOSED; | |
/* 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 | |
goto normal_return; | |
} | |
#endif | |
/* Guard against socket too large for select*/ | |
#ifndef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE | |
if (s->sock_fd >= FD_SETSIZE) | |
return SOCKET_TOO_LARGE_FOR_SELECT; | |
#endif | |
/* Construct the arguments to select */ | |
tv.tv_sec = (int)s->sock_timeout; | |
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); | |
FD_ZERO(&fds); | |
FD_SET(s->sock_fd, &fds); | |
/* See if the socket is ready */ | |
PySSL_BEGIN_ALLOW_THREADS | |
if (writing) | |
rc = select(s->sock_fd+1, NULL, &fds, NULL, &tv); | |
else | |
rc = select(s->sock_fd+1, &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(PySSLObject *self, PyObject *args) | |
{ | |
Py_buffer buf; | |
int len; | |
int sockstate; | |
int err; | |
int nonblocking; | |
if (!PyArg_ParseTuple(args, "s*:write", &buf)) | |
return NULL; | |
/* just in case the blocking state of the socket has been changed */ | |
nonblocking = (self->Socket->sock_timeout >= 0.0); | |
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); | |
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
PyErr_SetString(PySSLErrorObject, | |
"The write operation timed out"); | |
goto error; | |
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket has been closed."); | |
goto error; | |
} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket too large for select()."); | |
goto error; | |
} | |
do { | |
PySSL_BEGIN_ALLOW_THREADS | |
len = SSL_write(self->ssl, buf.buf, buf.len); | |
err = SSL_get_error(self->ssl, len); | |
PySSL_END_ALLOW_THREADS | |
if (PyErr_CheckSignals()) { | |
goto error; | |
} | |
if (err == SSL_ERROR_WANT_READ) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); | |
} else if (err == SSL_ERROR_WANT_WRITE) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); | |
} else { | |
sockstate = SOCKET_OPERATION_OK; | |
} | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
PyErr_SetString(PySSLErrorObject, | |
"The write operation timed out"); | |
goto error; | |
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket has been closed."); | |
goto error; | |
} else if (sockstate == SOCKET_IS_NONBLOCKING) { | |
break; | |
} | |
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); | |
PyBuffer_Release(&buf); | |
if (len > 0) | |
return PyInt_FromLong(len); | |
else | |
return PySSL_SetError(self, len, __FILE__, __LINE__); | |
error: | |
PyBuffer_Release(&buf); | |
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."); | |
static PyObject *PySSL_SSLpending(PySSLObject *self) | |
{ | |
int count = 0; | |
PySSL_BEGIN_ALLOW_THREADS | |
count = SSL_pending(self->ssl); | |
PySSL_END_ALLOW_THREADS | |
if (count < 0) | |
return PySSL_SetError(self, count, __FILE__, __LINE__); | |
else | |
return PyInt_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"); | |
static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) | |
{ | |
PyObject *buf; | |
int count = 0; | |
int len = 1024; | |
int sockstate; | |
int err; | |
int nonblocking; | |
if (!PyArg_ParseTuple(args, "|i:read", &len)) | |
return NULL; | |
if (!(buf = PyString_FromStringAndSize((char *) 0, len))) | |
return NULL; | |
/* just in case the blocking state of the socket has been changed */ | |
nonblocking = (self->Socket->sock_timeout >= 0.0); | |
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); | |
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); | |
/* first check if there are bytes ready to be read */ | |
PySSL_BEGIN_ALLOW_THREADS | |
count = SSL_pending(self->ssl); | |
PySSL_END_ALLOW_THREADS | |
if (!count) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
PyErr_SetString(PySSLErrorObject, | |
"The read operation timed out"); | |
Py_DECREF(buf); | |
return NULL; | |
} else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket too large for select()."); | |
Py_DECREF(buf); | |
return NULL; | |
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { | |
if (SSL_get_shutdown(self->ssl) != | |
SSL_RECEIVED_SHUTDOWN) | |
{ | |
Py_DECREF(buf); | |
PyErr_SetString(PySSLErrorObject, | |
"Socket closed without SSL shutdown handshake"); | |
return NULL; | |
} else { | |
/* should contain a zero-length string */ | |
_PyString_Resize(&buf, 0); | |
return buf; | |
} | |
} | |
} | |
do { | |
PySSL_BEGIN_ALLOW_THREADS | |
count = SSL_read(self->ssl, PyString_AsString(buf), len); | |
err = SSL_get_error(self->ssl, count); | |
PySSL_END_ALLOW_THREADS | |
if(PyErr_CheckSignals()) { | |
Py_DECREF(buf); | |
return NULL; | |
} | |
if (err == SSL_ERROR_WANT_READ) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); | |
} else if (err == SSL_ERROR_WANT_WRITE) { | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); | |
} else if ((err == SSL_ERROR_ZERO_RETURN) && | |
(SSL_get_shutdown(self->ssl) == | |
SSL_RECEIVED_SHUTDOWN)) | |
{ | |
_PyString_Resize(&buf, 0); | |
return buf; | |
} else { | |
sockstate = SOCKET_OPERATION_OK; | |
} | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
PyErr_SetString(PySSLErrorObject, | |
"The read operation timed out"); | |
Py_DECREF(buf); | |
return NULL; | |
} else if (sockstate == SOCKET_IS_NONBLOCKING) { | |
break; | |
} | |
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); | |
if (count <= 0) { | |
Py_DECREF(buf); | |
return PySSL_SetError(self, count, __FILE__, __LINE__); | |
} | |
if (count != len) | |
_PyString_Resize(&buf, count); | |
return buf; | |
} | |
PyDoc_STRVAR(PySSL_SSLread_doc, | |
"read([len]) -> string\n\ | |
\n\ | |
Read up to len bytes from the SSL socket."); | |
static PyObject *PySSL_SSLshutdown(PySSLObject *self) | |
{ | |
int err, ssl_err, sockstate, nonblocking; | |
int zeros = 0; | |
/* Guard against closed socket */ | |
if (self->Socket->sock_fd < 0) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket has been closed."); | |
return NULL; | |
} | |
/* Just in case the blocking state of the socket has been changed */ | |
nonblocking = (self->Socket->sock_timeout >= 0.0); | |
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); | |
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); | |
while (1) { | |
PySSL_BEGIN_ALLOW_THREADS | |
/* Disable read-ahead so that unwrap can work correctly. | |
* Otherwise OpenSSL might read in too much data, | |
* eating clear text data that happens to be | |
* transmitted after the SSL shutdown. | |
* Should be safe to call repeatedly everytime this | |
* function is used and the shutdown_seen_zero != 0 | |
* condition is met. | |
*/ | |
if (self->shutdown_seen_zero) | |
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; | |
if (err == 0) { | |
/* Don't loop endlessly; instead preserve legacy | |
behaviour of trying SSL_shutdown() only twice. | |
This looks necessary for OpenSSL < 0.9.8m */ | |
if (++zeros > 1) | |
break; | |
/* Shutdown was sent, now try receiving */ | |
self->shutdown_seen_zero = 1; | |
continue; | |
} | |
/* 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(self->Socket, 0); | |
else if (ssl_err == SSL_ERROR_WANT_WRITE) | |
sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); | |
else | |
break; | |
if (sockstate == SOCKET_HAS_TIMED_OUT) { | |
if (ssl_err == SSL_ERROR_WANT_READ) | |
PyErr_SetString(PySSLErrorObject, | |
"The read operation timed out"); | |
else | |
PyErr_SetString(PySSLErrorObject, | |
"The write operation timed out"); | |
return NULL; | |
} | |
else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { | |
PyErr_SetString(PySSLErrorObject, | |
"Underlying socket too large for select()."); | |
return NULL; | |
} | |
else if (sockstate != SOCKET_OPERATION_OK) | |
/* Retain the SSL error code */ | |
break; | |
} | |
if (err < 0) | |
return PySSL_SetError(self, err, __FILE__, __LINE__); | |
else { | |
Py_INCREF(self->Socket); | |
return (PyObject *) (self->Socket); | |
} | |
} | |
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."); | |
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}, | |
{"server", (PyCFunction)PySSL_server, METH_NOARGS}, | |
{"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, | |
{"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS, | |
PySSL_peercert_doc}, | |
{"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS}, | |
{"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, | |
PySSL_SSLshutdown_doc}, | |
{NULL, NULL} | |
}; | |
static PyObject *PySSL_getattr(PySSLObject *self, char *name) | |
{ | |
return Py_FindMethod(PySSLMethods, (PyObject *)self, name); | |
} | |
static PyTypeObject PySSL_Type = { | |
PyVarObject_HEAD_INIT(NULL, 0) | |
"ssl.SSLContext", /*tp_name*/ | |
sizeof(PySSLObject), /*tp_basicsize*/ | |
0, /*tp_itemsize*/ | |
/* methods */ | |
(destructor)PySSL_dealloc, /*tp_dealloc*/ | |
0, /*tp_print*/ | |
(getattrfunc)PySSL_getattr, /*tp_getattr*/ | |
0, /*tp_setattr*/ | |
0, /*tp_compare*/ | |
0, /*tp_repr*/ | |
0, /*tp_as_number*/ | |
0, /*tp_as_sequence*/ | |
0, /*tp_as_mapping*/ | |
0, /*tp_hash*/ | |
}; | |
#ifdef HAVE_OPENSSL_RAND | |
/* helper routines for seeding the SSL PRNG */ | |
static PyObject * | |
PySSL_RAND_add(PyObject *self, PyObject *args) | |
{ | |
char *buf; | |
int len; | |
double entropy; | |
if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) | |
return NULL; | |
RAND_add(buf, len, entropy); | |
Py_INCREF(Py_None); | |
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_status(PyObject *self) | |
{ | |
return PyInt_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."); | |
static PyObject * | |
PySSL_RAND_egd(PyObject *self, PyObject *arg) | |
{ | |
int bytes; | |
if (!PyString_Check(arg)) | |
return PyErr_Format(PyExc_TypeError, | |
"RAND_egd() expected string, found %s", | |
Py_TYPE(arg)->tp_name); | |
bytes = RAND_egd(PyString_AS_STRING(arg)); | |
if (bytes == -1) { | |
PyErr_SetString(PySSLErrorObject, | |
"EGD connection failed or EGD did not return " | |
"enough data to seed the PRNG"); | |
return NULL; | |
} | |
return PyInt_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 provide enough data to seed PRNG."); | |
#endif | |
/* 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 | |
{"RAND_add", PySSL_RAND_add, METH_VARARGS, | |
PySSL_RAND_add_doc}, | |
{"RAND_egd", PySSL_RAND_egd, METH_O, | |
PySSL_RAND_egd_doc}, | |
{"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, | |
PySSL_RAND_status_doc}, | |
#endif | |
{NULL, NULL} /* Sentinel */ | |
}; | |
#ifdef WITH_THREAD | |
/* an implementation of OpenSSL threading operations in terms | |
of the Python C thread library */ | |
static PyThread_type_lock *_ssl_locks = NULL; | |
static unsigned long _ssl_thread_id_function (void) { | |
return PyThread_get_thread_ident(); | |
} | |
static void _ssl_thread_locking_function (int mode, int n, const char *file, int line) { | |
/* this function is needed to perform locking on shared data | |
structures. (Note that OpenSSL uses a number of global data | |
structures that will be implicitly shared whenever multiple threads | |
use OpenSSL.) Multi-threaded applications will crash at random if | |
it is not set. | |
locking_function() must be able to handle up to CRYPTO_num_locks() | |
different mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and | |
releases it otherwise. | |
file and line are the file number of the function setting the | |
lock. They can be useful for debugging. | |
*/ | |
if ((_ssl_locks == NULL) || | |
(n < 0) || ((unsigned)n >= _ssl_locks_count)) | |
return; | |
if (mode & CRYPTO_LOCK) { | |
PyThread_acquire_lock(_ssl_locks[n], 1); | |
} else { | |
PyThread_release_lock(_ssl_locks[n]); | |
} | |
} | |
static int _setup_ssl_threads(void) { | |
unsigned int i; | |
if (_ssl_locks == NULL) { | |
_ssl_locks_count = CRYPTO_num_locks(); | |
_ssl_locks = (PyThread_type_lock *) | |
malloc(sizeof(PyThread_type_lock) * _ssl_locks_count); | |
if (_ssl_locks == NULL) | |
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) { | |
unsigned int j; | |
for (j = 0; j < i; j++) { | |
PyThread_free_lock(_ssl_locks[j]); | |
} | |
free(_ssl_locks); | |
return 0; | |
} | |
} | |
CRYPTO_set_locking_callback(_ssl_thread_locking_function); | |
CRYPTO_set_id_callback(_ssl_thread_id_function); | |
} | |
return 1; | |
} | |
#endif /* def HAVE_THREAD */ | |
PyDoc_STRVAR(module_doc, | |
"Implementation module for SSL socket operations. See the socket module\n\ | |
for documentation."); | |
PyMODINIT_FUNC | |
init_ssl(void) | |
{ | |
PyObject *m, *d, *r; | |
unsigned long libver; | |
unsigned int major, minor, fix, patch, status; | |
Py_TYPE(&PySSL_Type) = &PyType_Type; | |
m = Py_InitModule3("_ssl", PySSL_methods, module_doc); | |
if (m == NULL) | |
return; | |
d = PyModule_GetDict(m); | |
/* Load _socket module and its C API */ | |
if (PySocketModule_ImportModuleAndAPI()) | |
return; | |
/* Init OpenSSL */ | |
SSL_load_error_strings(); | |
SSL_library_init(); | |
#ifdef WITH_THREAD | |
/* note that this will start threading if not already started */ | |
if (!_setup_ssl_threads()) { | |
return; | |
} | |
#endif | |
OpenSSL_add_all_algorithms(); | |
/* Add symbols to module dict */ | |
PySSLErrorObject = PyErr_NewException("ssl.SSLError", | |
PySocketModule.error, | |
NULL); | |
if (PySSLErrorObject == NULL) | |
return; | |
if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0) | |
return; | |
if (PyDict_SetItemString(d, "SSLType", | |
(PyObject *)&PySSL_Type) != 0) | |
return; | |
PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", | |
PY_SSL_ERROR_ZERO_RETURN); | |
PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", | |
PY_SSL_ERROR_WANT_READ); | |
PyModule_AddIntConstant(m, "SSL_ERROR_WANT_WRITE", | |
PY_SSL_ERROR_WANT_WRITE); | |
PyModule_AddIntConstant(m, "SSL_ERROR_WANT_X509_LOOKUP", | |
PY_SSL_ERROR_WANT_X509_LOOKUP); | |
PyModule_AddIntConstant(m, "SSL_ERROR_SYSCALL", | |
PY_SSL_ERROR_SYSCALL); | |
PyModule_AddIntConstant(m, "SSL_ERROR_SSL", | |
PY_SSL_ERROR_SSL); | |
PyModule_AddIntConstant(m, "SSL_ERROR_WANT_CONNECT", | |
PY_SSL_ERROR_WANT_CONNECT); | |
/* non ssl.h errorcodes */ | |
PyModule_AddIntConstant(m, "SSL_ERROR_EOF", | |
PY_SSL_ERROR_EOF); | |
PyModule_AddIntConstant(m, "SSL_ERROR_INVALID_ERROR_CODE", | |
PY_SSL_ERROR_INVALID_ERROR_CODE); | |
/* cert requirements */ | |
PyModule_AddIntConstant(m, "CERT_NONE", | |
PY_SSL_CERT_NONE); | |
PyModule_AddIntConstant(m, "CERT_OPTIONAL", | |
PY_SSL_CERT_OPTIONAL); | |
PyModule_AddIntConstant(m, "CERT_REQUIRED", | |
PY_SSL_CERT_REQUIRED); | |
/* protocol versions */ | |
#ifndef OPENSSL_NO_SSL2 | |
PyModule_AddIntConstant(m, "PROTOCOL_SSLv2", | |
PY_SSL_VERSION_SSL2); | |
#endif | |
PyModule_AddIntConstant(m, "PROTOCOL_SSLv3", | |
PY_SSL_VERSION_SSL3); | |
PyModule_AddIntConstant(m, "PROTOCOL_SSLv23", | |
PY_SSL_VERSION_SSL23); | |
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", | |
PY_SSL_VERSION_TLS1); | |
/* OpenSSL version */ | |
/* SSLeay() gives us the version of the library linked against, | |
which could be different from the headers version. | |
*/ | |
libver = SSLeay(); | |
r = PyLong_FromUnsignedLong(libver); | |
if (r == NULL) | |
return; | |
if (PyModule_AddObject(m, "OPENSSL_VERSION_NUMBER", r)) | |
return; | |
status = libver & 0xF; | |
libver >>= 4; | |
patch = libver & 0xFF; | |
libver >>= 8; | |
fix = libver & 0xFF; | |
libver >>= 8; | |
minor = libver & 0xFF; | |
libver >>= 8; | |
major = libver & 0xFF; | |
r = Py_BuildValue("IIIII", major, minor, fix, patch, status); | |
if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION_INFO", r)) | |
return; | |
r = PyString_FromString(SSLeay_version(SSLEAY_VERSION)); | |
if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION", r)) | |
return; | |
} |