Add support for specifying subject and issuer information when creating X509 extensions
diff --git a/ChangeLog b/ChangeLog
index 197c472..d9d45f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2009-07-17 Rick Dean <rick@fdd.com>
+ Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c: Add subject and issuer parameters to
+ X509Extension, allowing creation of extensions which require that
+ information. Fixes LP#322813.
+
+2009-07-16 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/util.py: Changed the base TestCase's tearDown to assert that
+ no errors were left in the OpenSSL error queue by the test.
+ * src/crypto/crypto.c: Add a private helper in support of the
+ TestCase.tearDown change.
+ * src/crypto/x509name.c: Changed X509Name's getattr implementation
+ to clean up the error queue. Fixes LP#314814.
+ * test/util.c: Changed flush_error_queue to avoid a reference
+ counting bug caused by macro expansion.
+
+2009-07-16 Rick Dean <rick@fdd.com>
+
+ * src/rand.c: Added OpenSSL.rand.bytes to get random bytes directly.
+ * src/util.c: Added generic exceptions_from_error_queue to replace
+ the various other implementations of this function. Also updated
+ the rest of the codebase to use this version instead.
+
2009-07-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
* test/util.py, test/test_ssl.py, test/test_crypto.py: Fold the
diff --git a/doc/README b/doc/README
new file mode 100644
index 0000000..0ceff15
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,18 @@
+
+Only the .tex format documentation is original,
+because the others are derived from it, so don't
+edit them directly. To build the other formats
+use a command of ...
+
+ make all
+
+
+To build the documentation you will need to have
+latex2html and lynx installed. On Fedora both can
+be obtained with ...
+
+ sudo yum install latex2html lynx
+
+Or on Debian or Ubuntu ...
+
+ sudo apt-get install latex2html lynx
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index 0e1e6c2..bd83d6d 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -596,6 +596,18 @@
\var{string}, measured in bytes. For more information, see e.g. \rfc{1750}.
\end{funcdesc}
+\begin{funcdesc}{bytes}{num_bytes}
+Get some random bytes from the PRNG as a string.
+
+This is a wrapper for the C function \function{RAND_bytes}.
+\end{funcdesc}
+
+\begin{funcdesc}{cleanup}{}
+Erase the memory used by the PRNG.
+
+This is a wrapper for the C function \function{RAND_cleanup}.
+\end{funcdesc}
+
\begin{funcdesc}{egd}{path\optional{, bytes}}
Query the Entropy Gathering Daemon\footnote{See
\url{http://www.lothar.com/tech/crypto/}} on socket \var{path} for \var{bytes}
@@ -627,6 +639,16 @@
file can then be used with \function{load_file} to seed the PRNG again.
\end{funcdesc}
+\begin{excdesc}{Error}
+If the current RAND method supports any errors, this is raised when needed.
+The default method does not raise this when the entropy pool is depleted.
+
+Whenever this exception is raised directly, it has a list of error messages
+from the OpenSSL error queue, where each item is a tuple \code{(\var{lib},
+\var{function}, \var{reason})}. Here \var{lib}, \var{function} and \var{reason}
+are all strings, describing where and what the problem is. See \manpage{err}{3}
+for more information.
+\end{excdesc}
% % % SSL module
@@ -728,10 +750,19 @@
The operation did not complete; the same I/O method should be called again
later, with the same arguments. Any I/O method can lead to this since new
handshakes can occur at any time.
+
+The wanted read is for \emph{dirty} data sent over the network, not the
+\emph{clean} data inside the tunnel. For a socket based SSL connection,
+\emph{read} means data coming at us over the network. Until that read
+succeeds, the attempted \method{OpenSSL.SSL.Connection.recv},
+\method{OpenSSL.SSL.Connection.send}, or
+\method{OpenSSL.SSL.Connection.do_handshake} is prevented or incomplete. You
+probably want to \method{select()} on the socket before trying again.
\end{excdesc}
\begin{excdesc}{WantWriteError}
-See \exception{WantReadError}.
+See \exception{WantReadError}. The socket send buffer may be too full to
+write more data.
\end{excdesc}
\begin{excdesc}{WantX509LookupError}
diff --git a/setup.py b/setup.py
index 645e459..06d477b 100755
--- a/setup.py
+++ b/setup.py
@@ -88,6 +88,7 @@
'OpenSSL.version', 'OpenSSL.test.__init__',
'OpenSSL.test.util',
'OpenSSL.test.test_crypto',
+ 'OpenSSL.test.test_rand',
'OpenSSL.test.test_ssl'],
data_files = data_files,
description = 'Python wrapper module around the OpenSSL library',
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index 2240df0..21501c1 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -2,7 +2,7 @@
* crypto.c
*
* Copyright (C) AB Strakt 2001, All rights reserved
- * Copyright (C) Jean-Paul Calderone 2008, All rights reserved
+ * Copyright (C) Jean-Paul Calderone 2008-2009, All rights reserved
*
* Main file of crypto sub module.
* See the file RATIONALE for a short explanation of why this module was written.
@@ -112,7 +112,7 @@
if (pkey == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -213,7 +213,7 @@
if (ret == 0)
{
BIO_free(bio);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -264,7 +264,7 @@
if (cert == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -316,7 +316,7 @@
if (ret == 0)
{
BIO_free(bio);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -367,7 +367,7 @@
if (req == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -419,7 +419,7 @@
if (ret == 0)
{
BIO_free(bio);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -475,7 +475,7 @@
*/
if (pkcs7 == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -506,7 +506,7 @@
if ((p12 = d2i_PKCS12_bio(bio, NULL)) == NULL)
{
BIO_free(bio);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
BIO_free(bio);
@@ -535,6 +535,17 @@
return PyString_FromString(str);
}
+static char crypto_exception_from_error_queue_doc[] = "\n\
+Raise an exception from the current OpenSSL error queue.\n\
+";
+
+static PyObject *
+crypto_exception_from_error_queue(PyObject *spam, PyObject *eggs) {
+ exception_from_error_queue(crypto_Error);
+ return NULL;
+}
+
+
/* Methods in the OpenSSL.crypto module (i.e. none) */
static PyMethodDef crypto_methods[] = {
/* Module functions */
@@ -547,6 +558,7 @@
{ "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
{ "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
{ "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },
+ { "_exception_from_error_queue", (PyCFunction)crypto_exception_from_error_queue, METH_NOARGS, crypto_exception_from_error_queue_doc },
{ NULL, NULL }
};
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 7b8bade..b5e6b65 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -27,15 +27,6 @@
extern PyObject *crypto_Error;
-#ifdef exception_from_error_queue
-# undef exception_from_error_queue
-#endif
-#define exception_from_error_queue() do { \
- PyObject *errlist = error_queue_to_list(); \
- PyErr_SetObject(crypto_Error, errlist); \
- Py_DECREF(errlist); \
-} while (0)
-
#define crypto_X509_New_NUM 0
#define crypto_X509_New_RETURN crypto_X509Obj *
#define crypto_X509_New_PROTO (X509 *, int)
diff --git a/src/crypto/netscape_spki.c b/src/crypto/netscape_spki.c
index d4255f0..4fa9d8d 100644
--- a/src/crypto/netscape_spki.c
+++ b/src/crypto/netscape_spki.c
@@ -57,7 +57,7 @@
spki = NETSCAPE_SPKI_new();
if (spki == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
return (PyObject *)crypto_NetscapeSPKI_New(spki, 1);
@@ -107,7 +107,7 @@
if (!NETSCAPE_SPKI_sign(self->netscape_spki, pkey->pkey, digest))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -133,7 +133,7 @@
if ((answer = NETSCAPE_SPKI_verify(self->netscape_spki, pkey->pkey)) < 0)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -176,7 +176,7 @@
if ((pkey = NETSCAPE_SPKI_get_pubkey(self->netscape_spki)) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -200,7 +200,7 @@
if (!NETSCAPE_SPKI_set_pubkey(self->netscape_spki, pkey->pkey))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
diff --git a/src/crypto/pkcs12.c b/src/crypto/pkcs12.c
index 71ebcdd..28ea2fe 100644
--- a/src/crypto/pkcs12.c
+++ b/src/crypto/pkcs12.c
@@ -110,7 +110,7 @@
/* parse the PKCS12 lump */
if (!(cacerts && PKCS12_parse(p12, passphrase, &pkey, &cert, &cacerts)))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
diff --git a/src/crypto/pkey.c b/src/crypto/pkey.c
index 6632abe..583a2a1 100644
--- a/src/crypto/pkey.c
+++ b/src/crypto/pkey.c
@@ -21,7 +21,7 @@
*/
#define FAIL() \
do { \
- exception_from_error_queue(); \
+ exception_from_error_queue(crypto_Error); \
return NULL; \
} while (0)
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index 119c048..e089d40 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -136,7 +136,7 @@
if (bignum == NULL) {
if (ASN1_INTEGER_set(X509_get_serialNumber(self->x509), small_serial)) {
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
goto err;
}
} else {
@@ -144,11 +144,11 @@
BN_free(bignum);
bignum = NULL;
if (asn1_i == NULL) {
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
goto err;
}
if (!X509_set_serialNumber(self->x509, asn1_i)) {
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
goto err;
}
ASN1_INTEGER_free(asn1_i);
@@ -221,7 +221,7 @@
if (!X509_set_issuer_name(self->x509, issuer->x509_name))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -273,7 +273,7 @@
if (!X509_set_subject_name(self->x509, subject->x509_name))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -299,7 +299,7 @@
if ((pkey = X509_get_pubkey(self->x509)) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -327,7 +327,7 @@
if (!X509_set_pubkey(self->x509, pkey->pkey))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -422,7 +422,7 @@
} else {
ASN1_TIME_to_generalizedtime(timestamp, >_timestamp);
if (gt_timestamp == NULL) {
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
} else {
py_timestamp = PyString_FromString((char *)gt_timestamp->data);
@@ -558,7 +558,7 @@
if (!X509_sign(self->x509, pkey->pkey, digest))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -629,7 +629,7 @@
if (!X509_digest(self->x509,digest,fp,&len))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
}
tmp = malloc(3*len+1);
memset(tmp, 0, 3*len+1);
@@ -679,7 +679,7 @@
if (!X509_add_ext(self->x509, ext->x509_extension, -1))
{
Py_DECREF(seq);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
}
diff --git a/src/crypto/x509ext.c b/src/crypto/x509ext.c
index bcfa978..90ef543 100644
--- a/src/crypto/x509ext.c
+++ b/src/crypto/x509ext.c
@@ -150,7 +150,7 @@
return self;
nconf_error:
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
critical_malloc_error:
Py_XDECREF(self);
@@ -241,7 +241,7 @@
if (!X509V3_EXT_print(bio, self->x509_extension, 0, 0))
{
BIO_free(bio);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
diff --git a/src/crypto/x509name.c b/src/crypto/x509name.c
index 5d49430..39fdcf8 100644
--- a/src/crypto/x509name.c
+++ b/src/crypto/x509name.c
@@ -2,7 +2,7 @@
* x509name.c
*
* Copyright (C) AB Strakt 2001, All rights reserved
- * Copyright (C) Jean-Paul Calderone 2008, All rights reserved
+ * Copyright (C) Jean-Paul Calderone 2008-2009, All rights reserved
*
* X.509 Name handling, mostly thin wrapping.
* See the file RATIONALE for a short explanation of why this module was written.
@@ -88,7 +88,7 @@
data = X509_NAME_ENTRY_get_data(entry);
if ((len = ASN1_STRING_to_UTF8((unsigned char **)utf8string, data)) < 0)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return -1;
}
@@ -129,7 +129,7 @@
(unsigned char *)utf8string,
-1, -1, 0))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return -1;
}
return 0;
@@ -153,8 +153,15 @@
int nid, len;
char *utf8string;
- if ((nid = OBJ_txt2nid(name)) == NID_undef)
- {
+ if ((nid = OBJ_txt2nid(name)) == NID_undef) {
+ /*
+ * This is a bit weird. OBJ_txt2nid indicated failure, but it seems
+ * a lower level function, a2d_ASN1_OBJECT, also feels the need to
+ * push something onto the error queue. If we don't clean that up
+ * now, someone else will bump into it later and be quite confused.
+ * See lp#314814.
+ */
+ flush_error_queue();
return Py_FindMethod(crypto_X509Name_methods, (PyObject *)self, name);
}
@@ -236,7 +243,7 @@
if (X509_NAME_oneline(self->x509_name, tmpbuf, 512) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
else
diff --git a/src/crypto/x509req.c b/src/crypto/x509req.c
index a1325e9..07bd44b 100644
--- a/src/crypto/x509req.c
+++ b/src/crypto/x509req.c
@@ -30,7 +30,7 @@
if ((name = X509_REQ_get_subject_name(self->x509_req)) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
if ((pyname = crypto_X509Name_New(name, 0)) != NULL) {
@@ -58,7 +58,7 @@
if ((pkey = X509_REQ_get_pubkey(self->x509_req)) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -86,7 +86,7 @@
if (!X509_REQ_set_pubkey(self->x509_req, pkey->pkey))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -131,7 +131,7 @@
if (!X509_REQ_sign(self->x509_req, pkey->pkey, digest))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -160,7 +160,7 @@
if ((answer = X509_REQ_verify(self->x509_req, key->pkey)) < 0)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -194,7 +194,7 @@
/* Make a STACK_OF(X509_EXTENSION) from sequence */
if ((exts = sk_X509_EXTENSION_new_null()) == NULL)
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
@@ -217,7 +217,7 @@
if (!X509_REQ_add_extensions(self->x509_req, exts))
{
sk_X509_EXTENSION_free(exts);
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
diff --git a/src/crypto/x509store.c b/src/crypto/x509store.c
index 9f46029..16af3b0 100644
--- a/src/crypto/x509store.c
+++ b/src/crypto/x509store.c
@@ -27,7 +27,7 @@
if (!X509_STORE_add_cert(self->x509_store, cert->x509))
{
- exception_from_error_queue();
+ exception_from_error_queue(crypto_Error);
return NULL;
}
diff --git a/src/rand/rand.c b/src/rand/rand.c
index f1c8440..e922e08 100644
--- a/src/rand/rand.c
+++ b/src/rand/rand.c
@@ -21,6 +21,9 @@
# endif
#endif
#include <openssl/rand.h>
+#include "../util.h"
+
+PyObject *rand_Error;
static char rand_doc[] = "\n\
PRNG management routines, thin wrappers.\n\
@@ -188,6 +191,42 @@
return PyInt_FromLong((long)RAND_write_file(filename));
}
+static char rand_bytes_doc[] = "\n\
+Get some randomm bytes as a string.\n\
+\n\
+@param num_bytes: The number of bytes to fetch\n\
+@return: A string of random bytes\n\
+";
+
+static PyObject *
+rand_bytes(PyObject *spam, PyObject *args, PyObject *keywds)
+{
+ int num_bytes;
+ static char *kwlist[] = {"num_bytes", NULL};
+ char *buf;
+ unsigned int rc;
+ PyObject *obj = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "i:bytes", kwlist, &num_bytes))
+ return NULL;
+ if(num_bytes < 0) {
+ PyErr_SetString(PyExc_ValueError, "num_bytes must not be negative");
+ return NULL;
+ }
+ buf = malloc(num_bytes);
+ if (buf == NULL) /* out of memory */
+ return NULL;
+ rc = RAND_bytes((unsigned char *) buf, num_bytes);
+ if(rc != 1) { /* if unsuccessful */
+ exception_from_error_queue(rand_Error);
+ goto done;
+ }
+ obj = PyString_FromStringAndSize(buf, (unsigned) num_bytes);
+ done:
+ free(buf);
+ return obj;
+}
+
/* Methods in the OpenSSL.rand module */
static PyMethodDef rand_methods[] = {
@@ -201,6 +240,7 @@
{ "cleanup", (PyCFunction)rand_cleanup, METH_VARARGS, rand_cleanup_doc },
{ "load_file", (PyCFunction)rand_load_file, METH_VARARGS, rand_load_file_doc },
{ "write_file",(PyCFunction)rand_write_file, METH_VARARGS, rand_write_file_doc },
+ { "bytes", (PyCFunction)rand_bytes, METH_VARARGS|METH_KEYWORDS, rand_bytes_doc },
{ NULL, NULL }
};
@@ -220,5 +260,13 @@
if ((module = Py_InitModule3("rand", rand_methods, rand_doc)) == NULL)
return;
+
+ rand_Error = PyErr_NewException("OpenSSL.rand.Error", NULL, NULL);
+ if (rand_Error == NULL)
+ goto error;
+ if (PyModule_AddObject(module, "Error", rand_Error) != 0)
+ goto error;
+ error:
+ ;
}
diff --git a/src/ssl/connection.c b/src/ssl/connection.c
index a5fdc51..e10989b 100755
--- a/src/ssl/connection.c
+++ b/src/ssl/connection.c
@@ -164,7 +164,7 @@
* the code which triggered the error also kindly pushed something onto
* the error stack.
*/
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
}
}
@@ -238,7 +238,7 @@
case SSL_ERROR_SSL:
;
default:
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
break;
}
}
@@ -788,7 +788,7 @@
if (ret < 0)
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else if (ret > 0)
diff --git a/src/ssl/context.c b/src/ssl/context.c
index ae669d0..df7411f 100644
--- a/src/ssl/context.c
+++ b/src/ssl/context.c
@@ -265,7 +265,7 @@
if (!SSL_CTX_load_verify_locations(self->ctx, cafile, capath))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -291,7 +291,7 @@
* -exarkun
*/
if (!SSL_CTX_set_default_verify_paths(self->ctx)) {
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
Py_INCREF(Py_None);
@@ -388,14 +388,14 @@
}
if (!(cert_original = X509_dup(cert->x509)))
{
- /* exception_from_error_queue(); */
+ /* exception_from_error_queue(ssl_Error); */
PyErr_SetString(PyExc_RuntimeError, "X509_dup failed");
return NULL;
}
if (!SSL_CTX_add_extra_chain_cert(self->ctx, cert_original))
{
X509_free(cert_original);
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -422,7 +422,7 @@
if (!SSL_CTX_use_certificate_chain_file(self->ctx, certfile))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -451,7 +451,7 @@
if (!SSL_CTX_use_certificate_file(self->ctx, certfile, filetype))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -478,7 +478,7 @@
if (!SSL_CTX_use_certificate(self->ctx, cert->x509))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -516,7 +516,7 @@
if (!ret)
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -565,7 +565,7 @@
if (!SSL_CTX_use_PrivateKey(self->ctx, pkey->pkey))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -588,7 +588,7 @@
if (!SSL_CTX_check_private_key(self->ctx))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -637,7 +637,7 @@
if (!SSL_CTX_set_session_id_context(self->ctx, buf, len))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
@@ -779,7 +779,7 @@
if (!SSL_CTX_set_cipher_list(self->ctx, cipher_list))
{
- exception_from_error_queue();
+ exception_from_error_queue(ssl_Error);
return NULL;
}
else
diff --git a/src/ssl/ssl.h b/src/ssl/ssl.h
index 9cf0186..ba7ba8b 100644
--- a/src/ssl/ssl.h
+++ b/src/ssl/ssl.h
@@ -27,15 +27,6 @@
*ssl_WantX509LookupError, /* ... */
*ssl_SysCallError; /* Uses (errno,errstr) */
-#ifdef exception_from_error_queue
-# undef exception_from_error_queue
-#endif
-#define exception_from_error_queue() do { \
- PyObject *errlist = error_queue_to_list(); \
- PyErr_SetObject(ssl_Error, errlist); \
- Py_DECREF(errlist); \
-} while (0)
-
#define ssl_Context_New_NUM 0
#define ssl_Context_New_RETURN ssl_ContextObj *
#define ssl_Context_New_PROTO (int method)
diff --git a/src/util.c b/src/util.c
index aa9d238..ae6ee5e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2,6 +2,7 @@
* util.c
*
* Copyright (C) AB Strakt 2001, All rights reserved
+ * Copyright (C) Jean-Paul Calderone 2009, All rights reserved
*
* Utility functions.
* See the file RATIONALE for a short explanation of why this module was written.
@@ -19,15 +20,13 @@
* Returns: A list of errors (new reference)
*/
PyObject *
-error_queue_to_list(void)
-{
+error_queue_to_list(void) {
PyObject *errlist, *tuple;
long err;
errlist = PyList_New(0);
- while ((err = ERR_get_error()) != 0)
- {
+ while ((err = ERR_get_error()) != 0) {
tuple = Py_BuildValue("(sss)", ERR_lib_error_string(err),
ERR_func_error_string(err),
ERR_reason_error_string(err));
@@ -38,6 +37,12 @@
return errlist;
}
+void exception_from_error_queue(PyObject *the_Error) {
+ PyObject *errlist = error_queue_to_list();
+ PyErr_SetObject(the_Error, errlist);
+ Py_DECREF(errlist);
+}
+
/*
* Flush OpenSSL's error queue and ignore the result
*
@@ -45,8 +50,12 @@
* Returns: None
*/
void
-flush_error_queue(void)
-{
- Py_DECREF(error_queue_to_list());
+flush_error_queue(void) {
+ /*
+ * Make sure to save the errors to a local. Py_DECREF might expand such
+ * that it evaluates its argument more than once, which would lead to
+ * very nasty things if we just invoked it with error_queue_to_list().
+ */
+ PyObject *list = error_queue_to_list();
+ Py_DECREF(list);
}
-
diff --git a/src/util.h b/src/util.h
index 1774956..d9dc7d2 100644
--- a/src/util.h
+++ b/src/util.h
@@ -23,6 +23,7 @@
#include "pymemcompat.h"
extern PyObject *error_queue_to_list(void);
+extern void exception_from_error_queue(PyObject *the_Error);
extern void flush_error_queue(void);
/*
diff --git a/test/test_rand.py b/test/test_rand.py
new file mode 100644
index 0000000..05bf8c8
--- /dev/null
+++ b/test/test_rand.py
@@ -0,0 +1,80 @@
+# Copyright (C) Frederick Dean 2009, All rights reserved
+
+"""
+Unit tests for L{OpenSSL.rand}.
+"""
+
+from unittest import main
+import os
+import stat
+
+from OpenSSL.test.util import TestCase
+from OpenSSL import rand
+
+
+class RandTests(TestCase):
+ def test_bytes(self):
+ """
+ Verify that we can obtain bytes from rand_bytes() and
+ that they are different each time. Test the parameter
+ of rand_bytes() for bad values.
+ """
+ b1 = rand.bytes(50)
+ self.assertEqual(len(b1), 50)
+ b2 = rand.bytes(num_bytes=50) # parameter by name
+ self.assertNotEqual(b1, b2) # Hip, Hip, Horay! FIPS complaince
+ b3 = rand.bytes(num_bytes=0)
+ self.assertEqual(len(b3), 0)
+ exc = self.assertRaises(ValueError, rand.bytes, -1)
+ self.assertEqual(str(exc), "num_bytes must not be negative")
+
+
+ def test_add(self):
+ """
+ L{OpenSSL.rand.add} adds entropy to the PRNG.
+ """
+ rand.add('hamburger', 3)
+
+
+ def test_seed(self):
+ """
+ L{OpenSSL.rand.seed} adds entropy to the PRNG.
+ """
+ rand.seed('milk shake')
+
+
+ def test_status(self):
+ """
+ L{OpenSSL.rand.status} returns C{True} if the PRNG has sufficient
+ entropy, C{False} otherwise.
+ """
+ # It's hard to know what it is actually going to return. Different
+ # OpenSSL random engines decide differently whether they have enough
+ # entropy or not.
+ self.assertTrue(rand.status() in (1, 2))
+
+
+ def test_files(self):
+ """
+ Test reading and writing of files via rand functions.
+ """
+ # Write random bytes to a file
+ tmpfile = self.mktemp()
+ # Make sure it exists (so cleanup definitely succeeds)
+ fObj = file(tmpfile, 'w')
+ fObj.close()
+ try:
+ rand.write_file(tmpfile)
+ # Verify length of written file
+ size = os.stat(tmpfile)[stat.ST_SIZE]
+ self.assertEquals(size, 1024)
+ # Read random bytes from file
+ rand.load_file(tmpfile)
+ rand.load_file(tmpfile, 4) # specify a length
+ finally:
+ # Cleanup
+ os.unlink(tmpfile)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/test_ssl.py b/test/test_ssl.py
index a51cfc4..a7c744e 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -31,6 +31,37 @@
OP_NO_TICKET = None
+def socket_pair():
+ """
+ Establish and return a pair of network sockets connected
+ to each other.
+ """
+ # Connect a pair of sockets
+ port = socket()
+ port.bind(('', 0))
+ port.listen(1)
+ client = socket()
+ client.setblocking(False)
+ client.connect_ex(port.getsockname())
+ client.setblocking(True)
+ server = port.accept()[0]
+
+ # Let's pass some unencrypted data to make sure our socket connection is
+ # fine. Just one byte, so we don't have to worry about buffers getting
+ # filled up or fragmentation.
+ server.send("x")
+ assert client.recv(1024) == "x"
+ client.send("y")
+ assert server.recv(1024) == "y"
+
+ # All our callers want non-blocking sockets, make it easy for them.
+ server.setblocking(False)
+ client.setblocking(False)
+
+ return (server, client)
+
+
+
class ContextTests(TestCase):
"""
Unit tests for L{OpenSSL.SSL.Context}.
@@ -88,13 +119,7 @@
L{Context.set_info_callback} accepts a callable which will be invoked
when certain information about an SSL connection is available.
"""
- port = socket()
- port.bind(('', 0))
- port.listen(1)
-
- client = socket()
- client.setblocking(False)
- client.connect_ex(port.getsockname())
+ (server, client) = socket_pair()
clientSSL = Connection(Context(TLSv1_METHOD), client)
clientSSL.set_connect_state()
@@ -109,9 +134,6 @@
context.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
- server, ignored = port.accept()
- server.setblocking(False)
-
serverSSL = Connection(context, server)
serverSSL.set_accept_state()
@@ -127,13 +149,7 @@
def _load_verify_locations_test(self, *args):
- port = socket()
- port.bind(('', 0))
- port.listen(1)
-
- client = socket()
- client.setblocking(False)
- client.connect_ex(port.getsockname())
+ (server, client) = socket_pair()
clientContext = Context(TLSv1_METHOD)
clientContext.load_verify_locations(*args)
@@ -146,9 +162,6 @@
clientSSL = Connection(clientContext, client)
clientSSL.set_connect_state()
- server, _ = port.accept()
- server.setblocking(False)
-
serverContext = Context(TLSv1_METHOD)
serverContext.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
@@ -412,7 +425,11 @@
"""
Tests for L{OpenSSL.SSL.Connection} using a memory BIO.
"""
- def _server(self):
+ def _server(self, sock):
+ """
+ Create a new server-side SSL L{Connection} object wrapped around
+ C{sock}.
+ """
# Create the server side Connection. This is mostly setup boilerplate
# - use TLSv1, use a particular certificate, etc.
server_ctx = Context(TLSv1_METHOD)
@@ -423,15 +440,20 @@
server_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
server_ctx.check_privatekey()
server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
- # Here the Connection is actually created. None is passed as the 2nd
- # parameter, indicating a memory BIO should be created.
- server_conn = Connection(server_ctx, None)
+ # Here the Connection is actually created. If None is passed as the 2nd
+ # parameter, it indicates a memory BIO should be created.
+ server_conn = Connection(server_ctx, sock)
server_conn.set_accept_state()
return server_conn
- def _client(self):
- # Now create the client side Connection. Similar boilerplate to the above.
+ def _client(self, sock):
+ """
+ Create a new client-side SSL L{Connection} object wrapped around
+ C{sock}.
+ """
+ # Now create the client side Connection. Similar boilerplate to the
+ # above.
client_ctx = Context(TLSv1_METHOD)
client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
client_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
@@ -440,8 +462,7 @@
client_ctx.use_certificate(load_certificate(FILETYPE_PEM, client_cert_pem))
client_ctx.check_privatekey()
client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
- # Again, None to create a new memory BIO.
- client_conn = Connection(client_ctx, None)
+ client_conn = Connection(client_ctx, sock)
client_conn.set_connect_state()
return client_conn
@@ -493,15 +514,15 @@
write.bio_write(dirty)
- def test_connect(self):
+ def test_memoryConnect(self):
"""
Two L{Connection}s which use memory BIOs can be manually connected by
reading from the output of each and writing those bytes to the input of
the other and in this way establish a connection and exchange
application-level bytes with each other.
"""
- server_conn = self._server()
- client_conn = self._client()
+ server_conn = self._server(None)
+ client_conn = self._client(None)
# There should be no key or nonces yet.
self.assertIdentical(server_conn.master_key(), None)
@@ -536,6 +557,47 @@
(server_conn, important_message[::-1]))
+ def test_socketConnect(self):
+ """
+ Just like L{test_memoryConnect} but with an actual socket.
+
+ This is primarily to rule out the memory BIO code as the source of
+ any problems encountered while passing data over a L{Connection} (if
+ this test fails, there must be a problem outside the memory BIO
+ code, as no memory BIO is involved here). Even though this isn't a
+ memory BIO test, it's convenient to have it here.
+ """
+ (server, client) = socket_pair()
+
+ # Let the encryption begin...
+ client_conn = self._client(client)
+ server_conn = self._server(server)
+
+ # Establish the connection
+ established = False
+ while not established:
+ established = True # assume the best
+ for ssl in client_conn, server_conn:
+ try:
+ # Generally a recv() or send() could also work instead
+ # of do_handshake(), and we would stop on the first
+ # non-exception.
+ ssl.do_handshake()
+ except WantReadError:
+ established = False
+
+ important_message = "Help me Obi Wan Kenobi, you're my only hope."
+ client_conn.send(important_message)
+ msg = server_conn.recv(1024)
+ self.assertEqual(msg, important_message)
+
+ # Again in the other direction, just for fun.
+ important_message = important_message[::-1]
+ server_conn.send(important_message)
+ msg = client_conn.recv(1024)
+ self.assertEqual(msg, important_message)
+
+
def test_socketOverridesMemory(self):
"""
Test that L{OpenSSL.SSL.bio_read} and L{OpenSSL.SSL.bio_write} don't
@@ -556,8 +618,8 @@
returned and that many bytes from the beginning of the input can be
read from the other end of the connection.
"""
- server = self._server()
- client = self._client()
+ server = self._server(None)
+ client = self._client(None)
self._loopback(client, server)
@@ -581,7 +643,7 @@
L{Connection.bio_shutdown} signals the end of the data stream from
which the L{Connection} reads.
"""
- server = self._server()
+ server = self._server(None)
server.bio_shutdown()
e = self.assertRaises(Error, server.recv, 1024)
# We don't want WantReadError or ZeroReturnError or anything - it's a
diff --git a/test/util.py b/test/util.py
index 72c9e44..e0e25f9 100644
--- a/test/util.py
+++ b/test/util.py
@@ -13,6 +13,8 @@
from unittest import TestCase
import sys
+from OpenSSL.crypto import Error, _exception_from_error_queue
+
class TestCase(TestCase):
"""
@@ -31,7 +33,12 @@
shutil.rmtree(temp)
elif os.path.exists(temp):
os.unlink(temp)
-
+ try:
+ _exception_from_error_queue()
+ except Error, e:
+ if e.args != ([],):
+ self.fail("Left over errors in OpenSSL error queue: " + repr(e))
+
def failUnlessIdentical(self, first, second, msg=None):
"""