diff -r fbf0e8dfe85e Doc/library/ssl.rst --- a/Doc/library/ssl.rst Sun Jul 24 14:41:08 2016 -0400 +++ b/Doc/library/ssl.rst Sat Aug 13 14:51:13 2016 +0200 @@ -1112,13 +1112,13 @@ .. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=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 + string must be the path to a single file or a file object containing the certificate as well as any number of CA certificates needed to establish - the certificate's authenticity. The *keyfile* string, if present, must - point to a file containing the private key in. Otherwise the private - 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*. + the certificate's authenticity, in the PEM format. The *keyfile* string, if + present, must be the path to a file or a file object containing the private + key in PEM format. Otherwise the private 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 *certfile*. 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 @@ -1129,6 +1129,10 @@ as the *password* argument. It will be ignored if the private key is not encrypted and no password is needed. + If *certfile* is provided as a file path, *keyfile* (if given) must be + provided as a file path as well (mixing file path and file object for these + arguments is not allowed). + If the *password* argument is not specified and a password is required, OpenSSL's built-in password prompting mechanism will be used to interactively prompt the user for a password. diff -r fbf0e8dfe85e Lib/ssl.py --- a/Lib/ssl.py Sun Jul 24 14:41:08 2016 -0400 +++ b/Lib/ssl.py Sat Aug 13 14:51:13 2016 +0200 @@ -92,6 +92,7 @@ import re import sys import os +import io from collections import namedtuple from enum import Enum as _Enum, IntEnum as _IntEnum @@ -426,6 +427,51 @@ self._load_windows_store_certs(storename, purpose) self.set_default_verify_paths() + def load_cert_chain(self, certfile, keyfile=None, password=None): + # If `certfile` is bytes or string, treat it as file path. + if isinstance(certfile, str) or isinstance(certfile, bytes): + certfile_path = certfile + + # If no `keyfile` is given, read private key from `certfile`. + if keyfile is None: + keyfile_path = certfile_path + else: + # If `certfile` is bytes or string, expect `keyfile` to be + # a bytes or string file path, too. + keyfile_path = keyfile + + # Pre CPython 3.6 behavior: let OpenSSL consume the files via + # SSL_CTX_use_certificate_chain_file(). + return self._load_cert_chain_pem_from_file_paths( + certfile_path, keyfile_path, password) + + # Expect `certfile` to be a file object, expect `keyfile` to be `None` + # or a file object. Read file(s) and prepare OpenSSL memory BIO + # objects. If file objects return text, expect it to be from the set + # of ASCII characters (expect PEM encoding). + + certdata = certfile.read() + if isinstance(certdata, str): + certdata = certdata.encode('ascii') + + if keyfile is not None: + keydata = keyfile.read() + if isinstance(keydata, str): + keydata = keydata.encode('ascii') + else: + # Expect that `certdata` contains the private key, too. + keydata = certdata + + certbio = MemoryBIO() + certbio.write(certdata) + certbio.write_eof() + + keybio = MemoryBIO() + keybio.write(keydata) + keybio.write_eof() + + return self._load_cert_chain_pem_from_bio(certbio, keybio, password) + def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, capath=None, cadata=None): diff -r fbf0e8dfe85e Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Sun Jul 24 14:41:08 2016 -0400 +++ b/Lib/test/test_ssl.py Sat Aug 13 14:51:13 2016 +0200 @@ -18,6 +18,7 @@ import weakref import platform import functools +from io import BytesIO, StringIO ssl = support.import_module("ssl") @@ -113,6 +114,24 @@ return cert_time +def bytebuf(filepath): + """ + Get re-read()able buffer object, with bytes read from `filepath`. + """ + with open(filepath, 'rb') as f: + buf = BytesIO(f.read()) + buf.read = buf.getvalue + return buf + +def textbuf(filepath): + """ + Get re-read()able buffer object, with bytes read from `filepath`. + """ + with open(filepath) as f: + buf = StringIO(f.read()) + buf.read = buf.getvalue + return buf + # Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2 def skip_if_broken_ubuntu_ssl(func): if hasattr(ssl, 'PROTOCOL_SSLv2'): @@ -885,53 +904,122 @@ with self.assertRaises(TypeError): ctx.verify_flags = None - def test_load_cert_chain(self): + def test_load_cert_chain_signature(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - # Combined key and cert in a single file - ctx.load_cert_chain(CERTFILE, keyfile=None) - ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE) - self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE) - with self.assertRaises(OSError) as cm: - ctx.load_cert_chain(NONEXISTINGCERT) - 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 + + # Test missing certfile argument. + with self.assertRaises(TypeError): + ctx.load_cert_chain(keyfile=ONLYKEY) + + # Test behavior if no file object or file path is given for certfile. + with self.assertRaisesRegex(AttributeError, "has no attribute 'read'"): + ctx.load_cert_chain(certfile=True) + + # Test behavior is certfile is valid (file obj), but keyfile is not. + with self.assertRaisesRegex(AttributeError, "has no attribute 'read'"): + ctx.load_cert_chain(certfile=textbuf(CERTFILE), keyfile=True) + + # Test behavior is certfile is valid (file path), but keyfile is not. + # If certfile is a path, then keyfile is also expected to be one. + with self.assertRaisesRegex( + TypeError, "keyfile should be a valid filesystem path"): + ctx.load_cert_chain(certfile=CERTFILE, keyfile=True) + + # Test behavior if keyfile is a valid object, but certfile is not. + with self.assertRaisesRegex(AttributeError, "has no attribute 'read'"): + ctx.load_cert_chain(certfile=True, keyfile=BYTES_ONLYKEY) + + # Test behavior for mixed str and bytes file paths. + ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=ONLYKEY) + ctx.load_cert_chain(certfile=ONLYCERT, keyfile=BYTES_ONLYKEY) + + def test_load_cert_chain_core_filepaths(self): + self._test_load_cert_chain_core(lambda x:x) + + def test_load_cert_chain_core_byte_buffers(self): + # Create byte buffers from file contents. + self._test_load_cert_chain_core(bytebuf) + + def test_load_cert_chain_core_text_buffers(self): + # Create text buffers from file contents. + self._test_load_cert_chain_core(textbuf) + + def _test_load_cert_chain_core(self, transform_func): + """ + This method is executed multiple times, with varying transformation + functions. + """ + + # Prepare `keyfile` and `certfile` argument values based off defaults, + # (all being file paths, either str or byte objects). Apply + # transformation function to all values before proceeding with the + # tests. + base = { + 'certfile': CERTFILE, + 'onlycert': ONLYCERT, + 'onlykey': ONLYKEY, + 'bytes_onlykey': BYTES_ONLYKEY, + 'bytes_onlycert': BYTES_ONLYCERT, + 'cafile_cacert': CAFILE_CACERT, + 'certfile_protected': CERTFILE_PROTECTED, + 'onlykey_protected': ONLYKEY_PROTECTED + } + + k = {key: transform_func(val) for key, val in base.items()} + 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 + + # Test combined key and cert in a single file. + ctx.load_cert_chain(k['certfile'], keyfile=None) + ctx.load_cert_chain(k['certfile'], keyfile=k['certfile']) + + # Test separate key and cert. ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - with self.assertRaisesRegex(ssl.SSLError, "key values mismatch"): - ctx.load_cert_chain(CAFILE_CACERT, 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())) + ctx.load_cert_chain(k['onlycert'], k['onlykey']) + ctx.load_cert_chain( + certfile=k['onlycert'], + keyfile=k['onlykey'] + ) + ctx.load_cert_chain( + certfile=k['bytes_onlycert'], + keyfile=k['bytes_onlykey'] + ) + + # Test password-protected key and cert in a single file. + ctx.load_cert_chain( + k['certfile_protected'], + password=KEY_PASSWORD + ) + ctx.load_cert_chain( + k['certfile_protected'], + password=KEY_PASSWORD.encode() + ) + ctx.load_cert_chain( + k['certfile_protected'], + password=bytearray(KEY_PASSWORD.encode()) + ) + + # Test password-protected key. + ctx.load_cert_chain( + k['onlycert'], + k['onlykey_protected'], + KEY_PASSWORD + ) + ctx.load_cert_chain( + k['onlycert'], + k['onlykey_protected'], + KEY_PASSWORD.encode() + ) + ctx.load_cert_chain( + k['onlycert'], + k['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 + ctx.load_cert_chain(k['certfile_protected'], password=True) + + # Test password callback. def getpass_unicode(): return KEY_PASSWORD def getpass_bytes(): @@ -951,22 +1039,95 @@ 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, + ctx.load_cert_chain(k['certfile_protected'], password=getpass_unicode) + ctx.load_cert_chain(k['certfile_protected'], password=getpass_bytes) + ctx.load_cert_chain(k['certfile_protected'], password=getpass_bytearray) + ctx.load_cert_chain(k['certfile_protected'], password=GetPassCallable()) + ctx.load_cert_chain(k['certfile_protected'], password=GetPassCallable().getpass) with self.assertRaises(ssl.SSLError): - ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_badpass) + ctx.load_cert_chain(k['certfile_protected'], password=getpass_badpass) with self.assertRaisesRegex(ValueError, "cannot be longer"): - ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_huge) + ctx.load_cert_chain(k['certfile_protected'], password=getpass_huge) with self.assertRaisesRegex(TypeError, "must return a string"): - ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bad_type) + ctx.load_cert_chain(k['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) + ctx.load_cert_chain(k['certfile_protected'], password=getpass_exception) + + # Make sure the password function isn't called if it isn't needed. + ctx.load_cert_chain(k['certfile'], password=getpass_exception) + + def test_load_cert_chain_byte_buffers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + + with self.assertRaisesRegex(ssl.SSLError, "PEM: BAD_BASE64_DECODE"): + ctx.load_cert_chain(bytebuf(BADCERT)) + + with self.assertRaisesRegex(ssl.SSLError, "PEM: NO_START_LINE"): + ctx.load_cert_chain(bytebuf(EMPTYCERT)) + + with self.assertRaisesRegex(ssl.SSLError, "Can't read private key"): + ctx.load_cert_chain(bytebuf(ONLYCERT)) + + with self.assertRaisesRegex(ssl.SSLError, "PEM: NO_START_LINE"): + ctx.load_cert_chain(bytebuf(ONLYKEY)) + + with self.assertRaisesRegex(ssl.SSLError, "PEM: NO_START_LINE"): + ctx.load_cert_chain( + certfile=bytebuf(ONLYKEY), + keyfile=bytebuf(ONLYCERT) + ) + + with self.assertRaisesRegex(ssl.SSLError, "Can't read private key"): + ctx.load_cert_chain(bytebuf(CERTFILE_PROTECTED), password="bad") + + with self.assertRaisesRegex(ssl.SSLError, "Can't read private key"): + ctx.load_cert_chain( + certfile=bytebuf(ONLYCERT), + keyfile=bytebuf(ONLYKEY_PROTECTED), + password="bad" + ) + + with self.assertRaisesRegex(ValueError, "cannot be longer"): + # OpenSSL has a limit on the password buffer. + ctx.load_cert_chain( + bytebuf(CERTFILE_PROTECTED), + password=b'a' * 102400 + ) + + # Mismatching key and cert. + with self.assertRaisesRegex(ssl.SSLError, "SSL: NO_CERTIFICATE_ASSIGNED"): + ctx.load_cert_chain(bytebuf(CAFILE_CACERT), bytebuf(ONLYKEY)) + + def test_load_cert_chain_filepaths(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + + with self.assertRaises(OSError) as cm: + ctx.load_cert_chain(NONEXISTINGCERT) + 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) + + 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_PROTECTED, password="badpass") + + with self.assertRaisesRegex(ValueError, "cannot be longer"): + # OpenSSL has a limit on the password buffer. + ctx.load_cert_chain(CERTFILE_PROTECTED, password=b'a' * 102400) + + # Mismatching key and cert. + with self.assertRaisesRegex(ssl.SSLError, "key values mismatch"): + ctx.load_cert_chain(CAFILE_CACERT, ONLYKEY) def test_load_verify_locations(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -1044,7 +1205,6 @@ with self.assertRaisesRegex(ssl.SSLError, "not enough data"): ctx.load_verify_locations(cadata=b"broken") - def test_load_dh_params(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.load_dh_params(DHFILE) @@ -2314,7 +2474,7 @@ sys.stdout.write("\n") server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - server_context.load_cert_chain(SIGNED_CERTFILE) + server_context.load_cert_chain(bytebuf(SIGNED_CERTFILE)) context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = ssl.CERT_REQUIRED diff -r fbf0e8dfe85e Modules/_ssl.c --- a/Modules/_ssl.c Sun Jul 24 14:41:08 2016 -0400 +++ b/Modules/_ssl.c Sat Aug 13 14:51:13 2016 +0200 @@ -2777,18 +2777,20 @@ return -1; } + /*[clinic input] -_ssl._SSLContext.load_cert_chain +_ssl._SSLContext._load_cert_chain_pem_from_file_paths certfile: object keyfile: object = NULL password: object = NULL - [clinic start generated code]*/ static PyObject * -_ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, - PyObject *keyfile, PyObject *password) -/*[clinic end generated code: output=9480bc1c380e2095 input=7cf9ac673cbee6fc]*/ +_ssl__SSLContext__load_cert_chain_pem_from_file_paths_impl(PySSLContext *self, + PyObject *certfile, + PyObject *keyfile, + PyObject *password) +/*[clinic end generated code: output=27abe7da8870568c input=c6527c523f56633a]*/ { PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; pem_password_cb *orig_passwd_cb = self->ctx->default_passwd_callback; @@ -2880,6 +2882,199 @@ return NULL; } + +static int +_openssl_use_certificate_chain_from_bio(SSL_CTX *ctx, BIO *bio) +{ + /* + This function resembles the SSL_CTX_use_certificate_chain_file() + implementation from OpenSSL 1.1.0. + + That is, a return value of 0 means that an error occurred. + */ + + int ret = 0; + int r = 0; + unsigned long err = 0; + X509 *usecert = NULL; + X509 *cacert = NULL; + pem_password_cb *pwcb; + void *pwcb_data; + + ERR_clear_error(); + + pwcb = ctx->default_passwd_callback; + pwcb_data = ctx->default_passwd_callback_userdata; + + usecert = PEM_read_bio_X509_AUX(bio, NULL, pwcb, pwcb_data); + if (usecert == NULL) + goto end; + + ret = SSL_CTX_use_certificate(ctx, usecert); + if (ERR_peek_error() != 0) { + ret = 0; + goto end; + } + + /* Certificate is set up, proceed reading chain certificates. */ + r = SSL_CTX_clear_chain_certs(ctx); + if (r == 0) { + ret = 0; + goto end; + } + + while (1) { + cacert = PEM_read_bio_X509(bio, NULL, pwcb, pwcb_data); + + if (cacert == NULL) + /* Error: BIO EOF or something critical */ + break; + + r = SSL_CTX_add0_chain_cert(ctx, cacert); + if (!r) { + X509_free(cacert); + ret = 0; + goto end; + } + } + + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM + && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) + /* BIO EOF, expected. */ + ERR_clear_error(); + else + /* A critical error. */ + ret = 0; + + end: + X509_free(usecert); + return ret; +} + + +/*[clinic input] +_ssl._SSLContext._load_cert_chain_pem_from_bio + certbio: object(subclass_of="&PySSLMemoryBIO_Type", type="PySSLMemoryBIO *") + keybio: object(subclass_of="&PySSLMemoryBIO_Type", type="PySSLMemoryBIO *") + password: object = NULL + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext__load_cert_chain_pem_from_bio_impl(PySSLContext *self, + PySSLMemoryBIO *certbio, + PySSLMemoryBIO *keybio, + PyObject *password) +/*[clinic end generated code: output=1fa9f45cbdeddf1c input=4ba4aff7f210d0fd]*/ +{ + /* + Note that _password_callback() starts by invoking + + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); + + end finishes with invoking + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); + + and all OpenSSL API calls that potentially call the password callback are + required to be wrapped in + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + [...] + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + */ + + EVP_PKEY *private_key = NULL; + 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 }; + int r; + + errno = 0; + ERR_clear_error(); + + if (password && password != Py_None) { + if (PyCallable_Check(password)) { + pw_info.callable = password; + } else if (!_pwinfo_set(&pw_info, password, + "password should be a string or callable")) { + goto error; + } + SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info); + } + + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + r = _openssl_use_certificate_chain_from_bio(self->ctx, certbio->bio); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + + if (r != 1) { + if (pw_info.error) { + ERR_clear_error(); + /* the password callback has already set the error information */ + } + else if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrno(PyExc_IOError); + } + else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + } + goto error; + } + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + private_key = PEM_read_bio_PrivateKey( + keybio->bio, + NULL, + self->ctx->default_passwd_callback, + self->ctx->default_passwd_callback_userdata + ); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + + if (private_key == NULL) { + if (pw_info.error) { + ERR_clear_error(); + /* the password callback has already set the error information */ + } + else if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrno(PyExc_IOError); + } + else { + _setSSLError("Can't read private key", 0, __FILE__, __LINE__); + } + goto error; + } + + r = SSL_CTX_use_PrivateKey(self->ctx, private_key); + + EVP_PKEY_free(private_key); + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + r = SSL_CTX_check_private_key(self->ctx); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + if (r != 1) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; + } + + /* Restore original password callback */ + SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); + PyMem_Free(pw_info.password); + Py_RETURN_NONE; + +error: + SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); + PyMem_Free(pw_info.password); + return NULL; +} + /* internal helper function, returns -1 on error */ static int @@ -3556,7 +3751,8 @@ _SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF _SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF - _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF + _SSL__SSLCONTEXT__LOAD_CERT_CHAIN_PEM_FROM_FILE_PATHS_METHODDEF + _SSL__SSLCONTEXT__LOAD_CERT_CHAIN_PEM_FROM_BIO_METHODDEF _SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF _SSL__SSLCONTEXT_LOAD_VERIFY_LOCATIONS_METHODDEF _SSL__SSLCONTEXT_SESSION_STATS_METHODDEF diff -r fbf0e8dfe85e Modules/clinic/_ssl.c.h --- a/Modules/clinic/_ssl.c.h Sun Jul 24 14:41:08 2016 -0400 +++ b/Modules/clinic/_ssl.c.h Sat Aug 13 14:51:13 2016 +0200 @@ -442,20 +442,23 @@ return return_value; } -PyDoc_STRVAR(_ssl__SSLContext_load_cert_chain__doc__, -"load_cert_chain($self, /, certfile, keyfile=None, password=None)\n" +PyDoc_STRVAR(_ssl__SSLContext__load_cert_chain_pem_from_file_paths__doc__, +"_load_cert_chain_pem_from_file_paths($self, /, certfile, keyfile=None,\n" +" password=None)\n" "--\n" "\n"); -#define _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF \ - {"load_cert_chain", (PyCFunction)_ssl__SSLContext_load_cert_chain, METH_VARARGS|METH_KEYWORDS, _ssl__SSLContext_load_cert_chain__doc__}, +#define _SSL__SSLCONTEXT__LOAD_CERT_CHAIN_PEM_FROM_FILE_PATHS_METHODDEF \ + {"_load_cert_chain_pem_from_file_paths", (PyCFunction)_ssl__SSLContext__load_cert_chain_pem_from_file_paths, METH_VARARGS|METH_KEYWORDS, _ssl__SSLContext__load_cert_chain_pem_from_file_paths__doc__}, static PyObject * -_ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, - PyObject *keyfile, PyObject *password); +_ssl__SSLContext__load_cert_chain_pem_from_file_paths_impl(PySSLContext *self, + PyObject *certfile, + PyObject *keyfile, + PyObject *password); static PyObject * -_ssl__SSLContext_load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwargs) +_ssl__SSLContext__load_cert_chain_pem_from_file_paths(PySSLContext *self, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; static char *_keywords[] = {"certfile", "keyfile", "password", NULL}; @@ -463,11 +466,44 @@ PyObject *keyfile = NULL; PyObject *password = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OO:load_cert_chain", _keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OO:_load_cert_chain_pem_from_file_paths", _keywords, &certfile, &keyfile, &password)) { goto exit; } - return_value = _ssl__SSLContext_load_cert_chain_impl(self, certfile, keyfile, password); + return_value = _ssl__SSLContext__load_cert_chain_pem_from_file_paths_impl(self, certfile, keyfile, password); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext__load_cert_chain_pem_from_bio__doc__, +"_load_cert_chain_pem_from_bio($self, /, certbio, keybio, password=None)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT__LOAD_CERT_CHAIN_PEM_FROM_BIO_METHODDEF \ + {"_load_cert_chain_pem_from_bio", (PyCFunction)_ssl__SSLContext__load_cert_chain_pem_from_bio, METH_VARARGS|METH_KEYWORDS, _ssl__SSLContext__load_cert_chain_pem_from_bio__doc__}, + +static PyObject * +_ssl__SSLContext__load_cert_chain_pem_from_bio_impl(PySSLContext *self, + PySSLMemoryBIO *certbio, + PySSLMemoryBIO *keybio, + PyObject *password); + +static PyObject * +_ssl__SSLContext__load_cert_chain_pem_from_bio(PySSLContext *self, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + static char *_keywords[] = {"certbio", "keybio", "password", NULL}; + PySSLMemoryBIO *certbio; + PySSLMemoryBIO *keybio; + PyObject *password = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!|O:_load_cert_chain_pem_from_bio", _keywords, + &PySSLMemoryBIO_Type, &certbio, &PySSLMemoryBIO_Type, &keybio, &password)) { + goto exit; + } + return_value = _ssl__SSLContext__load_cert_chain_pem_from_bio_impl(self, certbio, keybio, password); exit: return return_value; @@ -1135,4 +1171,4 @@ #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=02444732c19722b3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cb7513d6abfe2ad3 input=a9049054013a1b77]*/