summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog39
-rw-r--r--MANIFEST.in3
-rw-r--r--doc/pyOpenSSL.tex39
-rwxr-xr-xsetup.py8
-rw-r--r--src/crypto/crypto.c2
-rw-r--r--src/crypto/x509.c8
-rw-r--r--src/crypto/x509ext.c152
-rw-r--r--src/crypto/x509name.c11
-rw-r--r--src/crypto/x509req.c52
-rw-r--r--src/rand/rand.c6
-rw-r--r--src/ssl/context.c60
-rw-r--r--src/ssl/ssl.c15
-rw-r--r--test/test_crypto.py76
-rw-r--r--test/test_ssl.py189
14 files changed, 537 insertions, 123 deletions
diff --git a/ChangeLog b/ChangeLog
index d1b046e..75a9745 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,42 @@
+2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c, test/test_crypto.py: Add the get_short_name
+ method to X509Extension based on patch from Alex Stapleton.
+
+2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c, test/test_crypto.py: Fix X509Extension so
+ that it is possible to instantiate extensions which use s2i or r2i
+ instead of v2i (an extremely obscure extension implementation
+ detail).
+
+2008-12-30 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * MANIFEST.in, src/crypto/crypto.c, src/crypto/x509.c,
+ src/crypto/x509name.c, src/rand/rand.c, src/ssl/context.c: Changes
+ which eliminate compiler warnings but should not change any
+ behavior.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/test_ssl.py, src/ssl/ssl.c: Expose DTLS-related constants,
+ OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, and OP_NO_TICKET.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/context.c: Add a capath parameter to
+ Context.load_verify_locations to allow Python code to specify
+ either or both arguments to the underlying
+ SSL_CTX_load_verify_locations API.
+ * src/ssl/context.c: Add Context.set_default_verify_paths, a wrapper
+ around SSL_CTX_set_default_verify_paths.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/test_crypto.py, src/crypto/x509req.c: Added get_version and
+ set_version_methods to X509ReqType based on patch from Wouter van
+ Bommel. Resolves LP#274418.
+
2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
* Release 0.8
diff --git a/MANIFEST.in b/MANIFEST.in
index 2506b7b..9f7aa73 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,7 +4,4 @@ include doc/pyOpenSSL.tex doc/Makefile
recursive-include doc/tools *
recursive-include examples *
recursive-include rpm *
-global-exclude .cvsignore
global-exclude *.pyc
-
-# @(#) $Id: MANIFEST.in,v 1.11 2003/06/15 09:51:19 martin Exp $
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index 4252cac..ebd08ff 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -370,6 +370,9 @@ Return the hash of the certificate subject.
\begin{methoddesc}[X509]{digest}{digest_name}
Return a digest of the certificate, using the \var{digest_name} method.
+\var{digest_name} must be a string describing a digest algorithm supported
+by OpenSSL (by EVP_get_digestbyname, specifically). For example,
+\constant{"md5"} or \constant{"sha1"}.
\end{methoddesc}
\begin{methoddesc}[X509]{add_extensions}{extensions}
@@ -454,6 +457,15 @@ algorithm identified by the string \var{digest}.
Verify a certificate request using the public key \var{pkey}.
\end{methoddesc}
+\begin{methoddesc}[X509Req]{set_version}{version}
+Set the version (RFC 2459, 4.1.2.1) of the certificate request to
+\var{version}.
+\end{methoddesc}
+
+\begin{methoddesc}[X509Req]{get_version}{}
+Get the version (RFC 2459, 4.1.2.1) of the certificate request.
+\end{methoddesc}
+
\subsubsection{X509Store objects \label{openssl-x509store}}
The X509Store object has currently just one method:
@@ -522,12 +534,16 @@ None if no CA certificates are present.
\subsubsection{X509Extension objects \label{openssl-509ext}}
-X509Extension objects currently only have one method:
+X509Extension objects have several methods:
\begin{methoddesc}[X509Extension]{get_critical}{}
Return the critical field of the extension object.
\end{methoddesc}
+\begin{methoddesc}[X509Extension]{get_short_name}{}
+Return the short type name of the extension object.
+\end{methoddesc}
+
\subsubsection{NetscapeSPKI objects \label{openssl-netscape-spki}}
NetscapeSPKI objects have the following methods:
@@ -545,7 +561,10 @@ Set the public key of the object to \var{key}.
\end{methoddesc}
\begin{methoddesc}[NetscapeSPKI]{sign}{key, digest_name}
-Sign the NetscapeSPKI object using the given \var{key} and \var{digest_name}.
+Sign the NetscapeSPKI object using the given \var{key} and
+\var{digest_name}. \var{digest_name} must be a string describing a digest
+algorithm supported by OpenSSL (by EVP_get_digestbyname, specifically). For
+example, \constant{"md5"} or \constant{"sha1"}.
\end{methoddesc}
\begin{methoddesc}[NetscapeSPKI]{verify}{key}
@@ -747,7 +766,7 @@ Retrieve the Context object's verify depth, as set by
\end{methoddesc}
\begin{methoddesc}[Context]{get_verify_mode}{}
-Retrieve the Context object's verify mode, as set by \method{set_verify_mode}.
+Retrieve the Context object's verify mode, as set by \method{set_verify}.
\end{methoddesc}
\begin{methoddesc}[Context]{load_client_ca}{pemfile}
@@ -755,9 +774,17 @@ Read a file with PEM-formatted certificates that will be sent to the client
when requesting a client certificate.
\end{methoddesc}
-\begin{methoddesc}[Context]{load_verify_locations}{pemfile}
-Specify where CA certificates for verification purposes are located. These are
-trusted certificates. Note that the certificates have to be in PEM format.
+\begin{methoddesc}[Context]{load_verify_locations}{pemfile, capath}
+Specify where CA certificates for verification purposes are located. These
+are trusted certificates. Note that the certificates have to be in PEM
+format. If capath is passed, it must be a directory prepared using the
+\code{c_rehash} tool included with OpenSSL. Either, but not both, of
+\var{pemfile} or \var{capath} may be \code{None}.
+\end{methoddesc}
+
+\begin{methoddesc}[Context]{set_default_verify_paths}{}
+Specify that the platform provided CA certificates are to be used for
+verification purposes.
\end{methoddesc}
\begin{methoddesc}[Context]{load_tmp_dh}{dhfile}
diff --git a/setup.py b/setup.py
index c5c0247..c12b876 100755
--- a/setup.py
+++ b/setup.py
@@ -102,7 +102,13 @@ LibraryDirs = None
# Add more platforms here when needed
if os.name == 'nt' or sys.platform == 'win32':
Libraries = ['eay32', 'Ws2_32']
- ExtraObjects = [r"c:\Python25\libs\ssleay32.a"]
+ # Try to find it...
+ for path in ["c:/Python25/libs/", "C:/Python26/libs/", "C:/OpenSSL/lib/MinGW/"]:
+ if os.path.exists(os.path.join(path, "ssleay32.a")):
+ ExtraObjects = [os.path.join(path, "ssleay32.a")]
+ break
+ else:
+ raise SystemExit("Cannot find ssleay32.a, aborting")
else:
Libraries = ['ssl', 'crypto']
ExtraObjects = []
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index d239a3b..a2b62c4 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -734,7 +734,7 @@ static int init_openssl_threads(void) {
for (i = 0; i < CRYPTO_num_locks(); ++i) {
mutex_buf[i] = PyThread_allocate_lock();
}
- CRYPTO_set_id_callback(PyThread_get_thread_ident);
+ CRYPTO_set_id_callback((unsigned long (*)(void))PyThread_get_thread_ident);
CRYPTO_set_locking_callback(locking_function);
return 1;
}
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index cc56c2c..90a213b 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -365,7 +365,7 @@ _set_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObjec
ASN1_GENERALIZEDTIME dummy;
dummy.type = V_ASN1_GENERALIZEDTIME;
dummy.length = strlen(when);
- dummy.data = when;
+ dummy.data = (unsigned char *)when;
if (!ASN1_GENERALIZEDTIME_check(&dummy)) {
PyErr_SetString(PyExc_ValueError, "Invalid string");
} else {
@@ -440,14 +440,14 @@ _get_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObjec
Py_INCREF(Py_None);
return Py_None;
} else if (timestamp->type == V_ASN1_GENERALIZEDTIME) {
- return PyString_FromString(timestamp->data);
+ return PyString_FromString((char *)timestamp->data);
} else {
ASN1_TIME_to_generalizedtime(timestamp, &gt_timestamp);
if (gt_timestamp == NULL) {
exception_from_error_queue();
return NULL;
} else {
- py_timestamp = PyString_FromString(gt_timestamp->data);
+ py_timestamp = PyString_FromString((char *)gt_timestamp->data);
ASN1_GENERALIZEDTIME_free(gt_timestamp);
return py_timestamp;
}
@@ -652,7 +652,7 @@ crypto_X509_digest(crypto_X509Obj *self, PyObject *args)
unsigned char fp[EVP_MAX_MD_SIZE];
char *tmp;
char *digest_name;
- int len,i;
+ unsigned int len,i;
PyObject *ret;
const EVP_MD *digest;
diff --git a/src/crypto/x509ext.c b/src/crypto/x509ext.c
index a898842..3b3c814 100644
--- a/src/crypto/x509ext.c
+++ b/src/crypto/x509ext.c
@@ -30,6 +30,31 @@ crypto_X509Extension_get_critical(crypto_X509ExtensionObj *self, PyObject *args)
return PyInt_FromLong(X509_EXTENSION_get_critical(self->x509_extension));
}
+static char crypto_X509Extension_get_short_name_doc[] = "\n\
+Returns the short version of the type name of the X509Extension\n\
+\n\
+Arguments: self - The X509Extension object\n\
+ args - The argument tuple, should be empty\n\
+Returns: The short type name.\n\
+";
+
+static PyObject *
+crypto_X509Extension_get_short_name(crypto_X509ExtensionObj *self, PyObject *args) {
+ ASN1_OBJECT *obj;
+ const char *extname;
+
+ if (!PyArg_ParseTuple(args, ":get_short_name")) {
+ return NULL;
+ }
+
+ /* Returns an internal pointer to x509_extension, not a copy */
+ obj = X509_EXTENSION_get_object(self->x509_extension);
+
+ extname = OBJ_nid2sn(OBJ_obj2nid(obj));
+ return PyString_FromString(extname);
+}
+
+
/*
* ADD_METHOD(name) expands to a correct PyMethodDef declaration
* { 'name', (PyCFunction)crypto_X509Extension_name, METH_VARARGS }
@@ -40,6 +65,7 @@ crypto_X509Extension_get_critical(crypto_X509ExtensionObj *self, PyObject *args)
static PyMethodDef crypto_X509Extension_methods[] =
{
ADD_METHOD(get_critical),
+ ADD_METHOD(get_short_name),
{ NULL, NULL }
};
#undef ADD_METHOD
@@ -55,109 +81,63 @@ static PyMethodDef crypto_X509Extension_methods[] =
crypto_X509ExtensionObj *
crypto_X509Extension_New(char *type_name, int critical, char *value)
{
+ X509V3_CTX ctx;
crypto_X509ExtensionObj *self;
- int ext_len, ext_nid;
- unsigned char *ext_der;
- X509V3_EXT_METHOD *ext_method = NULL;
- ASN1_OCTET_STRING *ext_oct;
- STACK_OF(CONF_VALUE) *nval;
- void * ext_struct;
- X509_EXTENSION *extension = NULL;
+ char* value_with_critical = NULL;
- self = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type);
+ /* We have no configuration database - but perhaps we should. Anyhow, the
+ * context is necessary for any extension which uses the r2i conversion
+ * method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx. */
+ X509V3_set_ctx_nodb(&ctx);
- if (self == NULL)
- return NULL;
+ self = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type);
- /* Try to get a NID for the name */
- if ((ext_nid = OBJ_sn2nid(type_name)) == NID_undef)
- {
- PyErr_SetString(PyExc_ValueError, "Unknown extension name");
- return NULL;
+ if (self == NULL) {
+ goto error;
}
- /* Lookup the extension method structure */
- if (!(ext_method = X509V3_EXT_get_nid(ext_nid)))
- {
- PyErr_SetString(PyExc_ValueError, "Unknown extension");
- return NULL;
+ self->dealloc = 0;
+
+ /* There are other OpenSSL APIs which would let us pass in critical
+ * separately, but they're harder to use, and since value is already a pile
+ * of crappy junk smuggling a ton of utterly important structured data,
+ * what's the point of trying to avoid nasty stuff with strings? (However,
+ * X509V3_EXT_i2d in particular seems like it would be a better API to
+ * invoke. I do not know where to get the ext_struc it desires for its
+ * last parameter, though.) */
+ value_with_critical = malloc(strlen("critical,") + strlen(value) + 1);
+ if (!value_with_critical) {
+ goto critical_malloc_error;
}
- /* Look if it has a function to convert value to an
- * internal structure.
- */
- if (!ext_method->v2i)
- {
- PyErr_SetString(PyExc_ValueError, "Can't initialize exception");
- return NULL;
+ if (critical) {
+ strcpy(value_with_critical, "critical,");
+ strcpy(value_with_critical + strlen("critical,"), value);
+ } else {
+ strcpy(value_with_critical, value);
}
- /* Parse the value */
- nval = X509V3_parse_list(value);
- if (!nval)
- {
- PyErr_SetString(PyExc_ValueError, "Invalid extension string");
- return NULL;
- }
+ self->x509_extension = X509V3_EXT_nconf(
+ NULL, &ctx, type_name, value_with_critical);
- /* And use it to get the internal structure */
- if(!(ext_struct = ext_method->v2i(ext_method, NULL, nval))) {
- exception_from_error_queue();
- return NULL;
- }
+ free(value_with_critical);
- /* Deallocate the configuration value stack */
- sk_CONF_VALUE_pop_free(nval, X509V3_conf_free);
-
- /* Find out how much memory we need */
-
-
- /* Convert internal representation to DER */
- /* and Allocate */
- if (ext_method->it) {
- ext_der = NULL;
- ext_len = ASN1_item_i2d(ext_struct, &ext_der, ASN1_ITEM_ptr(ext_method->it));
- if (ext_len < 0) {
- PyErr_SetString(PyExc_MemoryError, "Could not allocate memory");
- return NULL;
- }
- } else {
- unsigned char *p;
- ext_len = ext_method->i2d(ext_struct, NULL);
- if(!(ext_der = malloc(ext_len))) {
- PyErr_SetString(PyExc_MemoryError, "Could not allocate memory");
- return NULL;
- }
- p = ext_der;
- ext_method->i2d(ext_struct, &p);
+ if (!self->x509_extension) {
+ goto nconf_error;
}
- /* And create the ASN1_OCTET_STRING */
- if(!(ext_oct = M_ASN1_OCTET_STRING_new())) {
- exception_from_error_queue();
- return NULL;
- }
-
- ext_oct->data = ext_der;
- ext_oct->length = ext_len;
+ self->dealloc = 1;
+ return self;
- /* Now that we got all ingredients, make the extension */
- extension = X509_EXTENSION_create_by_NID(NULL, ext_nid, critical, ext_oct);
- if (extension == NULL)
- {
- exception_from_error_queue();
- M_ASN1_OCTET_STRING_free(ext_oct);
- ext_method->ext_free(ext_struct);
- return NULL;
- }
-
- M_ASN1_OCTET_STRING_free(ext_oct);
- /* ext_method->ext_free(ext_struct); */
+ nconf_error:
+ exception_from_error_queue();
- self->x509_extension = extension;
- self->dealloc = 1;
+ critical_malloc_error:
+ PyObject_Free(self);
+
+ error:
+ return NULL;
- return self;
}
/*
diff --git a/src/crypto/x509name.c b/src/crypto/x509name.c
index d304591..8871ab1 100644
--- a/src/crypto/x509name.c
+++ b/src/crypto/x509name.c
@@ -13,7 +13,7 @@
#define crypto_MODULE
#include "crypto.h"
-static PyMethodDef crypto_X509Name_methods[];
+static PyMethodDef crypto_X509Name_methods[4];
/*
* Constructor for X509Name, never called by Python code directly
@@ -102,8 +102,9 @@ set_name_by_nid(X509_NAME *name, int nid, char *utf8string)
}
/* Add the new entry */
- if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_UTF8, utf8string,
- -1, -1, 0))
+ if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_UTF8,
+ (unsigned char *)utf8string,
+ -1, -1, 0))
{
exception_from_error_queue();
return -1;
@@ -193,7 +194,7 @@ crypto_X509Name_compare(crypto_X509NameObj *n, crypto_X509NameObj *m)
return -1;
} else if (result > 0) {
return 1;
- } else if (result == 0) {
+ } else {
return 0;
}
}
@@ -318,7 +319,7 @@ crypto_X509Name_get_components(crypto_X509NameObj *self, PyObject *args)
tuple = PyTuple_New(2);
PyTuple_SetItem(tuple, 0, PyString_FromString(OBJ_nid2sn(nid)));
- PyTuple_SetItem(tuple, 1, PyString_FromStringAndSize(str, l));
+ PyTuple_SetItem(tuple, 1, PyString_FromStringAndSize((char *)str, l));
PyList_SetItem(list, i, tuple);
}
diff --git a/src/crypto/x509req.c b/src/crypto/x509req.c
index f367360..bb6385f 100644
--- a/src/crypto/x509req.c
+++ b/src/crypto/x509req.c
@@ -239,6 +239,56 @@ crypto_X509Req_add_extensions(crypto_X509ReqObj *self, PyObject *args)
return Py_None;
}
+static char crypto_X509Req_set_version_doc[] = "\n\
+Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate\n\
+request.\n\
+\n\
+Arguments: self - X509Req object\n\
+ args - The Python argument tuple, should be:\n\
+ version - The version number\n\
+Returns: None\n\
+";
+
+static PyObject *
+crypto_X509Req_set_version(crypto_X509ReqObj *self, PyObject *args)
+{
+ long version;
+
+ if (!PyArg_ParseTuple(args, "l:set_version", &version)) {
+ return NULL;
+ }
+
+ if (!X509_REQ_set_version(self->x509_req, version)) {
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static char crypto_X509Req_get_version_doc[] = "\n\
+Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate\n\
+request.\n\
+\n\
+Arguments: self - X509Req object\n\
+ args - The Python argument tuple, should be empty.\n\
+Returns: an integer giving the value of the version subfield\n\
+";
+
+static PyObject *
+crypto_X509Req_get_version(crypto_X509ReqObj *self, PyObject *args)
+{
+ long version;
+
+ if (!PyArg_ParseTuple(args, ":get_version")) {
+ return NULL;
+ }
+
+ version = X509_REQ_get_version(self->x509_req);
+
+ return PyLong_FromLong(version);
+}
+
/*
* ADD_METHOD(name) expands to a correct PyMethodDef declaration
* { 'name', (PyCFunction)crypto_X509Req_name, METH_VARARGS }
@@ -254,6 +304,8 @@ static PyMethodDef crypto_X509Req_methods[] =
ADD_METHOD(sign),
ADD_METHOD(verify),
ADD_METHOD(add_extensions),
+ ADD_METHOD(set_version),
+ ADD_METHOD(get_version),
{ NULL, NULL }
};
#undef ADD_METHOD
diff --git a/src/rand/rand.c b/src/rand/rand.c
index 9802a92..ff89ebb 100644
--- a/src/rand/rand.c
+++ b/src/rand/rand.c
@@ -15,8 +15,10 @@
* WIN32 or WINDOWS needs to be defined, otherwise we get a
* warning.
*/
-#ifdef MS_WINDOWS
-#define WIN32
+#ifdef MS_WINDOWS
+# ifndef WIN32
+# define WIN32
+# endif
#endif
#include <openssl/rand.h>
diff --git a/src/ssl/context.c b/src/ssl/context.c
index ed0eabe..1fecc9b 100644
--- a/src/ssl/context.c
+++ b/src/ssl/context.c
@@ -11,6 +11,12 @@
*/
#include <Python.h>
+#if PY_VERSION_HEX >= 0x02050000
+# define PYARG_PARSETUPLE_FORMAT const char
+#else
+# define PYARG_PARSETUPLE_FORMAT char
+#endif
+
#ifndef MS_WINDOWS
# include <sys/socket.h>
# include <netinet/in.h>
@@ -156,7 +162,7 @@ global_verify_callback(int ok, X509_STORE_CTX *x509_ctx)
SSL *ssl;
ssl_ConnectionObj *conn;
crypto_X509Obj *cert;
- int errnum, errdepth, c_ret, use_thread_state;
+ int errnum, errdepth, c_ret;
// Get Connection object to check thread state
ssl = (SSL *)X509_STORE_CTX_get_app_data(x509_ctx);
@@ -197,7 +203,7 @@ global_verify_callback(int ok, X509_STORE_CTX *x509_ctx)
* Returns: None
*/
static void
-global_info_callback(SSL *ssl, int where, int _ret)
+global_info_callback(const SSL *ssl, int where, int _ret)
{
ssl_ConnectionObj *conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl);
PyObject *argv, *ret;
@@ -238,18 +244,20 @@ chain\n\
\n\
Arguments: self - The Context object\n\
args - The Python argument tuple, should be:\n\
- cafile - Which file we can find the certificates\n\
+ cafile - In which file we can find the certificates\n\
+ capath - In which directory we can find the certificates\r\
Returns: None\n\
";
static PyObject *
-ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args)
-{
- char *cafile;
+ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args) {
+ char *cafile = NULL;
+ char *capath = NULL;
- if (!PyArg_ParseTuple(args, "s:load_verify_locations", &cafile))
+ if (!PyArg_ParseTuple(args, "z|z:load_verify_locations", &cafile, &capath)) {
return NULL;
+ }
- if (!SSL_CTX_load_verify_locations(self->ctx, cafile, NULL))
+ if (!SSL_CTX_load_verify_locations(self->ctx, cafile, capath))
{
exception_from_error_queue();
return NULL;
@@ -261,6 +269,33 @@ ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args)
}
}
+static char ssl_Context_set_default_verify_paths_doc[] = "\n\
+Use the platform-specific CA certificate locations\n\
+\n\
+Arguments: self - The Context object\n\
+ args - None\n\
+\n\
+Returns: None\n\
+";
+static PyObject *
+ssl_Context_set_default_verify_paths(ssl_ContextObj *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, ":set_default_verify_paths")) {
+ return NULL;
+ }
+
+ /*
+ * XXX Error handling for SSL_CTX_set_default_verify_paths is untested.
+ * -exarkun
+ */
+ if (!SSL_CTX_set_default_verify_paths(self->ctx)) {
+ exception_from_error_queue();
+ return NULL;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+};
+
+
static char ssl_Context_set_passwd_cb_doc[] = "\n\
Set the passphrase callback\n\
\n\
@@ -314,7 +349,7 @@ parse_certificate_argument(const char* format1, const char* format2, PyObject* a
if (!crypto_X509_type)
{
- if (!PyArg_ParseTuple(args, format1, &cert))
+ if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format1, &cert))
return NULL;
if (strcmp(cert->ob_type->tp_name, "X509") != 0 ||
@@ -327,7 +362,7 @@ parse_certificate_argument(const char* format1, const char* format2, PyObject* a
crypto_X509_type = cert->ob_type;
}
else
- if (!PyArg_ParseTuple(args, format2, crypto_X509_type,
+ if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format2, crypto_X509_type,
&cert))
return NULL;
return cert;
@@ -611,8 +646,8 @@ Returns: None\n\
static PyObject *
ssl_Context_set_session_id(ssl_ContextObj *self, PyObject *args)
{
- char *buf;
- int len;
+ unsigned char *buf;
+ unsigned int len;
if (!PyArg_ParseTuple(args, "s#:set_session_id", &buf, &len))
return NULL;
@@ -952,6 +987,7 @@ ssl_Context_set_options(ssl_ContextObj *self, PyObject *args)
static PyMethodDef ssl_Context_methods[] = {
ADD_METHOD(load_verify_locations),
ADD_METHOD(set_passwd_cb),
+ ADD_METHOD(set_default_verify_paths),
ADD_METHOD(use_certificate_chain_file),
ADD_METHOD(use_certificate_file),
ADD_METHOD(use_certificate),
diff --git a/src/ssl/ssl.c b/src/ssl/ssl.c
index 1f8cbcc..f1c51aa 100644
--- a/src/ssl/ssl.c
+++ b/src/ssl/ssl.c
@@ -193,7 +193,20 @@ do { \
PyModule_AddIntConstant(module, "OP_NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG);
PyModule_AddIntConstant(module, "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
- /* For SSL_set_shutdown */
+ /* DTLS related options. The first two of these were introduced in
+ * 2005, the third in 2007. To accomodate systems which are still using
+ * older versions, make them optional. */
+#ifdef SSL_OP_NO_QUERY_MTU
+ PyModule_AddIntConstant(module, "OP_NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU);
+#endif
+#ifdef SSL_OP_COOKIE_EXCHANGE
+ PyModule_AddIntConstant(module, "OP_COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE);
+#endif
+#ifdef SSL_OP_NO_TICKET
+ PyModule_AddIntConstant(module, "OP_NO_TICKET", SSL_OP_NO_TICKET);
+#endif
+
+ /* For SSL_set_shutdown */
PyModule_AddIntConstant(module, "SENT_SHUTDOWN", SSL_SENT_SHUTDOWN);
PyModule_AddIntConstant(module, "RECEIVED_SHUTDOWN", SSL_RECEIVED_SHUTDOWN);
diff --git a/test/test_crypto.py b/test/test_crypto.py
index 31ceb2d..902bde1 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -9,6 +9,7 @@ from unittest import TestCase
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
from OpenSSL.crypto import X509Req, X509ReqType
+from OpenSSL.crypto import X509Extension, X509ExtensionType
from OpenSSL.crypto import FILETYPE_PEM, load_certificate, load_privatekey
from OpenSSL.crypto import dump_privatekey
@@ -83,6 +84,67 @@ class _Python23TestCaseHelper:
+class X509ExtTests(TestCase, _Python23TestCaseHelper):
+ def test_construction(self):
+ """
+ L{X509Extension} accepts an extension type name, a critical flag,
+ and an extension value and returns an L{X509ExtensionType} instance.
+ """
+ basic = X509Extension('basicConstraints', True, 'CA:true')
+ self.assertTrue(
+ isinstance(basic, X509ExtensionType),
+ "%r is of type %r, should be %r" % (
+ basic, type(basic), X509ExtensionType))
+
+ comment = X509Extension('nsComment', False, 'pyOpenSSL unit test')
+ self.assertTrue(
+ isinstance(comment, X509ExtensionType),
+ "%r is of type %r, should be %r" % (
+ comment, type(comment), X509ExtensionType))
+
+
+ def test_invalid_extension(self):
+ """
+ L{X509Extension} raises something if it is passed a bad extension
+ name or value.
+ """
+ self.assertRaises(
+ Error, X509Extension, 'thisIsMadeUp', False, 'hi')
+ self.assertRaises(
+ Error, X509Extension, 'basicConstraints', False, 'blah blah')
+
+ # Exercise a weird one (an extension which uses the r2i method). This
+ # exercises the codepath that requires a non-NULL ctx to be passed to
+ # X509V3_EXT_nconf. It can't work now because we provide no
+ # configuration database. It might be made to work in the future.
+ self.assertRaises(
+ Error, X509Extension, 'proxyCertInfo', True,
+ 'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB')
+
+
+ def test_get_critical(self):
+ """
+ L{X509ExtensionType.get_critical} returns the value of the
+ extension's critical flag.
+ """
+ ext = X509Extension('basicConstraints', True, 'CA:true')
+ self.assertTrue(ext.get_critical())
+ ext = X509Extension('basicConstraints', False, 'CA:true')
+ self.assertFalse(ext.get_critical())
+
+
+ def test_get_short_name(self):
+ """
+ L{X509ExtensionType.get_short_name} returns a string giving the short
+ type name of the extension.
+ """
+ ext = X509Extension('basicConstraints', True, 'CA:true')
+ self.assertEqual(ext.get_short_name(), 'basicConstraints')
+ ext = X509Extension('nsComment', True, 'foo bar')
+ self.assertEqual(ext.get_short_name(), 'nsComment')
+
+
+
class PKeyTests(TestCase, _Python23TestCaseHelper):
"""
Unit tests for L{OpenSSL.crypto.PKey}.
@@ -419,6 +481,20 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin, _Python23TestCaseHelper
"%r is of type %r, should be %r" % (request, type(request), X509ReqType))
+ def test_version(self):
+ """
+ L{X509ReqType.set_version} sets the X.509 version of the certificate
+ request. L{X509ReqType.get_version} returns the X.509 version of
+ the certificate request. The initial value of the version is 0.
+ """
+ request = X509Req()
+ self.assertEqual(request.get_version(), 0)
+ request.set_version(1)
+ self.assertEqual(request.get_version(), 1)
+ request.set_version(3)
+ self.assertEqual(request.get_version(), 3)
+
+
def test_get_subject(self):
"""
L{X509ReqType.get_subject} returns an L{X509Name} for the subject of
diff --git a/test/test_ssl.py b/test/test_ssl.py
index cd07cd5..7fbb359 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -7,12 +7,26 @@ Unit tests for L{OpenSSL.SSL}.
from unittest import TestCase
from tempfile import mktemp
from socket import socket
+from os import makedirs, symlink
+from os.path import join
from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM, PKey, dump_privatekey, load_certificate, load_privatekey
-from OpenSSL.SSL import WantReadError, Context, Connection
+from OpenSSL.SSL import WantReadError, Context, Connection, Error
from OpenSSL.SSL import SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD
-
+from OpenSSL.SSL import VERIFY_PEER
from OpenSSL.test.test_crypto import _Python23TestCaseHelper, cleartextCertificatePEM, cleartextPrivateKeyPEM
+try:
+ from OpenSSL.SSL import OP_NO_QUERY_MTU
+except ImportError:
+ OP_NO_QUERY_MTU = None
+try:
+ from OpenSSL.SSL import OP_COOKIE_EXCHANGE
+except ImportError:
+ OP_COOKIE_EXCHANGE = None
+try:
+ from OpenSSL.SSL import OP_NO_TICKET
+except ImportError:
+ OP_NO_TICKET = None
class ContextTests(TestCase, _Python23TestCaseHelper):
@@ -115,3 +129,174 @@ class ContextTests(TestCase, _Python23TestCaseHelper):
# Kind of lame. Just make sure it got called somehow.
self.assertTrue(called)
+
+
+ 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())
+
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.load_verify_locations(*args)
+ # Require that the server certificate verify properly or the
+ # connection will fail.
+ clientContext.set_verify(
+ VERIFY_PEER,
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+
+ 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))
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+
+ serverSSL = Connection(serverContext, server)
+ serverSSL.set_accept_state()
+
+ for i in range(3):
+ for ssl in clientSSL, serverSSL:
+ try:
+ # Without load_verify_locations above, the handshake
+ # will fail:
+ # Error: [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE',
+ # 'certificate verify failed')]
+ ssl.do_handshake()
+ except WantReadError:
+ pass
+
+ cert = clientSSL.get_peer_certificate()
+ self.assertEqual(cert.get_subject().CN, 'pyopenssl.sf.net')
+
+ def test_load_verify_file(self):
+ """
+ L{Context.load_verify_locations} accepts a file name and uses the
+ certificates within for verification purposes.
+ """
+ cafile = self.mktemp()
+ fObj = file(cafile, 'w')
+ fObj.write(cleartextCertificatePEM)
+ fObj.close()
+
+ self._load_verify_locations_test(cafile)
+
+
+ def test_load_verify_invalid_file(self):
+ """
+ L{Context.load_verify_locations} raises L{Error} when passed a
+ non-existent cafile.
+ """
+ clientContext = Context(TLSv1_METHOD)
+ self.assertRaises(
+ Error, clientContext.load_verify_locations, self.mktemp())
+
+
+ def test_load_verify_directory(self):
+ """
+ L{Context.load_verify_locations} accepts a directory name and uses
+ the certificates within for verification purposes.
+ """
+ capath = self.mktemp()
+ makedirs(capath)
+ cafile = join(capath, 'cert.pem')
+ fObj = file(cafile, 'w')
+ fObj.write(cleartextCertificatePEM)
+ fObj.close()
+
+ # Hash value computed manually with c_rehash to avoid depending on
+ # c_rehash in the test suite.
+ symlink('cert.pem', join(capath, '07497d9e.0'))
+
+ self._load_verify_locations_test(None, capath)
+
+
+ def test_set_default_verify_paths(self):
+ """
+ L{Context.set_default_verify_paths} causes the platform-specific CA
+ certificate locations to be used for verification purposes.
+ """
+ # Testing this requires a server with a certificate signed by one of
+ # the CAs in the platform CA location. Getting one of those costs
+ # money. Fortunately (or unfortunately, depending on your
+ # perspective), it's easy to think of a public server on the
+ # internet which has such a certificate. Connecting to the network
+ # in a unit test is bad, but it's the only way I can think of to
+ # really test this. -exarkun
+
+ # Arg, verisign.com doesn't speak TLSv1
+ context = Context(SSLv3_METHOD)
+ context.set_default_verify_paths()
+ context.set_verify(
+ VERIFY_PEER,
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+
+ client = socket()
+ client.connect(('verisign.com', 443))
+ clientSSL = Connection(context, client)
+ clientSSL.set_connect_state()
+ clientSSL.do_handshake()
+ clientSSL.send('GET / HTTP/1.0\r\n\r\n')
+ self.assertTrue(clientSSL.recv(1024))
+
+
+ def test_set_default_verify_paths_signature(self):
+ """
+ L{Context.set_default_verify_paths} takes no arguments and raises
+ L{TypeError} if given any.
+ """
+ context = Context(TLSv1_METHOD)
+ self.assertRaises(TypeError, context.set_default_verify_paths, None)
+ self.assertRaises(TypeError, context.set_default_verify_paths, 1)
+ self.assertRaises(TypeError, context.set_default_verify_paths, "")
+
+
+
+class ConstantsTests(TestCase):
+ """
+ Tests for the values of constants exposed in L{OpenSSL.SSL}.
+
+ These are values defined by OpenSSL intended only to be used as flags to
+ OpenSSL APIs. The only assertions it seems can be made about them is
+ their values.
+ """
+ # unittest.TestCase has no skip mechanism
+ if OP_NO_QUERY_MTU is not None:
+ def test_op_no_query_mtu(self):
+ """
+ The value of L{OpenSSL.SSL.OP_NO_QUERY_MTU} is 0x1000, the value of
+ I{SSL_OP_NO_QUERY_MTU} defined by I{openssl/ssl.h}.
+ """
+ self.assertEqual(OP_NO_QUERY_MTU, 0x1000)
+ else:
+ "OP_NO_QUERY_MTU unavailable - OpenSSL version may be too old"
+
+
+ if OP_COOKIE_EXCHANGE is not None:
+ def test_op_cookie_exchange(self):
+ """
+ The value of L{OpenSSL.SSL.OP_COOKIE_EXCHANGE} is 0x2000, the value
+ of I{SSL_OP_COOKIE_EXCHANGE} defined by I{openssl/ssl.h}.
+ """
+ self.assertEqual(OP_COOKIE_EXCHANGE, 0x2000)
+ else:
+ "OP_COOKIE_EXCHANGE unavailable - OpenSSL version may be too old"
+
+
+ if OP_NO_TICKET is not None:
+ def test_op_no_ticket(self):
+ """
+ The value of L{OpenSSL.SSL.OP_NO_TICKET} is 0x4000, the value of
+ I{SSL_OP_NO_TICKET} defined by I{openssl/ssl.h}.
+ """
+ self.assertEqual(OP_NO_TICKET, 0x4000)
+ else:
+ "OP_NO_TICKET unavailable - OpenSSL version may be too old"