Hello community,
here is the log from the commit of package python-openssl for openSUSE:Factory
checked in at Fri Aug 7 21:54:32 CEST 2009.
--------
--- python-openssl/python-openssl.changes 2009-02-02 15:31:02.000000000 +0100
+++ python-openssl/python-openssl.changes 2009-08-05 20:58:57.000000000 +0200
@@ -1,0 +2,9 @@
+Wed Aug 5 20:57:34 CEST 2009 - matejcik@suse.cz
+
+- update to 0.9
+ * less compiler warnings
+ * couple bugfixes
+ * couple new methods and parameters exposed
+- removed all patches, as they are no longer needed
+
+-------------------------------------------------------------------
calling whatdependson for head-i586
Old:
----
pyOpenSSL-0.6-ssize.patch
pyOpenSSL-0.7a2-noreturn.patch
pyOpenSSL-0.8-default-certstore.diff
pyOpenSSL-0.8.tar.bz2
New:
----
pyOpenSSL-0.9.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-openssl.spec ++++++
--- /var/tmp/diff_new_pack.6AkTMu/_old 2009-08-07 21:53:18.000000000 +0200
+++ /var/tmp/diff_new_pack.6AkTMu/_new 2009-08-07 21:53:18.000000000 +0200
@@ -1,5 +1,5 @@
#
-# spec file for package python-openssl (Version 0.8.0)
+# spec file for package python-openssl (Version 0.9)
#
# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany.
#
@@ -21,13 +21,9 @@
Name: python-openssl
BuildRequires: openssl-devel python-devel
Summary: Python wrapper module around the OpenSSL library
-Version: 0.8.0
+Version: 0.9
Release: 1
-#Source: pyOpenSSL-%{version}.tar.bz2
-Source: pyOpenSSL-0.8.tar.bz2
-Patch0: pyOpenSSL-0.6-ssize.patch
-Patch1: pyOpenSSL-0.7a2-noreturn.patch
-Patch2: pyOpenSSL-0.8-default-certstore.diff
+Source: pyOpenSSL-%{version}.tar.bz2
License: LGPL v2.1 or later
Group: Development/Languages/Python
Url: https://launchpad.net/pyopenssl/
@@ -48,15 +44,10 @@
Authors:
--------
- Martin Sj�gren
+ Martin Sjoegren
%prep
-#%setup -n pyOpenSSL-%{version}
-%setup -n pyOpenSSL-0.8
-# needed?
-#patch0
-%patch1
-%patch2 -p0
+%setup -n pyOpenSSL-%{version}
%build
#export CFLAGS="$RPM_OPT_FLAGS"
++++++ pyOpenSSL-0.8.tar.bz2 -> pyOpenSSL-0.9.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/ChangeLog new/pyOpenSSL-0.9/ChangeLog
--- old/pyOpenSSL-0.8/ChangeLog 2008-10-19 18:01:32.000000000 +0200
+++ new/pyOpenSSL-0.9/ChangeLog 2009-04-25 14:47:05.000000000 +0200
@@ -1,3 +1,60 @@
+2009-04-25 Jean-Paul Calderone
+
+ * Release 0.9
+
+2009-04-01 Jean-Paul Calderone
+ Samuele Pedroni
+
+ * src/util.h: Delete the TLS key before trying to set a new value
+ for it in case the current thread identifier is a recycled one (if
+ it is recycled, the key won't be set because there is already a
+ value from the previous thread to have this identifier and to use
+ the pyOpenSSL API).
+
+2009-04-01 Jean-Paul Calderone
+
+ * src/crypto/crypto.c: Add FILETYPE_TEXT for dumping keys and
+ certificates and certificate signature requests to a text format.
+
+2008-12-31 Jean-Paul Calderone
+
+ * 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
+
+ * 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
+
+ * 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
+
+ * 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
+
+ * 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
+
+ * 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
* Release 0.8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/MANIFEST.in new/pyOpenSSL-0.9/MANIFEST.in
--- old/pyOpenSSL-0.8/MANIFEST.in 2008-09-22 02:36:54.000000000 +0200
+++ new/pyOpenSSL-0.9/MANIFEST.in 2009-01-30 20:37:04.000000000 +0100
@@ -4,7 +4,4 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/PKG-INFO new/pyOpenSSL-0.9/PKG-INFO
--- old/pyOpenSSL-0.8/PKG-INFO 2008-10-19 18:01:33.000000000 +0200
+++ new/pyOpenSSL-0.9/PKG-INFO 2009-04-25 14:47:12.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: pyOpenSSL
-Version: 0.8
+Version: 0.9
Summary: Python wrapper module around the OpenSSL library
Home-page: http://pyopenssl.sourceforge.net/
Author: Jean-Paul Calderone
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/doc/pyOpenSSL.tex new/pyOpenSSL-0.9/doc/pyOpenSSL.tex
--- old/pyOpenSSL-0.8/doc/pyOpenSSL.tex 2008-10-19 18:01:32.000000000 +0200
+++ new/pyOpenSSL-0.9/doc/pyOpenSSL.tex 2009-04-25 14:47:06.000000000 +0200
@@ -2,7 +2,7 @@
\title{Python OpenSSL Manual}
-\release{0.8}
+\release{0.9}
\author{Martin Sj�gren}
\authoraddress{\email{martin@strakt.com}}
@@ -370,6 +370,9 @@
\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 @@
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 @@
\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 @@
\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 @@
\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 @@
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. This method may not work properly on OS X.
\end{methoddesc}
\begin{methoddesc}[Context]{load_tmp_dh}{dhfile}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/setup.py new/pyOpenSSL-0.9/setup.py
--- old/pyOpenSSL-0.8/setup.py 2008-09-22 02:36:54.000000000 +0200
+++ new/pyOpenSSL-0.9/setup.py 2009-03-07 17:50:11.000000000 +0100
@@ -11,26 +11,12 @@
Installation script for the OpenSSL module
"""
-from distutils.core import setup, Extension
-import os, sys
+import sys, os
+from distutils.core import Extension, setup
-from version import __version__
-
-# A hack to determine if Extension objects support the depends keyword arg.
-try:
- init_func = Extension.__init__.func_code
- has_dep = 'depends' in init_func.co_varnames
-except:
- has_dep = 0
-if not has_dep:
- # If it doesn't, create a local replacement that removes depends
- # from the kwargs before calling the regular constructor.
- _Extension = Extension
- class Extension(_Extension):
- def __init__(self, name, sources, **kwargs):
- kwargs.pop('depends', None)
- _Extension.__init__(self, name, sources, **kwargs)
+from glob import glob
+from version import __version__
crypto_src = ['src/crypto/crypto.c', 'src/crypto/x509.c',
'src/crypto/x509name.c', 'src/crypto/pkey.c',
@@ -57,7 +43,17 @@
# 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:\\OpenSSL\\lib\\MinGW", "C:\\Python23\\libs",
+ "C:\\Python24\\libs", "C:\\Python25\\libs", "C:\\Python26\\libs"]:
+ # The .a is the "export library". It's the thing we need to link
+ # against to let us use the .dll.
+ ssleay32 = os.path.join(path, "ssleay32.a")
+ if os.path.exists(ssleay32):
+ ExtraObjects = [ssleay32]
+ break
+ else:
+ raise SystemExit("Cannot find ssleay32.a, aborting")
else:
Libraries = ['ssl', 'crypto']
ExtraObjects = []
@@ -66,6 +62,16 @@
IncludeDirs = ['/sw/include']
LibraryDirs = ['/sw/lib']
+# On Windows, make sure the necessary .dll's get added to the egg.
+data_files = []
+if sys.platform == 'win32':
+ import ctypes.util
+ libeay32 = ctypes.util.find_library("libeay32")
+ if libeay32 is None:
+ raise SystemExit("Cannot find libeay32.dll, aborting")
+ data_files = [("OpenSSL", [libeay32])]
+
+
def mkExtension(name):
modname = 'OpenSSL.' + name
src = globals()[name.lower() + '_src']
@@ -82,6 +88,7 @@
'OpenSSL.version', 'OpenSSL.test.__init__',
'OpenSSL.test.test_crypto',
'OpenSSL.test.test_ssl'],
+ data_files = data_files,
description = 'Python wrapper module around the OpenSSL library',
author = 'Martin Sjögren, AB Strakt',
author_email = 'msjogren@gmail.com',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/crypto.c new/pyOpenSSL-0.9/src/crypto/crypto.c
--- old/pyOpenSSL-0.8/src/crypto/crypto.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/crypto.c 2009-04-25 14:47:06.000000000 +0200
@@ -147,6 +147,7 @@
pem_password_cb *cb = NULL;
void *cb_arg = NULL;
BIO *bio;
+ RSA *rsa;
crypto_PKeyObj *pkey;
if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type,
@@ -199,8 +200,14 @@
ret = i2d_PrivateKey_bio(bio, pkey->pkey);
break;
+ case X509_FILETYPE_TEXT:
+ rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+ ret = RSA_print(bio, rsa, 0);
+ RSA_free(rsa);
+ break;
+
default:
- PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+ PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
BIO_free(bio);
return NULL;
}
@@ -302,8 +309,12 @@
ret = i2d_X509_bio(bio, cert->x509);
break;
+ case X509_FILETYPE_TEXT:
+ ret = X509_print_ex(bio, cert->x509, 0, 0);
+ break;
+
default:
- PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+ PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
BIO_free(bio);
return NULL;
}
@@ -405,8 +416,12 @@
ret = i2d_X509_REQ_bio(bio, req->x509_req);
break;
+ case X509_FILETYPE_TEXT:
+ ret = X509_REQ_print_ex(bio, req->x509_req, 0, 0);
+ break;
+
default:
- PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+ PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
BIO_free(bio);
return NULL;
}
@@ -734,7 +749,7 @@
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;
}
@@ -801,6 +816,7 @@
PyModule_AddIntConstant(module, "FILETYPE_PEM", X509_FILETYPE_PEM);
PyModule_AddIntConstant(module, "FILETYPE_ASN1", X509_FILETYPE_ASN1);
+ PyModule_AddIntConstant(module, "FILETYPE_TEXT", X509_FILETYPE_TEXT);
PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA);
PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/crypto.h new/pyOpenSSL-0.9/src/crypto/crypto.h
--- old/pyOpenSSL-0.8/src/crypto/crypto.h 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/crypto.h 2009-04-25 14:47:06.000000000 +0200
@@ -117,4 +117,12 @@
#endif /* crypto_MODULE */
+/* Define a new type for emitting text. Hopefully these don't collide with
+ * future official OpenSSL constants, but the switch statement of
+ * dump_certificate() will alert us if it matters.
+ */
+#ifndef X509_FILETYPE_TEXT
+#define X509_FILETYPE_TEXT (58)
+#endif
+
#endif /* PyOpenSSL_CRYPTO_H_ */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/x509.c new/pyOpenSSL-0.9/src/crypto/x509.c
--- old/pyOpenSSL-0.8/src/crypto/x509.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/x509.c 2009-01-30 20:37:04.000000000 +0100
@@ -365,7 +365,7 @@
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 @@
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, >_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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/x509ext.c new/pyOpenSSL-0.9/src/crypto/x509ext.c
--- old/pyOpenSSL-0.8/src/crypto/x509ext.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/x509ext.c 2009-01-30 20:37:04.000000000 +0100
@@ -30,6 +30,31 @@
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 @@
static PyMethodDef crypto_X509Extension_methods[] =
{
ADD_METHOD(get_critical),
+ ADD_METHOD(get_short_name),
{ NULL, NULL }
};
#undef ADD_METHOD
@@ -55,109 +81,63 @@
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;
- /* 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;
+ /* 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;
+ }
+
+ 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 */
+ if (!self->x509_extension) {
+ goto nconf_error;
+ }
+ self->dealloc = 1;
+ return self;
- /* 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);
- }
+ nconf_error:
+ exception_from_error_queue();
- /* 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;
-
- /* 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); */
+ critical_malloc_error:
+ PyObject_Free(self);
- self->x509_extension = extension;
- self->dealloc = 1;
+ error:
+ return NULL;
- return self;
}
/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/x509name.c new/pyOpenSSL-0.9/src/crypto/x509name.c
--- old/pyOpenSSL-0.8/src/crypto/x509name.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/x509name.c 2009-01-30 20:37:04.000000000 +0100
@@ -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 @@
}
/* 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 @@
return -1;
} else if (result > 0) {
return 1;
- } else if (result == 0) {
+ } else {
return 0;
}
}
@@ -318,7 +319,7 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/crypto/x509req.c new/pyOpenSSL-0.9/src/crypto/x509req.c
--- old/pyOpenSSL-0.8/src/crypto/x509req.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/crypto/x509req.c 2009-01-30 20:37:04.000000000 +0100
@@ -239,6 +239,56 @@
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 @@
ADD_METHOD(sign),
ADD_METHOD(verify),
ADD_METHOD(add_extensions),
+ ADD_METHOD(set_version),
+ ADD_METHOD(get_version),
{ NULL, NULL }
};
#undef ADD_METHOD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/rand/rand.c new/pyOpenSSL-0.9/src/rand/rand.c
--- old/pyOpenSSL-0.8/src/rand/rand.c 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/src/rand/rand.c 2009-01-30 20:37:04.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/ssl/context.c new/pyOpenSSL-0.9/src/ssl/context.c
--- old/pyOpenSSL-0.8/src/ssl/context.c 2008-09-22 14:50:53.000000000 +0200
+++ new/pyOpenSSL-0.9/src/ssl/context.c 2009-01-30 20:37:04.000000000 +0100
@@ -11,6 +11,12 @@
*/
#include
+#if PY_VERSION_HEX >= 0x02050000
+# define PYARG_PARSETUPLE_FORMAT const char
+#else
+# define PYARG_PARSETUPLE_FORMAT char
+#endif
+
#ifndef MS_WINDOWS
# include
# include
@@ -156,7 +162,7 @@
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 @@
* 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 @@
\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 @@
}
}
+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 @@
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 @@
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 @@
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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/ssl/ssl.c new/pyOpenSSL-0.9/src/ssl/ssl.c
--- old/pyOpenSSL-0.8/src/ssl/ssl.c 2008-09-22 14:50:53.000000000 +0200
+++ new/pyOpenSSL-0.9/src/ssl/ssl.c 2009-01-30 20:37:04.000000000 +0100
@@ -193,7 +193,20 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/src/util.h new/pyOpenSSL-0.9/src/util.h
--- old/pyOpenSSL-0.8/src/util.h 2008-09-22 14:50:53.000000000 +0200
+++ new/pyOpenSSL-0.9/src/util.h 2009-04-25 14:47:06.000000000 +0200
@@ -37,6 +37,7 @@
* some Python.
*/
# define MY_BEGIN_ALLOW_THREADS(ignored) \
+ PyThread_delete_key_value(_pyOpenSSL_tstate_key); \
PyThread_set_key_value(_pyOpenSSL_tstate_key, PyEval_SaveThread());
/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/test/test_crypto.py new/pyOpenSSL-0.9/test/test_crypto.py
--- old/pyOpenSSL-0.8/test/test_crypto.py 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/test/test_crypto.py 2009-04-25 14:47:06.000000000 +0200
@@ -4,71 +4,88 @@
Unit tests for L{OpenSSL.crypto}.
"""
-from unittest import TestCase
+from unittest import TestCase, main
+from os import popen2
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 FILETYPE_PEM, load_certificate, load_privatekey
-from OpenSSL.crypto import dump_privatekey
+from OpenSSL.crypto import X509Extension, X509ExtensionType
+from OpenSSL.crypto import load_certificate, load_privatekey
+from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT
+from OpenSSL.crypto import dump_certificate, load_certificate_request
+from OpenSSL.crypto import dump_certificate_request, dump_privatekey
+
+
+cleartextCertificatePEM = """-----BEGIN CERTIFICATE-----
+MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
+NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
+MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
+ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
+urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
+2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
+1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
+FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
+VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
+BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
+b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
+AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
+hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
+w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
+-----END CERTIFICATE-----
+"""
+cleartextPrivateKeyPEM = """-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
+jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
+3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
+AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
+yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
+6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
+BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
+u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
+PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
+I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
+ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
+6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
+cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
+-----END RSA PRIVATE KEY-----
+"""
-cleartextPrivateKeyPEM = (
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIICXAIBAAKBgQDaemNe1syksAbFFpF3aoOrZ18vB/IQNZrAjFqXPv9iieJm7+Tc\n"
- "g+lA/v0qmoEKrpT2xfwxXmvZwBNM4ZhyRC3DPIFEyJV7/3IA1p5iuMY/GJI1VIgn\n"
- "aikQCnrsyxtaRpsMBeZRniaVzcUJ+XnEdFGEjlo+k0xlwfVclDEMwgpXAQIDAQAB\n"
- "AoGBALi0a7pMQqqgnriVAdpBVJveQtxSDVWi2/gZMKVZfzNheuSnv4amhtaKPKJ+\n"
- "CMZtHkcazsE2IFvxRN/kgato9H3gJqq8nq2CkdpdLNVKBoxiCtkLfutdY4SQLtoY\n"
- "USN7exk131pchsAJXYlR6mCW+ZP+E523cNwpPgsyKxVbmXSBAkEA9470fy2W0jFM\n"
- "taZFslpntKSzbvn6JmdtjtvWrM1bBaeeqFiGBuQFYg46VaCUaeRWYw02jmYAsDYh\n"
- "ZQavmXThaQJBAOHtlAQ0IJJEiMZr6vtVPH32fmbthSv1AUSYPzKqdlQrUnOXPQXu\n"
- "z70cFoLG1TvPF5rBxbOkbQ/s8/ka5ZjPfdkCQCeC7YsO36+UpsWnUCBzRXITh4AC\n"
- "7eYLQ/U1KUJTVF/GrQ/5cQrQgftwgecAxi9Qfmk4xqhbp2h4e0QAmS5I9WECQH02\n"
- "0QwrX8nxFeTytr8pFGezj4a4KVCdb2B3CL+p3f70K7RIo9d/7b6frJI6ZL/LHQf2\n"
- "UP4pKRDkgKsVDx7MELECQGm072/Z7vmb03h/uE95IYJOgY4nfmYs0QKA9Is18wUz\n"
- "DpjfE33p0Ha6GO1VZRIQoqE24F8o5oimy3BEjryFuw4=\n"
- "-----END RSA PRIVATE KEY-----\n")
-
-
-cleartextCertificatePEM = (
- "-----BEGIN CERTIFICATE-----\n"
- "MIICfTCCAeYCAQEwDQYJKoZIhvcNAQEEBQAwgYYxCzAJBgNVBAYTAlVTMRkwFwYD\n"
- "VQQDExBweW9wZW5zc2wuc2YubmV0MREwDwYDVQQHEwhOZXcgWW9yazESMBAGA1UE\n"
- "ChMJUHlPcGVuU1NMMREwDwYDVQQIEwhOZXcgWW9yazEQMA4GCSqGSIb3DQEJARYB\n"
- "IDEQMA4GA1UECxMHVGVzdGluZzAeFw0wODAzMjUxOTA0MTNaFw0wOTAzMjUxOTA0\n"
- "MTNaMIGGMQswCQYDVQQGEwJVUzEZMBcGA1UEAxMQcHlvcGVuc3NsLnNmLm5ldDER\n"
- "MA8GA1UEBxMITmV3IFlvcmsxEjAQBgNVBAoTCVB5T3BlblNTTDERMA8GA1UECBMI\n"
- "TmV3IFlvcmsxEDAOBgkqhkiG9w0BCQEWASAxEDAOBgNVBAsTB1Rlc3RpbmcwgZ8w\n"
- "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSwBsUWkXdqg6tnXy8H8hA1\n"
- "msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nAE0zhmHJELcM8gUTIlXv/\n"
- "cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXNxQn5ecR0UYSOWj6TTGXB\n"
- "9VyUMQzCClcBAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAmm0Vzvv1O91WLl2LnF2P\n"
- "q55LJdOnJbCCXIgxLdoVmvYAz1ZJq1eGKgKWI5QLgxiSzJLEU7KK//aVfiZzoCd5\n"
- "RipBiEEMEV4eAY317bHPwPP+4Bj9t0l8AsDLseC5vLRHgxrLEu3bn08DYx6imB5Q\n"
- "UBj849/xpszEM7BhwKE0GiQ=\n"
- "-----END CERTIFICATE-----\n")
-
-
-encryptedPrivateKeyPEM = (
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "Proc-Type: 4,ENCRYPTED\n"
- "DEK-Info: BF-CBC,8306665233D056B1\n"
- "\n"
- "BwxghOcX1F+M108qRGBfpUBrfaeKOszDEV18OjEE55p0yGsiDxvdol3c4bwI5ITy\n"
- "ltP8w9O33CDUCjr+Ymj8xLpPP60TTfr/aHq+2fEuG4TfkeHb5fVYm0mgVnaOhJs3\n"
- "a2n5IL/KNCdP3zMZa0IaMJ0M+VK90SLpq5nzXOWkufLyZL1+n8srkk06gepmHS7L\n"
- "rH3rALNboG8yTH1qjE8PwcMrJAQfRMd4/4RTQv+4pUuKj7I2en+YwSQ/gomy7qN1\n"
- "3s/gMgV/2GUbEcTVch4thZ9l3WsX18V76rBQkiZ7yrJkxwNMv+Qc2GfHtBnsXAyA\n"
- "0nIE4Mm/OQqX8h7EJ4c2s1DMGVS0YZGU+75HN0A3iD01h8C5utqSScWzBA45j/Vy\n"
- "3aypQVqQeW7kBMQlpc6pHvJ1EsjiAJRCto7tZNLxRdjMKBV4w75JNLaAFSraqA+R\n"
- "/WPcdcXAQuhmCeh31fzmVOHJGRF7/5pAR/b7AnFTD4YbYVcglNis/jpdiI9k2AYP\n"
- "wZNwXOIh6Ibq5hMvyV4/pySyLbgDOrfrOGpi8N6lBbzewByYQKiXwUEZf+Y5499/\n"
- "CckajBhgYynPpe6mgsSeklWGc845iIwAtzavBNZIkn1hKP1P+TFjbl2O75u/9JLJ\n"
- "6i4IFYCyQmwiHX8nTR717SpCN2gyZ2HrX7z2mKP/KokkAX2yidwoKh9FMUV5lOGO\n"
- "JPc4MfPo4lPB7SP30AtOh7y7zlS3x8Uo0+0wCg5Z5Fn/73x3W+p5nyI0G9n7RGzL\n"
- "ZeCWLdG/Cm6ZyIpYZGbZ5m+U3Fr6/El9V6LSxrB1TB+8G1NTdLlbeA==\n"
- "-----END RSA PRIVATE KEY-----\n")
+cleartextCertificateRequestPEM = (
+ "-----BEGIN CERTIFICATE REQUEST-----\n"
+ "MIIBnjCCAQcCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQH\n"
+ "EwdDaGljYWdvMRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEXMBUGA1UEAxMORnJl\n"
+ "ZGVyaWNrIERlYW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSw\n"
+ "BsUWkXdqg6tnXy8H8hA1msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nA\n"
+ "E0zhmHJELcM8gUTIlXv/cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXN\n"
+ "xQn5ecR0UYSOWj6TTGXB9VyUMQzCClcBAgMBAAGgADANBgkqhkiG9w0BAQUFAAOB\n"
+ "gQAAJGuF/R/GGbeC7FbFW+aJgr9ee0Xbl6nlhu7pTe67k+iiKT2dsl2ti68MVTnu\n"
+ "Vrb3HUNqOkiwsJf6kCtq5oPn3QVYzTa76Dt2y3Rtzv6boRSlmlfrgS92GNma8JfR\n"
+ "oICQk3nAudi6zl1Dix3BCv1pUp5KMtGn3MeDEi6QFGy2rA==\n"
+ "-----END CERTIFICATE REQUEST-----\n")
+
+encryptedPrivateKeyPEM = """-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,9573604A18579E9E
+
+SHOho56WxDkT0ht10UTeKc0F5u8cqIa01kzFAmETw0MAs8ezYtK15NPdCXUm3X/2
+a17G7LSF5bkxOgZ7vpXyMzun/owrj7CzvLxyncyEFZWvtvzaAhPhvTJtTIB3kf8B
+8+qRcpTGK7NgXEgYBW5bj1y4qZkD4zCL9o9NQzsKI3Ie8i0239jsDOWR38AxjXBH
+mGwAQ4Z6ZN5dnmM4fhMIWsmFf19sNyAML4gHenQCHhmXbjXeVq47aC2ProInJbrm
++00TcisbAQ40V9aehVbcDKtS4ZbMVDwncAjpXpcncC54G76N6j7F7wL7L/FuXa3A
+fvSVy9n2VfF/pJ3kYSflLHH2G/DFxjF7dl0GxhKPxJjp3IJi9VtuvmN9R2jZWLQF
+tfC8dXgy/P9CfFQhlinqBTEwgH0oZ/d4k4NVFDSdEMaSdmBAjlHpc+Vfdty3HVnV
+rKXj//wslsFNm9kIwJGIgKUa/n2jsOiydrsk1mgH7SmNCb3YHgZhbbnq0qLat/HC
+gHDt3FHpNQ31QzzL3yrenFB2L9osIsnRsDTPFNi4RX4SpDgNroxOQmyzCCV6H+d4
+o1mcnNiZSdxLZxVKccq0AfRpHqpPAFnJcQHP6xyT9MZp6fBa0XkxDnt9kNU8H3Qw
+7SJWZ69VXjBUzMlQViLuaWMgTnL+ZVyFZf9hTF7U/ef4HMLMAVNdiaGG+G+AjCV/
+MbzjS007Oe4qqBnCWaFPSnJX6uLApeTbqAxAeyCql56ULW5x6vDMNC3dwjvS/CEh
+11n8RkgFIQA0AhuKSIg3CbuartRsJnWOLwgLTzsrKYL4yRog1RJrtw==
+-----END RSA PRIVATE KEY-----
+"""
encryptedPrivateKeyPEMPassphrase = "foobar"
@@ -83,6 +100,67 @@
+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 +497,20 @@
"%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
@@ -541,7 +633,7 @@
internally.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertEqual(cert.get_notBefore(), "20080325190413Z")
+ self.assertEqual(cert.get_notBefore(), "20090325123658Z")
def test_get_notAfter(self):
@@ -551,7 +643,7 @@
internally.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertEqual(cert.get_notAfter(), "20090325190413Z")
+ self.assertEqual(cert.get_notAfter(), "20170611123658Z")
def test_digest(self):
@@ -635,6 +727,73 @@
self.assertEqual(loadedKey.bits(), key.bits())
+ def _runopenssl(self, pem, *args):
+ """
+ Run the command line openssl tool with the given arguments and write
+ the given PEM to its stdin.
+ """
+ write, read = popen2(" ".join(("openssl",) + args))
+ write.write(pem)
+ write.close()
+ return read.read()
+
+
+ def test_dump_certificate(self):
+ """
+ L{dump_certificate} writes PEM, DER, and text.
+ """
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+ cert = load_certificate(FILETYPE_PEM, pemData)
+ dumped_pem = dump_certificate(FILETYPE_PEM, cert)
+ self.assertEqual(dumped_pem, cleartextCertificatePEM)
+ dumped_der = dump_certificate(FILETYPE_ASN1, cert)
+ good_der = self._runopenssl(dumped_pem, "x509", "-outform", "DER")
+ self.assertEqual(dumped_der, good_der)
+ cert2 = load_certificate(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_certificate(FILETYPE_PEM, cert2)
+ self.assertEqual(dumped_pem2, cleartextCertificatePEM)
+ dumped_text = dump_certificate(FILETYPE_TEXT, cert)
+ good_text = self._runopenssl(dumped_pem, "x509", "-noout", "-text")
+ self.assertEqual(dumped_text, good_text)
+
+
+ def test_dump_privatekey(self):
+ """
+ L{dump_privatekey} writes a PEM, DER, and text.
+ """
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ dumped_pem = dump_privatekey(FILETYPE_PEM, key)
+ self.assertEqual(dumped_pem, cleartextPrivateKeyPEM)
+ dumped_der = dump_privatekey(FILETYPE_ASN1, key)
+ # XXX This OpenSSL call writes "writing RSA key" to standard out. Sad.
+ good_der = self._runopenssl(dumped_pem, "rsa", "-outform", "DER")
+ self.assertEqual(dumped_der, good_der)
+ key2 = load_privatekey(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2)
+ self.assertEqual(dumped_pem2, cleartextPrivateKeyPEM)
+ dumped_text = dump_privatekey(FILETYPE_TEXT, key)
+ good_text = self._runopenssl(dumped_pem, "rsa", "-noout", "-text")
+ self.assertEqual(dumped_text, good_text)
+
+
+ def test_dump_certificate_request(self):
+ """
+ L{dump_certificate_request} writes a PEM, DER, and text.
+ """
+ req = load_certificate_request(FILETYPE_PEM, cleartextCertificateRequestPEM)
+ dumped_pem = dump_certificate_request(FILETYPE_PEM, req)
+ self.assertEqual(dumped_pem, cleartextCertificateRequestPEM)
+ dumped_der = dump_certificate_request(FILETYPE_ASN1, req)
+ good_der = self._runopenssl(dumped_pem, "req", "-outform", "DER")
+ self.assertEqual(dumped_der, good_der)
+ req2 = load_certificate_request(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2)
+ self.assertEqual(dumped_pem2, cleartextCertificateRequestPEM)
+ dumped_text = dump_certificate_request(FILETYPE_TEXT, req)
+ good_text = self._runopenssl(dumped_pem, "req", "-noout", "-text")
+ self.assertEqual(dumped_text, good_text)
+
+
def test_dump_privatekey_passphraseCallback(self):
"""
L{dump_privatekey} writes an encrypted PEM when given a callback which
@@ -653,3 +812,7 @@
self.assertTrue(isinstance(loadedKey, PKeyType))
self.assertEqual(loadedKey.type(), key.type())
self.assertEqual(loadedKey.bits(), key.bits())
+
+
+if __name__ == '__main__':
+ main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/test/test_ssl.py new/pyOpenSSL-0.9/test/test_ssl.py
--- old/pyOpenSSL-0.8/test/test_ssl.py 2008-09-22 02:36:55.000000000 +0200
+++ new/pyOpenSSL-0.9/test/test_ssl.py 2009-04-25 14:47:06.000000000 +0200
@@ -4,15 +4,36 @@
Unit tests for L{OpenSSL.SSL}.
"""
-from unittest import TestCase
+from sys import platform
from tempfile import mktemp
from socket import socket
+from os import makedirs, symlink
+from os.path import join
+
+try:
+ # Prefer Twisted's TestCase, since it supports things like skips.
+ from twisted.trial.unittest import TestCase
+except ImportError:
+ # Fall back to the stdlib TestCase though, since it kind of works.
+ from unittest import TestCase, main
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 +136,183 @@
# 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, 'Testing Root CA')
+
+ 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, 'c7adac82.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))
+ if platform == "darwin":
+ test_set_default_verify_paths.todo = (
+ "set_default_verify_paths appears not to work on OS X - a "
+ "problem with the supplied OpenSSL, perhaps?")
+
+
+ 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"
+
+
+if __name__ == '__main__':
+ main()
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyOpenSSL-0.8/version.py new/pyOpenSSL-0.9/version.py
--- old/pyOpenSSL-0.8/version.py 2008-10-19 18:01:32.000000000 +0200
+++ new/pyOpenSSL-0.9/version.py 2009-04-25 14:47:05.000000000 +0200
@@ -1,8 +1,8 @@
# Copyright (C) AB Strakt 2001-2004, All rights reserved
-# Copyright (C) Jean-Paul Calderone 2008, All rights reserved
+# Copyright (C) Jean-Paul Calderone 2008-2009, All rights reserved
"""
pyOpenSSL - A simple wrapper around the OpenSSL library
"""
-__version__ = '0.8'
+__version__ = '0.9'
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...
--
To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org
For additional commands, e-mail: opensuse-commit+help@opensuse.org