diff options
author | Minteck <contact@minteck.org> | 2023-01-10 14:54:04 +0100 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2023-01-10 14:54:04 +0100 |
commit | 99c1d9af689e5325f3cf535c4007b3aeb8325229 (patch) | |
tree | e663b3c2ebdbd67c818ac0c5147f0ce1d2463cda /school/node_modules/node-forge/tests/forge_ssl | |
parent | 9871b03912fc28ad38b4037ebf26a78aa937baba (diff) | |
download | pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.gz pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.bz2 pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.zip |
Update - This is an automated commit
Diffstat (limited to 'school/node_modules/node-forge/tests/forge_ssl')
5 files changed, 2536 insertions, 0 deletions
diff --git a/school/node_modules/node-forge/tests/forge_ssl/forge/__init__.py b/school/node_modules/node-forge/tests/forge_ssl/forge/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/school/node_modules/node-forge/tests/forge_ssl/forge/__init__.py diff --git a/school/node_modules/node-forge/tests/forge_ssl/forge/_ssl.c b/school/node_modules/node-forge/tests/forge_ssl/forge/_ssl.c new file mode 100644 index 0000000..bdef8c9 --- /dev/null +++ b/school/node_modules/node-forge/tests/forge_ssl/forge/_ssl.c @@ -0,0 +1,1770 @@ +/* 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 { + PY_SSL_VERSION_SSL2, + PY_SSL_VERSION_SSL3, + PY_SSL_VERSION_SSL23, + PY_SSL_VERSION_TLS1 +}; + +enum py_ssl_sess_cache_mode { + PY_SSL_SESS_CACHE_OFF, + PY_SSL_SESS_CACHE_CLIENT, + PY_SSL_SESS_CACHE_SERVER, + PY_SSL_SESS_CACHE_BOTH +}; + +/* 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 */ + int inherited; + 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(PySSLObject *ssl_object, 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, + enum py_ssl_sess_cache_mode cache_mode, + char *sess_id_ctx, + char *cacerts_file) +{ + PySSLObject *self; + char *errstr = NULL; + int ret; + int verification_mode; + int sess_cache_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->inherited = 0; + 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) && (ssl_object == NULL) && + ((key_file == NULL) || (cert_file == NULL))) { + errstr = ERRSTR("Both the key & certificate files " + "must be specified for server-side operation"); + goto fail; + } + + if (ssl_object != NULL) { + self->inherited = 1; + self->ctx = ssl_object->ctx; + } else { + self->inherited = 0; + + 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 */ + PySSL_END_ALLOW_THREADS + } + + if (self->ctx == NULL) { + errstr = ERRSTR("Invalid SSL protocol variant specified."); + goto fail; + } + + if (self->inherited == 0 && 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 (self->inherited == 0 && 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; + } + } + } + + if (self->inherited == 0) { + /* ssl compatibility */ + SSL_CTX_set_options(self->ctx, SSL_OP_ALL); + + /* session cache mode */ + PySSL_BEGIN_ALLOW_THREADS + sess_cache_mode = SSL_SESS_CACHE_SERVER; + if (cache_mode == PY_SSL_SESS_CACHE_OFF) + sess_cache_mode = SSL_SESS_CACHE_OFF; + else if (cache_mode == PY_SSL_SESS_CACHE_CLIENT) + sess_cache_mode = SSL_SESS_CACHE_CLIENT; + else if (cache_mode == PY_SSL_SESS_CACHE_SERVER) + sess_cache_mode = SSL_SESS_CACHE_SERVER; + else if (cache_mode == PY_SSL_SESS_CACHE_BOTH) + sess_cache_mode = SSL_SESS_CACHE_BOTH; + SSL_CTX_set_session_cache_mode(self->ctx, sess_cache_mode); + + /* session id context */ + if (sess_id_ctx != NULL) + SSL_CTX_set_session_id_context(self->ctx, + (const unsigned char*)sess_id_ctx, + strlen(sess_id_ctx)); + PySSL_END_ALLOW_THREADS + + 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 */ + } + + self->ssl = SSL_new(self->ctx); /* New ssl struct */ + 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; + int sess_cache_mode = PY_SSL_SESS_CACHE_SERVER; + char *sess_id_ctx = NULL; + char *key_file = NULL; + char *cert_file = NULL; + char *cacerts_file = NULL; + + if (!PyArg_ParseTuple(args, "O!i|zziiizz:sslwrap", + PySocketModule.Sock_Type, + &Sock, + &server_side, + &key_file, &cert_file, + &verification_mode, &protocol, + &sess_cache_mode, &sess_id_ctx, + &cacerts_file)) + return NULL; + + /* + fprintf(stderr, + "server_side is %d, keyfile %p, certfile %p, verify_mode %d, " + "protocol %d, sess_cache_mode %d, sess_id_ctx %p, certs %p\n", + server_side, key_file, cert_file, verification_mode, + protocol, sess_cache_mode, sess_id_ctx, cacerts_file); + */ + + return (PyObject *) newPySSLObject(NULL, Sock, key_file, cert_file, + server_side, verification_mode, + protocol, sess_cache_mode, sess_id_ctx, + cacerts_file); +} + +PyDoc_STRVAR(sslwrap_doc, +"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n" +" sess_cache_mode, sess_id_ctx, cacertsfile]) -> sslobject"); + +/* SSL object methods */ + +static PyObject * +PySSL_SSLwrap_accepted(PySSLObject *self, PyObject *args) +{ + PySocketSockObject *Sock; + + if (!PyArg_ParseTuple(args, "O!:sslwrap", + PySocketModule.Sock_Type, + &Sock)) + return NULL; + + return (PyObject *) newPySSLObject(self, Sock, NULL, NULL, + PY_SSL_SERVER, 0, 0, 0, NULL, NULL); +} + +PyDoc_STRVAR(PySSL_SSLwrap_accepted_doc, +"wrap_accepted(socket) -> sslobject"); + +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. */ + sockstate = 0; + 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; + X509V3_EXT_METHOD *method; + BIO *biobuf = NULL; + char buf[2048]; + char *vptr; + int len; + const unsigned char *p; + + 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); + + 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; + SSL_CIPHER *current; + char *cipher_name; + char *cipher_protocol; + + if (self->ssl == NULL) + return Py_None; + current = SSL_get_current_cipher(self->ssl); + if (current == NULL) + return Py_None; + + retval = PyTuple_New(3); + if (retval == NULL) + return NULL; + + cipher_name = (char *) SSL_CIPHER_get_name(current); + if (cipher_name == NULL) { + 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) { + 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 && self->inherited == 0) + 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) +{ + char *data; + int len; + int count; + int sockstate; + int err; + int nonblocking; + + if (!PyArg_ParseTuple(args, "s#:write", &data, &count)) + 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"); + return NULL; + } else if (sockstate == 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()."); + return NULL; + } + do { + err = 0; + PySSL_BEGIN_ALLOW_THREADS + len = SSL_write(self->ssl, data, count); + err = SSL_get_error(self->ssl, len); + 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, + "The write operation timed out"); + return NULL; + } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { + PyErr_SetString(PySSLErrorObject, + "Underlying socket has been closed."); + return NULL; + } else if (sockstate == SOCKET_IS_NONBLOCKING) { + break; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + if (len > 0) + return PyInt_FromLong(len); + else + return PySSL_SetError(self, len, __FILE__, __LINE__); +} + +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 { + err = 0; + 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[] = { + {"wrap_accepted", (PyCFunction)PySSL_SSLwrap_accepted, METH_VARARGS, + PySSL_SSLwrap_accepted_doc}, + {"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, sslwrap_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_forge_ssl(void) +{ + PyObject *m, *d; + + Py_TYPE(&PySSL_Type) = &PyType_Type; + + m = Py_InitModule3("_forge_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 */ + 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); + + /* session cache modes */ + PyModule_AddIntConstant(m, "SESS_CACHE_OFF", + PY_SSL_SESS_CACHE_OFF); + PyModule_AddIntConstant(m, "SESS_CACHE_CLIENT", + PY_SSL_SESS_CACHE_CLIENT); + PyModule_AddIntConstant(m, "SESS_CACHE_SERVER", + PY_SSL_SESS_CACHE_SERVER); + PyModule_AddIntConstant(m, "SESS_CACHE_BOTH", + PY_SSL_SESS_CACHE_BOTH); +} diff --git a/school/node_modules/node-forge/tests/forge_ssl/forge/socketmodule.h b/school/node_modules/node-forge/tests/forge_ssl/forge/socketmodule.h new file mode 100644 index 0000000..a4415b5 --- /dev/null +++ b/school/node_modules/node-forge/tests/forge_ssl/forge/socketmodule.h @@ -0,0 +1,268 @@ +/* Socket module header file */ + +/* Includes needed for the sockaddr_* symbols below */ +#ifndef MS_WINDOWS +#ifdef __VMS +# include <socket.h> +# else +# include <sys/socket.h> +# endif +# include <netinet/in.h> +# if !(defined(__BEOS__) || defined(__CYGWIN__) || (defined(PYOS_OS2) && defined(PYCC_VACPP))) +# include <netinet/tcp.h> +# endif + +#else /* MS_WINDOWS */ +# include <winsock2.h> +# include <ws2tcpip.h> +/* VC6 is shipped with old platform headers, and does not have MSTcpIP.h + * Separate SDKs have all the functions we want, but older ones don't have + * any version information. + * I use SIO_GET_MULTICAST_FILTER to detect a decent SDK. + */ +# ifdef SIO_GET_MULTICAST_FILTER +# include <MSTcpIP.h> /* for SIO_RCVALL */ +# define HAVE_ADDRINFO +# define HAVE_SOCKADDR_STORAGE +# define HAVE_GETADDRINFO +# define HAVE_GETNAMEINFO +# define ENABLE_IPV6 +# else +typedef int socklen_t; +# endif /* IPPROTO_IPV6 */ +#endif /* MS_WINDOWS */ + +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#else +# undef AF_UNIX +#endif + +#ifdef HAVE_LINUX_NETLINK_H +# ifdef HAVE_ASM_TYPES_H +# include <asm/types.h> +# endif +# include <linux/netlink.h> +#else +# undef AF_NETLINK +#endif + +#ifdef HAVE_BLUETOOTH_BLUETOOTH_H +/* +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sco.h> +#include <bluetooth/hci.h> +*/ +#endif + +#ifdef HAVE_BLUETOOTH_H +#include <bluetooth.h> +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +# include <sys/ioctl.h> +# include <net/if.h> +# include <netpacket/packet.h> +#endif + +#ifdef HAVE_LINUX_TIPC_H +# include <linux/tipc.h> +#endif + +#ifndef Py__SOCKET_H +#define Py__SOCKET_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Python module and C API name */ +#define PySocket_MODULE_NAME "_socket" +#define PySocket_CAPI_NAME "CAPI" + +/* Abstract the socket file descriptor type */ +#ifdef MS_WINDOWS +typedef SOCKET SOCKET_T; +# ifdef MS_WIN64 +# define SIZEOF_SOCKET_T 8 +# else +# define SIZEOF_SOCKET_T 4 +# endif +#else +typedef int SOCKET_T; +# define SIZEOF_SOCKET_T SIZEOF_INT +#endif + +/* Socket address */ +typedef union sock_addr { + struct sockaddr_in in; +#ifdef AF_UNIX + struct sockaddr_un un; +#endif +#ifdef AF_NETLINK + struct sockaddr_nl nl; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 in6; + struct sockaddr_storage storage; +#endif +#ifdef HAVE_BLUETOOTH_BLUETOOTH_H +/* + struct sockaddr_l2 bt_l2; + struct sockaddr_rc bt_rc; + struct sockaddr_sco bt_sco; + struct sockaddr_hci bt_hci; +*/ +#endif +#ifdef HAVE_NETPACKET_PACKET_H + struct sockaddr_ll ll; +#endif +} sock_addr_t; + +/* The object holding a socket. It holds some extra information, + like the address family, which is used to decode socket address + arguments properly. */ + +typedef struct { + PyObject_HEAD + SOCKET_T sock_fd; /* Socket file descriptor */ + int sock_family; /* Address family, e.g., AF_INET */ + int sock_type; /* Socket type, e.g., SOCK_STREAM */ + int sock_proto; /* Protocol type, usually 0 */ + PyObject *(*errorhandler)(void); /* Error handler; checks + errno, returns NULL and + sets a Python exception */ + double sock_timeout; /* Operation timeout in seconds; + 0.0 means non-blocking */ +} PySocketSockObject; + +/* --- C API ----------------------------------------------------*/ + +/* Short explanation of what this C API export mechanism does + and how it works: + + The _ssl module needs access to the type object defined in + the _socket module. Since cross-DLL linking introduces a lot of + problems on many platforms, the "trick" is to wrap the + C API of a module in a struct which then gets exported to + other modules via a PyCObject. + + The code in socketmodule.c defines this struct (which currently + only contains the type object reference, but could very + well also include other C APIs needed by other modules) + and exports it as PyCObject via the module dictionary + under the name "CAPI". + + Other modules can now include the socketmodule.h file + which defines the needed C APIs to import and set up + a static copy of this struct in the importing module. + + After initialization, the importing module can then + access the C APIs from the _socket module by simply + referring to the static struct, e.g. + + Load _socket module and its C API; this sets up the global + PySocketModule: + + if (PySocketModule_ImportModuleAndAPI()) + return; + + + Now use the C API as if it were defined in the using + module: + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + + PySocketModule.Sock_Type, + + (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + + Support could easily be extended to export more C APIs/symbols + this way. Currently, only the type object is exported, + other candidates would be socket constructors and socket + access functions. + +*/ + +/* C API for usage by other Python modules */ +typedef struct { + PyTypeObject *Sock_Type; + PyObject *error; +} PySocketModule_APIObject; + +/* XXX The net effect of the following appears to be to define a function + XXX named PySocketModule_APIObject in _ssl.c. It's unclear why it isn't + XXX defined there directly. + + >>> It's defined here because other modules might also want to use + >>> the C API. + +*/ +#ifndef PySocket_BUILDING_SOCKET + +/* --- C API ----------------------------------------------------*/ + +/* Interfacestructure to C API for other modules. + Call PySocketModule_ImportModuleAndAPI() to initialize this + structure. After that usage is simple: + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + &PySocketModule.Sock_Type, (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + ... +*/ + +static +PySocketModule_APIObject PySocketModule; + +/* You *must* call this before using any of the functions in + PySocketModule and check its outcome; otherwise all accesses will + result in a segfault. Returns 0 on success. */ + +#ifndef DPRINTF +# define DPRINTF if (0) printf +#endif + +static +int PySocketModule_ImportModuleAndAPI(void) +{ + PyObject *mod = 0, *v = 0; + char *apimodule = PySocket_MODULE_NAME; + char *apiname = PySocket_CAPI_NAME; + void *api; + + DPRINTF("Importing the %s C API...\n", apimodule); + mod = PyImport_ImportModuleNoBlock(apimodule); + if (mod == NULL) + goto onError; + DPRINTF(" %s package found\n", apimodule); + v = PyObject_GetAttrString(mod, apiname); + if (v == NULL) + goto onError; + Py_DECREF(mod); + DPRINTF(" API object %s found\n", apiname); + api = PyCObject_AsVoidPtr(v); + if (api == NULL) + goto onError; + Py_DECREF(v); + memcpy(&PySocketModule, api, sizeof(PySocketModule)); + DPRINTF(" API object loaded and initialized.\n"); + return 0; + + onError: + DPRINTF(" not found.\n"); + Py_XDECREF(mod); + Py_XDECREF(v); + return -1; +} + +#endif /* !PySocket_BUILDING_SOCKET */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py__SOCKET_H */ diff --git a/school/node_modules/node-forge/tests/forge_ssl/forge/ssl.py b/school/node_modules/node-forge/tests/forge_ssl/forge/ssl.py new file mode 100644 index 0000000..aa9fc14 --- /dev/null +++ b/school/node_modules/node-forge/tests/forge_ssl/forge/ssl.py @@ -0,0 +1,486 @@ +# Wrapper module for _ssl, providing some additional facilities +# implemented in Python. Written by Bill Janssen. + +"""\ +This module provides some more Pythonic support for SSL. + +Object types: + + SSLSocket -- subtype of socket.socket which does SSL over the socket + +Exceptions: + + SSLError -- exception raised for I/O errors + +Functions: + + cert_time_to_seconds -- convert time string used for certificate + notBefore and notAfter functions to integer + seconds past the Epoch (the time values + returned from time.time()) + + fetch_server_certificate (HOST, PORT) -- fetch the certificate provided + by the server running on HOST at port PORT. No + validation of the certificate is performed. + +Integer constants: + +SSL_ERROR_ZERO_RETURN +SSL_ERROR_WANT_READ +SSL_ERROR_WANT_WRITE +SSL_ERROR_WANT_X509_LOOKUP +SSL_ERROR_SYSCALL +SSL_ERROR_SSL +SSL_ERROR_WANT_CONNECT + +SSL_ERROR_EOF +SSL_ERROR_INVALID_ERROR_CODE + +The following group define certificate requirements that one side is +allowing/requiring from the other side: + +CERT_NONE - no certificates from the other side are required (or will + be looked at if provided) +CERT_OPTIONAL - certificates are not required, but if provided will be + validated, and if validation fails, the connection will + also fail +CERT_REQUIRED - certificates are required, and will be validated, and + if validation fails, the connection will also fail + +The following constants identify various SSL protocol variants: + +PROTOCOL_SSLv2 +PROTOCOL_SSLv3 +PROTOCOL_SSLv23 +PROTOCOL_TLSv1 + +The following constants identify various SSL session caching modes: + +SESS_CACHE_OFF +SESS_CACHE_CLIENT +SESS_CACHE_SERVER +SESS_CACHE_BOTH +""" + +import textwrap + +import _forge_ssl # if we can't import it, let the error propagate + +from _forge_ssl import SSLError +from _forge_ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED +from _forge_ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1 +from _forge_ssl import SESS_CACHE_OFF, SESS_CACHE_CLIENT, SESS_CACHE_SERVER, SESS_CACHE_BOTH +from _forge_ssl import RAND_status, RAND_egd, RAND_add +from _forge_ssl import \ + SSL_ERROR_ZERO_RETURN, \ + SSL_ERROR_WANT_READ, \ + SSL_ERROR_WANT_WRITE, \ + SSL_ERROR_WANT_X509_LOOKUP, \ + SSL_ERROR_SYSCALL, \ + SSL_ERROR_SSL, \ + SSL_ERROR_WANT_CONNECT, \ + SSL_ERROR_EOF, \ + SSL_ERROR_INVALID_ERROR_CODE + +from socket import socket, _fileobject, _delegate_methods +from socket import error as socket_error +from socket import getnameinfo as _getnameinfo +import base64 # for DER-to-PEM translation +import errno + +class SSLSocket(socket): + + """This class implements a subtype of socket.socket that wraps + the underlying OS socket in an SSL context when necessary, and + provides read and write methods over that channel.""" + + def __init__(self, parent_socket, sock, keyfile=None, certfile=None, + server_side=False, cert_reqs=CERT_NONE, + ssl_version=PROTOCOL_SSLv23, + sess_cache_mode=SESS_CACHE_SERVER, + sess_id_ctx=None, + ca_certs=None, + do_handshake_on_connect=True, + suppress_ragged_eofs=True): + socket.__init__(self, _sock=sock._sock) + # The initializer for socket overrides the methods send(), recv(), etc. + # in the instancce, which we don't need -- but we want to provide the + # methods defined in SSLSocket. + for attr in _delegate_methods: + try: + delattr(self, attr) + except AttributeError: + pass + + if certfile and not keyfile: + keyfile = certfile + + create = True + connected = False + if not server_side: + # see if it's connected + try: + socket.getpeername(self) + connected = True + except socket_error, e: + if e.errno != errno.ENOTCONN: + raise + # no, no connection yet + self._sslobj = None + create = False + if create: + # yes, create the SSL object + if parent_socket == None: + self._sslobj = _forge_ssl.sslwrap( + self._sock, + server_side, + keyfile, certfile, + cert_reqs, ssl_version, + sess_cache_mode, sess_id_ctx, + ca_certs) + else: + self._sslobj = parent_socket._sslobj.wrap_accepted(self._sock) + + if connected and do_handshake_on_connect: + self.do_handshake() + self.keyfile = keyfile + self.certfile = certfile + self.cert_reqs = cert_reqs + self.ssl_version = ssl_version + self.sess_cache_mode = sess_cache_mode + self.sess_id_ctx = sess_id_ctx + self.ca_certs = ca_certs + self.do_handshake_on_connect = do_handshake_on_connect + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + + def read(self, len=1024): + + """Read up to LEN bytes and return them. + Return zero-length string on EOF.""" + + try: + return self._sslobj.read(len) + except SSLError, x: + if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs: + return '' + else: + raise + + def write(self, data): + + """Write DATA to the underlying SSL channel. Returns + number of bytes of DATA actually transmitted.""" + + return self._sslobj.write(data) + + def getpeercert(self, binary_form=False): + + """Returns a formatted version of the data in the + certificate provided by the other end of the SSL channel. + Return None if no certificate was provided, {} if a + certificate was provided, but not validated.""" + + return self._sslobj.peer_certificate(binary_form) + + def cipher(self): + + if not self._sslobj: + return None + else: + return self._sslobj.cipher() + + def send(self, data, flags=0): + if self._sslobj: + if flags != 0: + raise ValueError( + "non-zero flags not allowed in calls to send() on %s" % + self.__class__) + while True: + try: + v = self._sslobj.write(data) + except SSLError, x: + if x.args[0] == SSL_ERROR_WANT_READ: + return 0 + elif x.args[0] == SSL_ERROR_WANT_WRITE: + return 0 + else: + raise + else: + return v + else: + return socket.send(self, data, flags) + + def sendto(self, data, addr, flags=0): + if self._sslobj: + raise ValueError("sendto not allowed on instances of %s" % + self.__class__) + else: + return socket.sendto(self, data, addr, flags) + + def sendall(self, data, flags=0): + if self._sslobj: + if flags != 0: + raise ValueError( + "non-zero flags not allowed in calls to sendall() on %s" % + self.__class__) + amount = len(data) + count = 0 + while (count < amount): + v = self.send(data[count:]) + count += v + return amount + else: + return socket.sendall(self, data, flags) + + def recv(self, buflen=1024, flags=0): + if self._sslobj: + if flags != 0: + raise ValueError( + "non-zero flags not allowed in calls to recv() on %s" % + self.__class__) + return self.read(buflen) + else: + return socket.recv(self, buflen, flags) + + def recv_into(self, buffer, nbytes=None, flags=0): + if buffer and (nbytes is None): + nbytes = len(buffer) + elif nbytes is None: + nbytes = 1024 + if self._sslobj: + if flags != 0: + raise ValueError( + "non-zero flags not allowed in calls to recv_into() on %s" % + self.__class__) + tmp_buffer = self.read(nbytes) + v = len(tmp_buffer) + buffer[:v] = tmp_buffer + return v + else: + return socket.recv_into(self, buffer, nbytes, flags) + + def recvfrom(self, addr, buflen=1024, flags=0): + if self._sslobj: + raise ValueError("recvfrom not allowed on instances of %s" % + self.__class__) + else: + return socket.recvfrom(self, addr, buflen, flags) + + def recvfrom_into(self, buffer, nbytes=None, flags=0): + if self._sslobj: + raise ValueError("recvfrom_into not allowed on instances of %s" % + self.__class__) + else: + return socket.recvfrom_into(self, buffer, nbytes, flags) + + def pending(self): + if self._sslobj: + return self._sslobj.pending() + else: + return 0 + + def unwrap(self): + if self._sslobj: + try: + # if connected then shutdown + self.getpeername() + s = self._sslobj.shutdown() + except: + s = self._sock + self._sslobj = None + return s + else: + raise ValueError("No SSL wrapper around " + str(self)) + + def shutdown(self, how): + self._sslobj = None + socket.shutdown(self, how) + + def close(self): + if self._makefile_refs < 1: + if self._sslobj: + self.unwrap() + socket.close(self) + else: + self._makefile_refs -= 1 + + def do_handshake(self): + + """Perform a TLS/SSL handshake.""" + + self._sslobj.do_handshake() + + def connect(self, addr): + + """Connects to remote ADDR, and then wraps the connection in + an SSL channel.""" + + # Here we assume that the socket is client-side, and not + # connected at the time of the call. We connect it, then wrap it. + if self._sslobj: + raise ValueError("attempt to connect already-connected SSLSocket!") + socket.connect(self, addr) + self._sslobj = _forge_ssl.sslwrap(self._sock, False, + self.keyfile, self.certfile, + self.cert_reqs, self.ssl_version, + self.sess_cache_mode, + self.sess_id_ctx, + self.ca_certs) + if self.do_handshake_on_connect: + self.do_handshake() + + def accept(self): + + """Accepts a new connection from a remote client, and returns + a tuple containing that new connection wrapped with a server-side + SSL channel, and the address of the remote client.""" + + newsock, addr = socket.accept(self) + return (SSLSocket(self, + newsock, + keyfile=self.keyfile, + certfile=self.certfile, + server_side=True, + cert_reqs=self.cert_reqs, + ssl_version=self.ssl_version, + sess_cache_mode=self.sess_cache_mode, + sess_id_ctx=self.sess_id_ctx, + ca_certs=self.ca_certs, + do_handshake_on_connect=self.do_handshake_on_connect, + suppress_ragged_eofs=self.suppress_ragged_eofs), + addr) + + def makefile(self, mode='r', bufsize=-1): + + """Make and return a file-like object that + works with the SSL connection. Just use the code + from the socket module.""" + + self._makefile_refs += 1 + # close=True so as to decrement the reference count when done with + # the file-like object. + return _fileobject(self, mode, bufsize, close=True) + + + +def wrap_socket(sock, parent_socket=None, keyfile=None, certfile=None, + server_side=False, cert_reqs=CERT_NONE, + ssl_version=PROTOCOL_SSLv23, + sess_cache_mode=SESS_CACHE_SERVER, + sess_id_ctx=None, + ca_certs=None, + do_handshake_on_connect=True, + suppress_ragged_eofs=True): + + return SSLSocket(parent_socket, + sock, keyfile=keyfile, certfile=certfile, + server_side=server_side, cert_reqs=cert_reqs, + ssl_version=ssl_version, + sess_cache_mode=sess_cache_mode, + sess_id_ctx=sess_id_ctx, + ca_certs=ca_certs, + do_handshake_on_connect=do_handshake_on_connect, + suppress_ragged_eofs=suppress_ragged_eofs) + + +# some utility functions + +def cert_time_to_seconds(cert_time): + + """Takes a date-time string in standard ASN1_print form + ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return + a Python time value in seconds past the epoch.""" + + import time + return time.mktime(time.strptime(cert_time, "%b %d %H:%M:%S %Y GMT")) + +PEM_HEADER = "-----BEGIN CERTIFICATE-----" +PEM_FOOTER = "-----END CERTIFICATE-----" + +def DER_cert_to_PEM_cert(der_cert_bytes): + + """Takes a certificate in binary DER format and returns the + PEM version of it as a string.""" + + if hasattr(base64, 'standard_b64encode'): + # preferred because older API gets line-length wrong + f = base64.standard_b64encode(der_cert_bytes) + return (PEM_HEADER + '\n' + + textwrap.fill(f, 64) + '\n' + + PEM_FOOTER + '\n') + else: + return (PEM_HEADER + '\n' + + base64.encodestring(der_cert_bytes) + + PEM_FOOTER + '\n') + +def PEM_cert_to_DER_cert(pem_cert_string): + + """Takes a certificate in ASCII PEM format and returns the + DER-encoded version of it as a byte sequence""" + + if not pem_cert_string.startswith(PEM_HEADER): + raise ValueError("Invalid PEM encoding; must start with %s" + % PEM_HEADER) + if not pem_cert_string.strip().endswith(PEM_FOOTER): + raise ValueError("Invalid PEM encoding; must end with %s" + % PEM_FOOTER) + d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)] + return base64.decodestring(d) + +def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None): + + """Retrieve the certificate from the server at the specified address, + and return it as a PEM-encoded string. + If 'ca_certs' is specified, validate the server cert against it. + If 'ssl_version' is specified, use it in the connection attempt.""" + + host, port = addr + if (ca_certs is not None): + cert_reqs = CERT_REQUIRED + else: + cert_reqs = CERT_NONE + s = wrap_socket(socket(), ssl_version=ssl_version, + cert_reqs=cert_reqs, ca_certs=ca_certs) + s.connect(addr) + dercert = s.getpeercert(True) + s.close() + return DER_cert_to_PEM_cert(dercert) + +def get_protocol_name(protocol_code): + if protocol_code == PROTOCOL_TLSv1: + return "TLSv1" + elif protocol_code == PROTOCOL_SSLv23: + return "SSLv23" + elif protocol_code == PROTOCOL_SSLv2: + return "SSLv2" + elif protocol_code == PROTOCOL_SSLv3: + return "SSLv3" + else: + return "<unknown>" + + +# a replacement for the old socket.ssl function + +def sslwrap_simple(sock, keyfile=None, certfile=None): + + """A replacement for the old socket.ssl function. Designed + for compability with Python 2.5 and earlier. Will disappear in + Python 3.0.""" + + if hasattr(sock, "_sock"): + sock = sock._sock + + ssl_sock = _forge_ssl.sslwrap(sock, 0, keyfile, certfile, + CERT_NONE, PROTOCOL_SSLv23, + SESS_CACHE_SERVER, None, None) + try: + sock.getpeername() + except: + # no, no connection yet + pass + else: + # yes, do the handshake + ssl_sock.do_handshake() + + return ssl_sock diff --git a/school/node_modules/node-forge/tests/forge_ssl/setup.py b/school/node_modules/node-forge/tests/forge_ssl/setup.py new file mode 100644 index 0000000..350ae37 --- /dev/null +++ b/school/node_modules/node-forge/tests/forge_ssl/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from distutils.core import setup, Extension + +ssl = Extension('_forge_ssl', + sources = ['forge/_ssl.c']) + +setup (name = 'Forge SSL', + version = '1.0', + description = 'Python SSL with session cache support.', + ext_modules = [ssl], + py_modules = ['forge.ssl']) |