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; +}