diff -r 2d79b169c632 Doc/library/ssl.rst
--- a/Doc/library/ssl.rst Fri Nov 16 15:02:09 2012 +0000
+++ b/Doc/library/ssl.rst Fri Nov 16 15:02:25 2012 +0000
@@ -147,6 +147,10 @@
contain a certificate to be used to identify the local side of the
connection. See the discussion of :ref:`ssl-certificates` for more
information on how the certificate is stored in the ``certfile``.
+ Both ``keyfile`` and ``certfile`` can alternatively be strings or bytes
+ objects containing the actual PEM encoded certificates. This is determined
+ by examining their contents and looking for the telltale "-----BEGIN"
+ signature.
The parameter ``server_side`` is a boolean which identifies whether
server-side or client-side behavior is desired from this socket.
@@ -164,6 +168,8 @@
the other end of the connection. See the discussion of
:ref:`ssl-certificates` for more information about how to arrange the
certificates in this file.
+ Alternatively, ``ca_certs`` can be a list of bytest objects, containing data
+ of the form that would be present in an actual file.
The parameter ``ssl_version`` specifies which version of the SSL protocol to
use. Typically, the server chooses a particular protocol version, and the
@@ -697,7 +703,8 @@
:class:`SSLContext` objects have the following methods and attributes:
-.. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
+.. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=None,
+ certdata=None, keydata=None)
Load a private key and the corresponding certificate. The *certfile*
string must be the path to a single file in PEM format containing the
@@ -707,6 +714,9 @@
key will be taken from *certfile* as well. See the discussion of
:ref:`ssl-certificates` for more information on how the certificate
is stored in the *certfile*.
+ *certdata* and/or *keydata* can be provided in stead of *certfile*
+ and *keyfile*. These are expected to be bytes objects containing
+ the PEM encoded data.
The *password* argument may be a function to call to get the password for
decrypting the private key. It will only be called if the private key is
@@ -727,7 +737,7 @@
.. versionchanged:: 3.3
New optional argument *password*.
-.. method:: SSLContext.load_verify_locations(cafile=None, capath=None)
+.. method:: SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)
Load a set of "certification authority" (CA) certificates used to validate
other peers' certificates when :data:`verify_mode` is other than
@@ -743,6 +753,11 @@
following an `OpenSSL specific layout
`_.
+ The *cadata* bytes object can be provided instead of both *cafile* and
+ *cadata*. This is expected to be a PEM encoded bytes object containing
+ CA certificates. This method can be called multiple times to add
+ additional certificates.
+
.. method:: SSLContext.set_default_verify_paths()
Load a set of default "certification authority" (CA) certificates from
diff -r 2d79b169c632 Lib/ssl.py
--- a/Lib/ssl.py Fri Nov 16 15:02:09 2012 +0000
+++ b/Lib/ssl.py Fri Nov 16 15:02:25 2012 +0000
@@ -233,7 +233,8 @@
family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
suppress_ragged_eofs=True, npn_protocols=None, ciphers=None,
server_hostname=None,
- _context=None):
+ _context=None,
+ ):
if _context:
self.context = _context
@@ -248,9 +249,22 @@
self.context = SSLContext(ssl_version)
self.context.verify_mode = cert_reqs
if ca_certs:
- self.context.load_verify_locations(ca_certs)
+ if isinstance(ca_certs, list):
+ for e in ca_certs:
+ self.context.load_verify_locations(cadata=e)
+ else:
+ self.context.load_verify_locations(ca_certs)
if certfile:
- self.context.load_cert_chain(certfile, keyfile)
+ args = {}
+ if certfile.startswith("-----BEGIN"):
+ args["certdata"] = certfile
+ else:
+ args["certfile"] = certfile
+ if keyfile.startswith("-----BEGIN"):
+ args["keydata"] = keyfile
+ else:
+ args["keyfile"] = keyfile
+ self.context.load_cert_chain(**args)
if npn_protocols:
self.context.set_npn_protocols(npn_protocols)
if ciphers:
diff -r 2d79b169c632 Lib/test/test_ssl.py
--- a/Lib/test/test_ssl.py Fri Nov 16 15:02:09 2012 +0000
+++ b/Lib/test/test_ssl.py Fri Nov 16 15:02:25 2012 +0000
@@ -437,106 +437,145 @@
ctx.verify_mode = 42
def test_load_cert_chain(self):
- ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- # Combined key and cert in a single file
- ctx.load_cert_chain(CERTFILE)
- ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE)
- self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE)
- with self.assertRaises(IOError) as cm:
- ctx.load_cert_chain(WRONGCERT)
- self.assertEqual(cm.exception.errno, errno.ENOENT)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_cert_chain(BADCERT)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_cert_chain(EMPTYCERT)
- # Separate key and cert
- ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- ctx.load_cert_chain(ONLYCERT, ONLYKEY)
- ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
- ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_cert_chain(ONLYCERT)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_cert_chain(ONLYKEY)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT)
- # Mismatching key and cert
- ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- with self.assertRaisesRegex(ssl.SSLError, "key values mismatch"):
- ctx.load_cert_chain(SVN_PYTHON_ORG_ROOT_CERT, ONLYKEY)
- # Password protected key and cert
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD)
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD.encode())
- ctx.load_cert_chain(CERTFILE_PROTECTED,
- password=bytearray(KEY_PASSWORD.encode()))
- ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD)
- ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD.encode())
- ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED,
- bytearray(KEY_PASSWORD.encode()))
- with self.assertRaisesRegex(TypeError, "should be a string"):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=True)
- with self.assertRaises(ssl.SSLError):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password="badpass")
- with self.assertRaisesRegex(ValueError, "cannot be longer"):
- # openssl has a fixed limit on the password buffer.
- # PEM_BUFSIZE is generally set to 1kb.
- # Return a string larger than this.
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=b'a' * 102400)
- # Password callback
- def getpass_unicode():
- return KEY_PASSWORD
- def getpass_bytes():
- return KEY_PASSWORD.encode()
- def getpass_bytearray():
- return bytearray(KEY_PASSWORD.encode())
- def getpass_badpass():
- return "badpass"
- def getpass_huge():
- return b'a' * (1024 * 1024)
- def getpass_bad_type():
- return 9
- def getpass_exception():
- raise Exception('getpass error')
- class GetPassCallable:
- def __call__(self):
+ #try both file and data mode for both arguments, do that by delegation
+ class CTX:
+ def __init__(self, ctx, mode):
+ self.ctx = ctx
+ self.mode = mode
+ def load_cert_chain(self, certfile, keyfile=None, password=None):
+ args = {"password" : password}
+ if (self.mode & 1) == 0:
+ args["certfile"] = certfile
+ else:
+ with open(certfile, "rb") as f:
+ args["certdata"] = f.read()
+ if keyfile:
+ if (self.mode & 2) == 0:
+ args["keyfile"] = keyfile
+ else:
+ with open(keyfile, "rb") as f:
+ args["keydata"] = f.read()
+ return self.ctx.load_cert_chain(**args)
+
+ for mode in range(4):
+ ctx = CTX(ssl.SSLContext(ssl.PROTOCOL_TLSv1), mode)
+
+ # Combined key and cert in a single file
+ ctx.load_cert_chain(CERTFILE)
+ ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE)
+ self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE)
+ with self.assertRaises(IOError) as cm:
+ ctx.load_cert_chain(WRONGCERT)
+ self.assertEqual(cm.exception.errno, errno.ENOENT)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_cert_chain(BADCERT)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_cert_chain(EMPTYCERT)
+ # Separate key and cert
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+ ctx.load_cert_chain(ONLYCERT, ONLYKEY)
+ ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
+ ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_cert_chain(ONLYCERT)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_cert_chain(ONLYKEY)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT)
+ # Mismatching key and cert
+ ctx = CTX(ssl.SSLContext(ssl.PROTOCOL_TLSv1), mode)
+ print(mode)
+ with self.assertRaisesRegex(ssl.SSLError, "key values mismatch"):
+ ctx.load_cert_chain(SVN_PYTHON_ORG_ROOT_CERT, ONLYKEY)
+ # Password protected key and cert
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD)
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD.encode())
+ ctx.load_cert_chain(CERTFILE_PROTECTED,
+ password=bytearray(KEY_PASSWORD.encode()))
+ ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD)
+ ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD.encode())
+ ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED,
+ bytearray(KEY_PASSWORD.encode()))
+ with self.assertRaisesRegex(TypeError, "should be a string"):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=True)
+ with self.assertRaises(ssl.SSLError):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password="badpass")
+ with self.assertRaisesRegex(ValueError, "cannot be longer"):
+ # openssl has a fixed limit on the password buffer.
+ # PEM_BUFSIZE is generally set to 1kb.
+ # Return a string larger than this.
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=b'a' * 102400)
+ # Password callback
+ def getpass_unicode():
return KEY_PASSWORD
- def getpass(self):
- return KEY_PASSWORD
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_unicode)
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytes)
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytearray)
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=GetPassCallable())
- ctx.load_cert_chain(CERTFILE_PROTECTED,
- password=GetPassCallable().getpass)
- with self.assertRaises(ssl.SSLError):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_badpass)
- with self.assertRaisesRegex(ValueError, "cannot be longer"):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_huge)
- with self.assertRaisesRegex(TypeError, "must return a string"):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bad_type)
- with self.assertRaisesRegex(Exception, "getpass error"):
- ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_exception)
- # Make sure the password function isn't called if it isn't needed
- ctx.load_cert_chain(CERTFILE, password=getpass_exception)
+ def getpass_bytes():
+ return KEY_PASSWORD.encode()
+ def getpass_bytearray():
+ return bytearray(KEY_PASSWORD.encode())
+ def getpass_badpass():
+ return "badpass"
+ def getpass_huge():
+ return b'a' * (1024 * 1024)
+ def getpass_bad_type():
+ return 9
+ def getpass_exception():
+ raise Exception('getpass error')
+ class GetPassCallable:
+ def __call__(self):
+ return KEY_PASSWORD
+ def getpass(self):
+ return KEY_PASSWORD
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_unicode)
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytes)
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytearray)
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=GetPassCallable())
+ ctx.load_cert_chain(CERTFILE_PROTECTED,
+ password=GetPassCallable().getpass)
+ with self.assertRaises(ssl.SSLError):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_badpass)
+ with self.assertRaisesRegex(ValueError, "cannot be longer"):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_huge)
+ with self.assertRaisesRegex(TypeError, "must return a string"):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bad_type)
+ with self.assertRaisesRegex(Exception, "getpass error"):
+ ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_exception)
+ # Make sure the password function isn't called if it isn't needed
+ ctx.load_cert_chain(CERTFILE, password=getpass_exception)
def test_load_verify_locations(self):
- ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- ctx.load_verify_locations(CERTFILE)
- ctx.load_verify_locations(cafile=CERTFILE, capath=None)
- ctx.load_verify_locations(BYTES_CERTFILE)
- ctx.load_verify_locations(cafile=BYTES_CERTFILE, capath=None)
- self.assertRaises(TypeError, ctx.load_verify_locations)
- self.assertRaises(TypeError, ctx.load_verify_locations, None, None)
- with self.assertRaises(IOError) as cm:
- ctx.load_verify_locations(WRONGCERT)
- self.assertEqual(cm.exception.errno, errno.ENOENT)
- with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
- ctx.load_verify_locations(BADCERT)
- ctx.load_verify_locations(CERTFILE, CAPATH)
- ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH)
+ #try both file and data mode for both arguments, do that by delegation
+ class CTX:
+ def __init__(self, ctx, mode):
+ self.ctx = ctx
+ self.mode = mode
+ def load_verify_locations(self, cafile, capath=None):
+ args = {}
+ if (self.mode) == 0 or capath != None:
+ args["cafile"] = cafile
+ args["capath"] = capath
+ else:
+ with open(cafile, "rb") as f:
+ args["cadata"] = f.read()
+ return self.ctx.load_verify_locations(**args)
- # Issue #10989: crash if the second argument type is invalid
- self.assertRaises(TypeError, ctx.load_verify_locations, None, True)
+ for mode in range(2):
+ ctx = CTX(ssl.SSLContext(ssl.PROTOCOL_TLSv1), mode)
+ ctx.load_verify_locations(CERTFILE)
+ ctx.load_verify_locations(cafile=CERTFILE, capath=None)
+ ctx.load_verify_locations(BYTES_CERTFILE)
+ ctx.load_verify_locations(cafile=BYTES_CERTFILE, capath=None)
+ self.assertRaises(TypeError, ctx.load_verify_locations)
+ self.assertRaises(TypeError, ctx.load_verify_locations, None, None)
+ with self.assertRaises(IOError) as cm:
+ ctx.load_verify_locations(WRONGCERT)
+ self.assertEqual(cm.exception.errno, errno.ENOENT)
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
+ ctx.load_verify_locations(BADCERT)
+ ctx.load_verify_locations(CERTFILE, CAPATH)
+ ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH)
+
+ # Issue #10989: crash if the second argument type is invalid
+ self.assertRaises(TypeError, ctx.load_verify_locations, None, True)
def test_load_dh_params(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
@@ -656,6 +695,17 @@
finally:
s.close()
+ def test_connect_listcert(self):
+ global SVN_PYTHON_ORG_ROOT_CERT
+ with open(SVN_PYTHON_ORG_ROOT_CERT, "rb") as f:
+ data = [f.read()]
+ old = SVN_PYTHON_ORG_ROOT_CERT
+ SVN_PYTHON_ORG_ROOT_CERT = data
+ try:
+ self.test_connect()
+ finally:
+ SVN_PYTHON_ORG_ROOT_CERT = old
+
def test_connect_ex(self):
# Issue #11326: check connect_ex() implementation
with support.transient_internet("svn.python.org"):
diff -r 2d79b169c632 Modules/_ssl.c
--- a/Modules/_ssl.c Fri Nov 16 15:02:09 2012 +0000
+++ b/Modules/_ssl.c Fri Nov 16 15:02:25 2012 +0000
@@ -220,6 +220,10 @@
#define ERRSTR1(x,y,z) (x ":" y ": " z)
#define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x)
+/* Forward declare some custom SSL functions */
+static int PySSL_CTX_use_PrivateKey_mem(SSL_CTX *ctx, void *data, int len);
+static int PySSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int len);
+static int PySSL_LoadVerifyCertsFromBuf(SSL_CTX *ctx, PyObject *obj);
/*
* SSL errors.
@@ -2010,9 +2014,11 @@
static PyObject *
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
{
- char *kwlist[] = {"certfile", "keyfile", "password", NULL};
- PyObject *certfile, *keyfile = NULL, *password = NULL;
+ char *kwlist[] = {"certfile", "keyfile", "password", "certdata", "keydata", NULL};
+ PyObject *certfile = NULL, *keyfile = NULL, *password = NULL;
PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL;
+ char *certdata = NULL, *keydata = NULL;
+ Py_ssize_t certlen, keylen;
pem_password_cb *orig_passwd_cb = self->ctx->default_passwd_callback;
void *orig_passwd_userdata = self->ctx->default_passwd_callback_userdata;
_PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 };
@@ -2021,12 +2027,21 @@
errno = 0;
ERR_clear_error();
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O|OO:load_cert_chain", kwlist,
- &certfile, &keyfile, &password))
+ "|OOOz#z#:load_cert_chain", kwlist,
+ &certfile, &keyfile, &password,
+ &certdata, &certlen, &keydata, &keylen))
return NULL;
if (keyfile == Py_None)
keyfile = NULL;
- if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) {
+ if ((!certfile ^ !certdata) == 0) {
+ PyErr_SetString(PyExc_ValueError, "one of certfile or certdata must be provided");
+ return NULL;
+ }
+ if (keyfile && keydata) {
+ PyErr_SetString(PyExc_ValueError, "at most one of keyfile or keydata can be provided");
+ return NULL;
+ }
+ if (certfile && !PyUnicode_FSConverter(certfile, &certfile_bytes)) {
PyErr_SetString(PyExc_TypeError,
"certfile should be a valid filesystem path");
return NULL;
@@ -2047,8 +2062,13 @@
SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info);
}
PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
- r = SSL_CTX_use_certificate_chain_file(self->ctx,
- PyBytes_AS_STRING(certfile_bytes));
+ if (certdata) {
+ r = PySSL_CTX_use_certificate_chain_mem(self->ctx, certdata, certlen);
+ }
+ else {
+ r = SSL_CTX_use_certificate_chain_file(self->ctx,
+ PyBytes_AS_STRING(certfile_bytes));
+ }
PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
if (r != 1) {
if (pw_info.error) {
@@ -2065,9 +2085,19 @@
goto error;
}
PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state);
- r = SSL_CTX_use_PrivateKey_file(self->ctx,
- PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
- SSL_FILETYPE_PEM);
+ if (keydata || (certdata && !keyfile)) {
+ if (keydata)
+ r = PySSL_CTX_use_PrivateKey_mem(self->ctx,
+ (void*)keydata, (int)keylen);
+ else
+ r = PySSL_CTX_use_PrivateKey_mem(self->ctx,
+ (void*)certdata, (int)certlen);
+ }
+ else {
+ r = SSL_CTX_use_PrivateKey_file(self->ctx,
+ PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
+ SSL_FILETYPE_PEM);
+ }
PySSL_END_ALLOW_THREADS_S(pw_info.thread_state);
Py_CLEAR(keyfile_bytes);
Py_CLEAR(certfile_bytes);
@@ -2109,24 +2139,27 @@
static PyObject *
load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
{
- char *kwlist[] = {"cafile", "capath", NULL};
+ char *kwlist[] = {"cafile", "capath", "cadata", NULL};
PyObject *cafile = NULL, *capath = NULL;
PyObject *cafile_bytes = NULL, *capath_bytes = NULL;
const char *cafile_buf = NULL, *capath_buf = NULL;
+ PyObject *cadata = NULL;
int r;
errno = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "|OO:load_verify_locations", kwlist,
- &cafile, &capath))
+ "|OOO:load_verify_locations", kwlist,
+ &cafile, &capath, &cadata))
return NULL;
if (cafile == Py_None)
cafile = NULL;
if (capath == Py_None)
capath = NULL;
- if (cafile == NULL && capath == NULL) {
+ if (cadata == Py_None)
+ cadata = NULL;
+ if (cafile == NULL && capath == NULL && cadata == NULL) {
PyErr_SetString(PyExc_TypeError,
- "cafile and capath cannot be both omitted");
+ "cafile, capath and cadata cannot all be omitted");
return NULL;
}
if (cafile && !PyUnicode_FSConverter(cafile, &cafile_bytes)) {
@@ -2144,18 +2177,24 @@
cafile_buf = PyBytes_AS_STRING(cafile_bytes);
if (capath)
capath_buf = PyBytes_AS_STRING(capath_bytes);
- PySSL_BEGIN_ALLOW_THREADS
- r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
- PySSL_END_ALLOW_THREADS
+ if (!cadata) {
+ PySSL_BEGIN_ALLOW_THREADS
+ r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
+ PySSL_END_ALLOW_THREADS
+ }
+ else
+ r = PySSL_LoadVerifyCertsFromBuf(self->ctx, cadata) == 0;
Py_XDECREF(cafile_bytes);
Py_XDECREF(capath_bytes);
if (r != 1) {
- if (errno != 0) {
- ERR_clear_error();
- PyErr_SetFromErrno(PyExc_IOError);
- }
- else {
- _setSSLError(NULL, 0, __FILE__, __LINE__);
+ if (!cadata) {
+ if (errno != 0) {
+ ERR_clear_error();
+ PyErr_SetFromErrno(PyExc_IOError);
+ }
+ else {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ }
}
return NULL;
}
@@ -2874,3 +2913,171 @@
return m;
}
+
+/* Custom additions to read certificates and keyfiles from memory rather than files */
+/* this one based on SSL_CTX_use_PrivateKey_file */
+static int
+PySSL_CTX_use_PrivateKey_mem(SSL_CTX *ctx, void *data, int len)
+{
+ int ret=0;
+ BIO *in;
+ EVP_PKEY *pkey=NULL;
+
+ in=BIO_new_mem_buf(data,len);
+ if (in == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ pkey=PEM_read_bio_PrivateKey(in,NULL,
+ ctx->default_passwd_callback,ctx->default_passwd_callback_userdata);
+ if (pkey == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE,ERR_R_PEM_LIB);
+ goto end;
+ }
+ ret=SSL_CTX_use_PrivateKey(ctx,pkey);
+ EVP_PKEY_free(pkey);
+end:
+ if (in != NULL) BIO_free(in);
+ return(ret);
+}
+
+static int
+PySSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int len)
+ {
+ BIO *in;
+ int ret=0;
+ X509 *x=NULL;
+
+ ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */
+
+ in=BIO_new_mem_buf(data,len);
+ if (in == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE,ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ x=PEM_read_bio_X509(in,NULL,ctx->default_passwd_callback,ctx->default_passwd_callback_userdata);
+ if (x == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE,ERR_R_PEM_LIB);
+ goto end;
+ }
+
+ ret=SSL_CTX_use_certificate(ctx,x);
+ if (ERR_peek_error() != 0)
+ ret = 0; /* Key/certificate mismatch doesn't imply ret==0 ... */
+ if (ret)
+ {
+ /* If we could set up our certificate, now proceed to
+ * the CA certificates.
+ */
+ X509 *ca;
+ int r;
+ unsigned long err;
+
+ if (ctx->extra_certs != NULL)
+ {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+
+ while ((ca = PEM_read_bio_X509(in,NULL,ctx->default_passwd_callback,ctx->default_passwd_callback_userdata))
+ != NULL)
+ {
+ r = SSL_CTX_add_extra_chain_cert(ctx, ca);
+ if (!r)
+ {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ /* Note that we must not free r if it was successfully
+ * added to the chain (while we must free the main
+ * certificate, since its reference count is increased
+ * by SSL_CTX_use_certificate). */
+ }
+ /* When the while loop ends, it's usually just EOF. */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
+ ERR_clear_error();
+ else
+ ret = 0; /* some real error */
+ }
+
+end:
+ if (x != NULL) X509_free(x);
+ if (in != NULL) BIO_free(in);
+ return(ret);
+}
+
+/* this one is custom, based on info from the net */
+static int
+PySSL_CTX_load_verify_certs_mem(SSL_CTX *ctx, void *data, int len)
+{
+ BIO *in;
+ int ret=1, err;
+ X509 *ca=NULL;
+
+ in=BIO_new_mem_buf(data,len);
+ if (in == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE,ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ while ((ca = PEM_read_bio_X509(in,NULL,ctx->default_passwd_callback,ctx->default_passwd_callback_userdata))
+ != NULL)
+ {
+ ret = X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), ca);
+ X509_free(ca);
+ if (!ret) {
+ /* allow a certificate to be specified multiple times */
+ err = ERR_peek_last_error();
+ if (ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ ERR_clear_error();
+ continue;
+ }
+ goto end;
+ }
+ }
+ /* When the while loop ends, it's usually just EOF. */
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
+ ERR_clear_error();
+ ret = 1;
+ } else {
+ /* compatibility with the "file" reading function which overwrites
+ * the true error with a generic one
+ */
+ if (ERR_GET_REASON(err) == PEM_R_BAD_BASE64_DECODE)
+ X509err(X509_F_X509_LOAD_CERT_FILE,
+ ERR_R_PEM_LIB);
+ ret = 0; /* some real error */
+ }
+
+end:
+ if (in != NULL) BIO_free(in);
+ return(ret);
+}
+
+static int
+PySSL_LoadVerifyCertsFromBuf(SSL_CTX *ctx, PyObject *obj)
+{
+ Py_buffer buf;
+ int ret;
+ if (PyObject_GetBuffer(obj, &buf, PyBUF_SIMPLE))
+ return -1;
+ PySSL_BEGIN_ALLOW_THREADS
+ ret = PySSL_CTX_load_verify_certs_mem(ctx, buf.buf, buf.len);
+ PySSL_END_ALLOW_THREADS
+ PyBuffer_Release(&buf);
+ if (ret != 1) {
+ _setSSLError(NULL, 0, __FILE__, __LINE__);
+ return -1;
+ }
+ return 0;
+}
diff -r 2d79b169c632 PCbuild/build_ssl.py
--- a/PCbuild/build_ssl.py Fri Nov 16 15:02:09 2012 +0000
+++ b/PCbuild/build_ssl.py Fri Nov 16 15:02:25 2012 +0000
@@ -67,8 +67,10 @@
def get_ssl_dir():
propfile = (os.path.join(os.path.dirname(__file__), 'pyproject.props'))
with open(propfile) as f:
- m = re.search('openssl-([^<]+)<', f.read())
- return "..\..\openssl-"+m.group(1)
+ props = f.read()
+ externals = re.search(r"(.+)", props).group(1)
+ ssl = re.search('openssl-([^<]+)<', props).group(1)
+ return os.path.join(externals, r"openssl-"+ssl)
def create_makefile64(makefile, m32):
diff -r 2d79b169c632 PCbuild/pyproject.props
--- a/PCbuild/pyproject.props Fri Nov 16 15:02:09 2012 +0000
+++ b/PCbuild/pyproject.props Fri Nov 16 15:02:25 2012 +0000
@@ -5,7 +5,7 @@
$(SolutionDir)
$(SolutionDir)$(PlatformName)-temp-$(Configuration)\$(ProjectName)\
false
-
+
<_ProjectFileVersion>10.0.30319.1
<_PropertySheetDisplayName>amd64
@@ -16,7 +16,7 @@
python34$(PyDebugExt)
$(OutDir)python$(PyDebugExt).exe
$(OutDir)kill_python$(PyDebugExt).exe
- ..\..
+ ..\..\..\external
$(externalsDir)\sqlite-3.7.12
$(externalsDir)\bzip2-1.0.6
$(externalsDir)\xz-5.0.3
@@ -102,4 +102,4 @@
$(tcltk64LibDebug)
-
+
\ No newline at end of file