diff options
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_ssl.c | 643 | ||||
-rw-r--r-- | Modules/arraymodule.c | 228 | ||||
-rw-r--r-- | Modules/main.c | 9 | ||||
-rw-r--r-- | Modules/mmapmodule.c | 154 |
4 files changed, 776 insertions, 258 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 177325c..a31030a 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1,4 +1,4 @@ -/* SSL socket module +/* SSL socket module SSL support based on patches by Brian E Gallew and Laszlo Kovacs. @@ -8,25 +8,44 @@ */ #include "Python.h" + 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_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_ZERO_RETURN, PY_SSL_ERROR_WANT_CONNECT, - /* start of non ssl.h errorcodes */ + /* 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 { + PY_SSL_VERSION_SSL2, + PY_SSL_VERSION_SSL3, + PY_SSL_VERSION_SSL23, + PY_SSL_VERSION_TLS1, +}; + /* Include symbols from _socket module */ #include "socketmodule.h" -#if defined(HAVE_POLL_H) +#if defined(HAVE_POLL_H) #include <poll.h> #elif defined(HAVE_SYS_POLL_H) #include <sys/poll.h> @@ -58,10 +77,10 @@ static PyObject *PySSLErrorObject; typedef struct { PyObject_HEAD PySocketSockObject *Socket; /* Socket on which we're layered */ - SSL_CTX* ctx; - SSL* ssl; - X509* server_cert; - char server[X509_NAME_MAXLEN]; + SSL_CTX* ctx; + SSL* ssl; + X509* peer_cert; + char server[X509_NAME_MAXLEN]; char issuer[X509_NAME_MAXLEN]; } PySSLObject; @@ -69,8 +88,10 @@ typedef struct { 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, +static int check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing); +static PyObject *PySSL_peercert(PySSLObject *self); + #define PySSLObject_Check(v) (Py_Type(v) == &PySSL_Type) @@ -83,141 +104,181 @@ typedef enum { 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) +PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno) { - PyObject *v, *n, *s; + PyObject *v; + char buf[2048]; char *errstr; int err; - enum py_ssl_error p; + enum py_ssl_error p = PY_SSL_ERROR_NONE; assert(ret <= 0); - - 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) { - /* the underlying BIO reported an I/O error */ - return obj->Socket->errorhandler(); - } else { /* possible? */ + + if ((obj != NULL) && (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 */ + return obj->Socket->errorhandler(); + } else { /* possible? */ + p = PY_SSL_ERROR_SYSCALL; + errstr = "Some I/O error occurred"; + } + } else { p = PY_SSL_ERROR_SYSCALL; - errstr = "Some I/O error occurred"; + /* XXX Protected by global interpreter lock */ + errstr = ERR_error_string(e, NULL); } - } else { - p = PY_SSL_ERROR_SYSCALL; - /* XXX Protected by global interpreter lock */ - errstr = ERR_error_string(e, NULL); + break; } - 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"; + 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; } - break; - } - default: - p = PY_SSL_ERROR_INVALID_ERROR_CODE; - errstr = "Invalid error code"; - } - n = PyInt_FromLong((long) p); - if (n == NULL) - return NULL; - v = PyTuple_New(2); - if (v == NULL) { - Py_DECREF(n); - return NULL; + default: + p = PY_SSL_ERROR_INVALID_ERROR_CODE; + errstr = "Invalid error code"; + } + } else { + errstr = ERR_error_string(ERR_peek_last_error(), NULL); } - - s = PyUnicode_FromString(errstr); - if (s == NULL) { + PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr); + v = Py_BuildValue("(is)", p, buf); + if (v != NULL) { + PyErr_SetObject(PySSLErrorObject, v); Py_DECREF(v); - Py_DECREF(n); } - PyTuple_SET_ITEM(v, 0, n); - PyTuple_SET_ITEM(v, 1, s); - PyErr_SetObject(PySSLErrorObject, v); - Py_DECREF(v); return NULL; } static PySSLObject * -newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) +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) { PySSLObject *self; char *errstr = NULL; int ret; int err; int sockstate; + 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->server_cert = NULL; + self->peer_cert = NULL; self->ssl = NULL; self->ctx = NULL; self->Socket = NULL; if ((key_file && !cert_file) || (!key_file && cert_file)) { - errstr = "Both the key & certificate files must be specified"; + 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; } Py_BEGIN_ALLOW_THREADS - self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ + 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 + self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ Py_END_ALLOW_THREADS + if (self->ctx == NULL) { - errstr = "SSL_CTX_new error"; + errstr = ERRSTR("Invalid SSL protocol variant specified."); 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 { + Py_BEGIN_ALLOW_THREADS + ret = SSL_CTX_load_verify_locations(self->ctx, + cacerts_file, + NULL); + Py_END_ALLOW_THREADS + if (ret != 1) { + PySSL_SetError(NULL, 0, __FILE__, __LINE__); + goto fail; + } + } + } if (key_file) { Py_BEGIN_ALLOW_THREADS ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file, - SSL_FILETYPE_PEM); + SSL_FILETYPE_PEM); Py_END_ALLOW_THREADS - if (ret < 1) { - errstr = "SSL_CTX_use_PrivateKey_file error"; + if (ret != 1) { + PySSL_SetError(NULL, 0, __FILE__, __LINE__); goto fail; } @@ -225,16 +286,24 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) ret = SSL_CTX_use_certificate_chain_file(self->ctx, cert_file); Py_END_ALLOW_THREADS - SSL_CTX_set_options(self->ctx, SSL_OP_ALL); /* ssl compatibility */ - if (ret < 1) { - errstr = "SSL_CTX_use_certificate_chain_file error"; + if (ret != 1) { + PySSL_SetError(NULL, 0, __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 */ + Py_BEGIN_ALLOW_THREADS - SSL_CTX_set_verify(self->ctx, - SSL_VERIFY_NONE, NULL); /* set verify lvl */ self->ssl = SSL_new(self->ctx); /* New ssl struct */ Py_END_ALLOW_THREADS SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ @@ -249,7 +318,10 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) } Py_BEGIN_ALLOW_THREADS - SSL_set_connect_state(self->ssl); + if (socket_type == PY_SSL_CLIENT) + SSL_set_connect_state(self->ssl); + else + SSL_set_accept_state(self->ssl); Py_END_ALLOW_THREADS /* Actually negotiate SSL connection */ @@ -257,11 +329,14 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) sockstate = 0; do { Py_BEGIN_ALLOW_THREADS - ret = SSL_connect(self->ssl); + if (socket_type == PY_SSL_CLIENT) + ret = SSL_connect(self->ssl); + else + ret = SSL_accept(self->ssl); err = SSL_get_error(self->ssl, ret); Py_END_ALLOW_THREADS if(PyErr_CheckSignals()) { - goto fail; + goto fail; } if (err == SSL_ERROR_WANT_READ) { sockstate = check_socket_and_wait_for_timeout(Sock, 0); @@ -270,30 +345,33 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) } else { sockstate = SOCKET_OPERATION_OK; } - if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySSLErrorObject, "The connect operation timed out"); + if (sockstate == SOCKET_HAS_TIMED_OUT) { + PyErr_SetString(PySSLErrorObject, + ERRSTR("The connect operation timed out")); goto fail; } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed."); + PyErr_SetString(PySSLErrorObject, + ERRSTR("Underlying socket has been closed.")); goto fail; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); + PyErr_SetString(PySSLErrorObject, + ERRSTR("Underlying socket too large for select().")); goto fail; } else if (sockstate == SOCKET_IS_NONBLOCKING) { break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); - if (ret <= 0) { - PySSL_SetError(self, ret); + if (ret < 1) { + PySSL_SetError(self, ret, __FILE__, __LINE__); goto fail; } self->ssl->debug = 1; Py_BEGIN_ALLOW_THREADS - if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) { - X509_NAME_oneline(X509_get_subject_name(self->server_cert), + 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->server_cert), + X509_NAME_oneline(X509_get_issuer_name(self->peer_cert), self->issuer, X509_NAME_MAXLEN); } Py_END_ALLOW_THREADS @@ -308,27 +386,41 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) } static PyObject * -PySocket_ssl(PyObject *self, PyObject *args) +PySSL_sslwrap(PyObject *self, PyObject *args) { - PySSLObject *rv; 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; - if (!PyArg_ParseTuple(args, "O!|zz:ssl", + if (!PyArg_ParseTuple(args, "O!i|zziiz:sslwrap", PySocketModule.Sock_Type, &Sock, - &key_file, &cert_file)) + &server_side, + &key_file, &cert_file, + &verification_mode, &protocol, + &cacerts_file)) return NULL; - rv = newPySSLObject(Sock, key_file, cert_file); - if (rv == NULL) - return NULL; - return (PyObject *)rv; + /* + 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); } PyDoc_STRVAR(ssl_doc, -"ssl(socket, [keyfile, certfile]) -> sslobject"); +"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n" +" cacertsfile]) -> sslobject"); /* SSL object methods */ @@ -344,15 +436,163 @@ PySSL_issuer(PySSLObject *self) return PyUnicode_FromString(self->issuer); } +static PyObject * +_create_dict_for_X509_NAME (X509_NAME *xname) +{ + PyObject *pd = PyDict_New(); + int index_counter; + + if (pd == NULL) + return NULL; + + for (index_counter = 0; + index_counter < X509_NAME_entry_count(xname); + index_counter++) + { + char namebuf[X509_NAME_MAXLEN]; + int buflen; + PyObject *name_obj; + ASN1_STRING *value; + PyObject *value_obj; + unsigned char *valuebuf = NULL; + + X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, + index_counter); + + ASN1_OBJECT *name = X509_NAME_ENTRY_get_object(entry); + buflen = OBJ_obj2txt(namebuf, sizeof(namebuf), name, 0); + if (buflen < 0) + goto fail0; + name_obj = PyString_FromStringAndSize(namebuf, buflen); + if (name_obj == NULL) + goto fail0; + + value = X509_NAME_ENTRY_get_data(entry); + buflen = ASN1_STRING_to_UTF8(&valuebuf, value); + if (buflen < 0) { + Py_DECREF(name_obj); + goto fail0; + } + value_obj = PyUnicode_DecodeUTF8((char *) valuebuf, + buflen, "strict"); + OPENSSL_free(valuebuf); + if (value_obj == NULL) { + Py_DECREF(name_obj); + goto fail0; + } + if (PyDict_SetItem(pd, name_obj, value_obj) < 0) { + Py_DECREF(name_obj); + Py_DECREF(value_obj); + goto fail0; + } + Py_DECREF(name_obj); + Py_DECREF(value_obj); + } + return pd; + + fail0: + Py_XDECREF(pd); + return NULL; +} + +static PyObject * +PySSL_peercert(PySSLObject *self) +{ + PyObject *retval = NULL; + BIO *biobuf = NULL; + PyObject *peer; + PyObject *issuer; + PyObject *version; + char buf[2048]; + int len; + ASN1_TIME *notBefore, *notAfter; + PyObject *pnotBefore, *pnotAfter; + int verification; + + if (!self->peer_cert) + Py_RETURN_NONE; + + retval = PyDict_New(); + if (retval == NULL) + return NULL; + + verification = SSL_CTX_get_verify_mode(self->ctx); + if ((verification & SSL_VERIFY_PEER) == 0) + return retval; + + peer = _create_dict_for_X509_NAME( + X509_get_subject_name(self->peer_cert)); + if (peer == NULL) + goto fail0; + if (PyDict_SetItemString(retval, (const char *) "subject", peer) < 0) { + Py_DECREF(peer); + goto fail0; + } + Py_DECREF(peer); + + issuer = _create_dict_for_X509_NAME( + X509_get_issuer_name(self->peer_cert)); + 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(self->peer_cert)); + 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()); + + notBefore = X509_get_notBefore(self->peer_cert); + ASN1_TIME_print(biobuf, notBefore); + len = BIO_gets(biobuf, buf, sizeof(buf)-1); + 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(self->peer_cert); + ASN1_TIME_print(biobuf, notAfter); + len = BIO_gets(biobuf, buf, sizeof(buf)-1); + BIO_free(biobuf); + pnotAfter = PyString_FromStringAndSize(buf, len); + if (pnotAfter == NULL) + goto fail0; + if (PyDict_SetItemString(retval, "notAfter", pnotAfter) < 0) { + Py_DECREF(pnotAfter); + goto fail0; + } + Py_DECREF(pnotAfter); + return retval; + + fail1: + if (biobuf != NULL) + BIO_free(biobuf); + fail0: + Py_XDECREF(retval); + return NULL; +} static void PySSL_dealloc(PySSLObject *self) { - if (self->server_cert) /* Possible not to have one? */ - X509_free (self->server_cert); + if (self->peer_cert) /* Possible not to have one? */ + X509_free (self->peer_cert); if (self->ssl) - SSL_free(self->ssl); + SSL_free(self->ssl); if (self->ctx) - SSL_CTX_free(self->ctx); + SSL_CTX_free(self->ctx); Py_XDECREF(self->Socket); PyObject_Del(self); } @@ -438,13 +678,16 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); if (sockstate == SOCKET_HAS_TIMED_OUT) { - PyErr_SetString(PySSLErrorObject, "The write operation timed out"); + PyErr_SetString(PySSLErrorObject, + "The write operation timed out"); return NULL; } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed."); + PyErr_SetString(PySSLErrorObject, + "Underlying socket has been closed."); return NULL; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { - PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); + PyErr_SetString(PySSLErrorObject, + "Underlying socket too large for select()."); return NULL; } do { @@ -457,17 +700,21 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) return NULL; } if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + 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); + 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"); + if (sockstate == SOCKET_HAS_TIMED_OUT) { + PyErr_SetString(PySSLErrorObject, + "The write operation timed out"); return NULL; } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed."); + PyErr_SetString(PySSLErrorObject, + "Underlying socket has been closed."); return NULL; } else if (sockstate == SOCKET_IS_NONBLOCKING) { break; @@ -476,7 +723,7 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) if (len > 0) return PyInt_FromLong(len); else - return PySSL_SetError(self, len); + return PySSL_SetError(self, len, __FILE__, __LINE__); } PyDoc_STRVAR(PySSL_SSLwrite_doc, @@ -498,7 +745,7 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) if (!(buf = PyBytes_FromStringAndSize((char *) 0, len))) return NULL; - + /* first check if there are bytes ready to be read */ Py_BEGIN_ALLOW_THREADS count = SSL_pending(self->ssl); @@ -507,12 +754,28 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) 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"); + 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()."); + 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 { @@ -526,23 +789,32 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) return NULL; } if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + 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); + 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"); + 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) { + if (count <= 0) { Py_DECREF(buf); - return PySSL_SetError(self, count); + return PySSL_SetError(self, count, __FILE__, __LINE__); } if (count != len) if (PyBytes_Resize(buf, count) < 0) { @@ -557,13 +829,49 @@ PyDoc_STRVAR(PySSL_SSLread_doc, \n\ Read up to len bytes from the SSL socket."); +static PyObject *PySSL_SSLshutdown(PySSLObject *self, PyObject *args) +{ + int err; + + /* Guard against closed socket */ + if (self->Socket->sock_fd < 0) { + PyErr_SetString(PySSLErrorObject, + "Underlying socket has been closed."); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + err = SSL_shutdown(self->ssl); + if (err == 0) { + /* we need to call it again to finish the shutdown */ + err = SSL_shutdown(self->ssl); + } + Py_END_ALLOW_THREADS + + 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[] = { {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, - PySSL_SSLwrite_doc}, + PySSL_SSLwrite_doc}, {"read", (PyCFunction)PySSL_SSLread, METH_VARARGS, - PySSL_SSLread_doc}, + PySSL_SSLread_doc}, {"server", (PyCFunction)PySSL_server, METH_NOARGS}, {"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, + {"peer_certificate", (PyCFunction)PySSL_peercert, METH_NOARGS}, + {"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, + PySSL_SSLshutdown_doc}, {NULL, NULL} }; @@ -574,7 +882,7 @@ static PyObject *PySSL_getattr(PySSLObject *self, char *name) static PyTypeObject PySSL_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "socket.SSL", /*tp_name*/ + "ssl.SSLContext", /*tp_name*/ sizeof(PySSLObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ @@ -649,7 +957,7 @@ PyDoc_STRVAR(PySSL_RAND_egd_doc, "RAND_egd(path) -> bytes\n\ \n\ Queries the entropy gather daemon (EGD) on socket path. Returns number\n\ -of bytes read. Raises socket.sslerror if connection to EGD fails or\n\ +of bytes read. Raises ssl.sslerror if connection to EGD fails or\n\ if it does provide enough data to seed PRNG."); #endif @@ -657,17 +965,17 @@ if it does provide enough data to seed PRNG."); /* List of functions exported by this module. */ static PyMethodDef PySSL_methods[] = { - {"ssl", PySocket_ssl, - METH_VARARGS, ssl_doc}, + {"sslwrap", PySSL_sslwrap, + METH_VARARGS, ssl_doc}, #ifdef HAVE_OPENSSL_RAND - {"RAND_add", PySSL_RAND_add, METH_VARARGS, + {"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 */ + {NULL, NULL} /* Sentinel */ }; @@ -689,19 +997,20 @@ init_ssl(void) /* Load _socket module and its C API */ if (PySocketModule_ImportModuleAndAPI()) - return; + return; /* Init OpenSSL */ SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); /* Add symbols to module dict */ - PySSLErrorObject = PyErr_NewException("socket.sslerror", - PySocketModule.error, - NULL); + PySSLErrorObject = PyErr_NewException("ssl.sslerror", + PySocketModule.error, + NULL); if (PySSLErrorObject == NULL) return; - PyDict_SetItemString(d, "sslerror", PySSLErrorObject); + if (PyDict_SetItemString(d, "sslerror", PySSLErrorObject) != 0) + return; if (PyDict_SetItemString(d, "SSLType", (PyObject *)&PySSL_Type) != 0) return; @@ -724,5 +1033,21 @@ init_ssl(void) 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 */ + PyModule_AddIntConstant(m, "PROTOCOL_SSLv2", + PY_SSL_VERSION_SSL2); + 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); } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 65c0bf0..e79847f 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1596,6 +1596,16 @@ array_subscr(arrayobject* self, PyObject* item) if (slicelength <= 0) { return newarrayobject(&Arraytype, 0, self->ob_descr); } + else if (step == 1) { + PyObject *result = newarrayobject(&Arraytype, + slicelength, self->ob_descr); + if (result == NULL) + return NULL; + memcpy(((arrayobject *)result)->ob_item, + self->ob_item + start * itemsize, + slicelength * itemsize); + return result; + } else { result = newarrayobject(&Arraytype, slicelength, self->ob_descr); if (!result) return NULL; @@ -1614,7 +1624,7 @@ array_subscr(arrayobject* self, PyObject* item) } else { PyErr_SetString(PyExc_TypeError, - "list indices must be integers"); + "array indices must be integers"); return NULL; } } @@ -1622,112 +1632,146 @@ array_subscr(arrayobject* self, PyObject* item) static int array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) { + Py_ssize_t start, stop, step, slicelength, needed; + arrayobject* other; + int itemsize; + if (PyIndex_Check(item)) { Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i==-1 && PyErr_Occurred()) + + if (i == -1 && PyErr_Occurred()) return -1; if (i < 0) i += Py_Size(self); - return array_ass_item(self, i, value); - } - else if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - int itemsize = self->ob_descr->itemsize; - - if (PySlice_GetIndicesEx((PySliceObject*)item, Py_Size(self), - &start, &stop, &step, &slicelength) < 0) { + if (i < 0 || i >= Py_Size(self)) { + PyErr_SetString(PyExc_IndexError, + "array assignment index out of range"); return -1; } - - /* treat A[slice(a,b)] = v _exactly_ like A[a:b] = v */ - if (step == 1 && ((PySliceObject*)item)->step == Py_None) - return array_ass_slice(self, start, stop, value); - if (value == NULL) { - /* delete slice */ - Py_ssize_t cur, i, extra; - - if (slicelength <= 0) - return 0; - - if (step < 0) { - stop = start + 1; - start = stop + step*(slicelength - 1) - 1; - step = -step; - } - - for (cur = start, i = 0; i < slicelength - 1; - cur += step, i++) { - memmove(self->ob_item + (cur - i)*itemsize, - self->ob_item + (cur + 1)*itemsize, - (step - 1) * itemsize); - } - extra = Py_Size(self) - (cur + 1); - if (extra > 0) { - memmove(self->ob_item + (cur - i)*itemsize, - self->ob_item + (cur + 1)*itemsize, - extra*itemsize); - } - - Py_Size(self) -= slicelength; - self->ob_item = (char *)PyMem_REALLOC(self->ob_item, - itemsize*Py_Size(self)); - self->allocated = Py_Size(self); - - return 0; + /* Fall through to slice assignment */ + start = i; + stop = i + 1; + step = 1; + slicelength = 1; } - else { - /* assign slice */ - Py_ssize_t cur, i; - arrayobject* av; - - if (!array_Check(value)) { - PyErr_Format(PyExc_TypeError, - "must assign array (not \"%.200s\") to slice", - Py_Type(value)->tp_name); - return -1; - } - - av = (arrayobject*)value; - - if (Py_Size(av) != slicelength) { - PyErr_Format(PyExc_ValueError, - "attempt to assign array of size %ld to extended slice of size %ld", - /*XXX*/(long)Py_Size(av), /*XXX*/(long)slicelength); + else + return (*self->ob_descr->setitem)(self, i, value); + } + else if (PySlice_Check(item)) { + if (PySlice_GetIndicesEx((PySliceObject *)item, + Py_Size(self), &start, &stop, + &step, &slicelength) < 0) { + return -1; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "array indices must be integer"); + return -1; + } + if (value == NULL) { + other = NULL; + needed = 0; + } + else if (array_Check(value)) { + other = (arrayobject *)value; + needed = Py_Size(other); + if (self == other) { + /* Special case "self[i:j] = self" -- copy self first */ + int ret; + value = array_slice(other, 0, needed); + if (value == NULL) return -1; - } - - if (!slicelength) - return 0; - - /* protect against a[::-1] = a */ - if (self == av) { - value = array_slice(av, 0, Py_Size(av)); - av = (arrayobject*)value; - if (!av) - return -1; - } - else { - Py_INCREF(value); - } - - for (cur = start, i = 0; i < slicelength; - cur += step, i++) { - memcpy(self->ob_item + cur*itemsize, - av->ob_item + i*itemsize, - itemsize); - } - + ret = array_ass_subscr(self, item, value); Py_DECREF(value); - - return 0; + return ret; + } + if (other->ob_descr != self->ob_descr) { + PyErr_BadArgument(); + return -1; } - } + } else { - PyErr_SetString(PyExc_TypeError, - "list indices must be integers"); + PyErr_Format(PyExc_TypeError, + "can only assign array (not \"%.200s\") to array slice", + Py_Type(value)->tp_name); return -1; } + itemsize = self->ob_descr->itemsize; + /* for 'a[2:1] = ...', the insertion point is 'start', not 'stop' */ + if ((step > 0 && stop < start) || + (step < 0 && stop > start)) + stop = start; + if (step == 1) { + if (slicelength > needed) { + memmove(self->ob_item + (start + needed) * itemsize, + self->ob_item + stop * itemsize, + (Py_Size(self) - stop) * itemsize); + if (array_resize(self, Py_Size(self) + + needed - slicelength) < 0) + return -1; + } + else if (slicelength < needed) { + if (array_resize(self, Py_Size(self) + + needed - slicelength) < 0) + return -1; + memmove(self->ob_item + (start + needed) * itemsize, + self->ob_item + stop * itemsize, + (Py_Size(self) - start - needed) * itemsize); + } + if (needed > 0) + memcpy(self->ob_item + start * itemsize, + other->ob_item, needed * itemsize); + return 0; + } + else if (needed == 0) { + /* Delete slice */ + Py_ssize_t cur, i; + + if (step < 0) { + stop = start + 1; + start = stop + step * (slicelength - 1) - 1; + step = -step; + } + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + Py_ssize_t lim = step - 1; + + if (cur + step >= Py_Size(self)) + lim = Py_Size(self) - cur - 1; + memmove(self->ob_item + (cur - i) * itemsize, + self->ob_item + (cur + 1) * itemsize, + lim * itemsize); + } + cur = start + slicelength * step; + if (cur < Py_Size(self)) { + memmove(self->ob_item + (cur-slicelength) * itemsize, + self->ob_item + cur * itemsize, + (Py_Size(self) - cur) * itemsize); + } + if (array_resize(self, Py_Size(self) - slicelength) < 0) + return -1; + return 0; + } + else { + Py_ssize_t cur, i; + + if (needed != slicelength) { + PyErr_Format(PyExc_ValueError, + "attempt to assign array of size %zd " + "to extended slice of size %zd", + needed, slicelength); + return -1; + } + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + memcpy(self->ob_item + cur * itemsize, + other->ob_item + i * itemsize, + itemsize); + } + return 0; + } } static PyMappingMethods array_as_mapping = { diff --git a/Modules/main.c b/Modules/main.c index 22f5c0a..4fb4d2d 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -138,17 +138,16 @@ static int RunModule(char *module) fprintf(stderr, "Could not import runpy module\n"); return -1; } - runmodule = PyObject_GetAttrString(runpy, "run_module"); + runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); if (runmodule == NULL) { - fprintf(stderr, "Could not access runpy.run_module\n"); + fprintf(stderr, "Could not access runpy._run_module_as_main\n"); Py_DECREF(runpy); return -1; } - runargs = Py_BuildValue("sOsO", module, - Py_None, "__main__", Py_True); + runargs = Py_BuildValue("(s)", module); if (runargs == NULL) { fprintf(stderr, - "Could not create arguments for runpy.run_module\n"); + "Could not create arguments for runpy._run_module_as_main\n"); Py_DECREF(runpy); Py_DECREF(runmodule); return -1; diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 16a9dd2..f3727e3 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -660,6 +660,60 @@ mmap_slice(mmap_object *self, Py_ssize_t ilow, Py_ssize_t ihigh) } static PyObject * +mmap_subscript(mmap_object *self, PyObject *item) +{ + CHECK_VALID(NULL); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += self->size; + if (i < 0 || i > self->size) { + PyErr_SetString(PyExc_IndexError, + "mmap index out of range"); + return NULL; + } + return PyBytes_FromStringAndSize(self->data + i, 1); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)item, self->size, + &start, &stop, &step, &slicelen) < 0) { + return NULL; + } + + if (slicelen <= 0) + return PyBytes_FromStringAndSize("", 0); + else if (step == 1) + return PyBytes_FromStringAndSize(self->data + start, + slicelen); + else { + char *result_buf = (char *)PyMem_Malloc(slicelen); + Py_ssize_t cur, i; + PyObject *result; + + if (result_buf == NULL) + return PyErr_NoMemory(); + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + result_buf[i] = self->data[cur]; + } + result = PyBytes_FromStringAndSize(result_buf, + slicelen); + PyMem_Free(result_buf); + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "mmap indices must be integers"); + return NULL; + } +} + +static PyObject * mmap_concat(mmap_object *self, PyObject *bb) { CHECK_VALID(NULL); @@ -733,7 +787,7 @@ mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v) } if (! (PyBytes_Check(v) && PyBytes_Size(v)==1) ) { PyErr_SetString(PyExc_IndexError, - "mmap assignment must be single-character string"); + "mmap assignment must be length-1 bytes()"); return -1; } if (!is_writeable(self)) @@ -743,6 +797,96 @@ mmap_ass_item(mmap_object *self, Py_ssize_t i, PyObject *v) return 0; } +static int +mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value) +{ + CHECK_VALID(-1); + + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + const char *buf; + + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->size; + if (i < 0 || i > self->size) { + PyErr_SetString(PyExc_IndexError, + "mmap index out of range"); + return -1; + } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support item deletion"); + return -1; + } + if (!PyBytes_Check(value) || PyBytes_Size(value) != 1) { + PyErr_SetString(PyExc_IndexError, + "mmap assignment must be length-1 bytes()"); + return -1; + } + if (!is_writeable(self)) + return -1; + buf = PyBytes_AsString(value); + self->data[i] = buf[0]; + return 0; + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->size, &start, &stop, + &step, &slicelen) < 0) { + return -1; + } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support slice deletion"); + return -1; + } + if (!PyBytes_Check(value)) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment must be bytes"); + return -1; + } + if (PyBytes_Size(value) != slicelen) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment is wrong size"); + return -1; + } + if (!is_writeable(self)) + return -1; + + if (slicelen == 0) + return 0; + else if (step == 1) { + const char *buf = PyBytes_AsString(value); + + if (buf == NULL) + return -1; + memcpy(self->data + start, buf, slicelen); + return 0; + } + else { + Py_ssize_t cur, i; + const char *buf = PyBytes_AsString(value); + + if (buf == NULL) + return -1; + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + self->data[cur] = buf[i]; + } + return 0; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "mmap indices must be integer"); + return -1; + } +} + static PySequenceMethods mmap_as_sequence = { (lenfunc)mmap_length, /*sq_length*/ (binaryfunc)mmap_concat, /*sq_concat*/ @@ -753,6 +897,12 @@ static PySequenceMethods mmap_as_sequence = { (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/ }; +static PyMappingMethods mmap_as_mapping = { + (lenfunc)mmap_length, + (binaryfunc)mmap_subscript, + (objobjargproc)mmap_ass_subscript, +}; + static PyBufferProcs mmap_as_buffer = { (getbufferproc)mmap_buffer_getbuf, (releasebufferproc)mmap_buffer_releasebuf, @@ -772,7 +922,7 @@ static PyTypeObject mmap_object_type = { 0, /* tp_repr */ 0, /* tp_as_number */ &mmap_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + &mmap_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ |