diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index c5ea78c1683761..26964348f5cd25 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -596,172 +596,6 @@ ABC hierarchy:: itself does not end in ``__init__``. -.. class:: ResourceReader - - *Superseded by TraversableResources* - - An :term:`abstract base class` to provide the ability to read - *resources*. - - From the perspective of this ABC, a *resource* is a binary - artifact that is shipped within a package. Typically this is - something like a data file that lives next to the ``__init__.py`` - file of the package. The purpose of this class is to help abstract - out the accessing of such data files so that it does not matter if - the package and its data file(s) are stored e.g. in a zip file - versus on the file system. - - For any of methods of this class, a *resource* argument is - expected to be a :term:`path-like object` which represents - conceptually just a file name. This means that no subdirectory - paths should be included in the *resource* argument. This is - because the location of the package the reader is for, acts as the - "directory". Hence the metaphor for directories and file - names is packages and resources, respectively. This is also why - instances of this class are expected to directly correlate to - a specific package (instead of potentially representing multiple - packages or a module). - - Loaders that wish to support resource reading are expected to - provide a method called ``get_resource_reader(fullname)`` which - returns an object implementing this ABC's interface. If the module - specified by fullname is not a package, this method should return - :const:`None`. An object compatible with this ABC should only be - returned when the specified module is a package. - - .. versionadded:: 3.7 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.TraversableResources` instead. - - .. method:: open_resource(resource) - :abstractmethod: - - Returns an opened, :term:`file-like object` for binary reading - of the *resource*. - - If the resource cannot be found, :exc:`FileNotFoundError` is - raised. - - .. method:: resource_path(resource) - :abstractmethod: - - Returns the file system path to the *resource*. - - If the resource does not concretely exist on the file system, - raise :exc:`FileNotFoundError`. - - .. method:: is_resource(name) - :abstractmethod: - - Returns ``True`` if the named *name* is considered a resource. - :exc:`FileNotFoundError` is raised if *name* does not exist. - - .. method:: contents() - :abstractmethod: - - Returns an :term:`iterable` of strings over the contents of - the package. Do note that it is not required that all names - returned by the iterator be actual resources, e.g. it is - acceptable to return names for which :meth:`is_resource` would - be false. - - Allowing non-resource names to be returned is to allow for - situations where how a package and its resources are stored - are known a priori and the non-resource names would be useful. - For instance, returning subdirectory names is allowed so that - when it is known that the package and resources are stored on - the file system then those subdirectory names can be used - directly. - - The abstract method returns an iterable of no items. - - -.. class:: Traversable - - An object with a subset of :class:`pathlib.Path` methods suitable for - traversing directories and opening files. - - For a representation of the object on the file-system, use - :meth:`importlib.resources.as_file`. - - .. versionadded:: 3.9 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.Traversable` instead. - - .. attribute:: name - - Abstract. The base name of this object without any parent references. - - .. method:: iterdir() - :abstractmethod: - - Yield ``Traversable`` objects in ``self``. - - .. method:: is_dir() - :abstractmethod: - - Return ``True`` if ``self`` is a directory. - - .. method:: is_file() - :abstractmethod: - - Return ``True`` if ``self`` is a file. - - .. method:: joinpath(child) - :abstractmethod: - - Return Traversable child in ``self``. - - .. method:: __truediv__(child) - :abstractmethod: - - Return ``Traversable`` child in ``self``. - - .. method:: open(mode='r', *args, **kwargs) - :abstractmethod: - - *mode* may be 'r' or 'rb' to open as text or binary. Return a handle - suitable for reading (same as :attr:`pathlib.Path.open`). - - When opening as text, accepts encoding parameters such as those - accepted by :class:`io.TextIOWrapper`. - - .. method:: read_bytes() - - Read contents of ``self`` as bytes. - - .. method:: read_text(encoding=None) - - Read contents of ``self`` as text. - - -.. class:: TraversableResources - - An abstract base class for resource readers capable of serving - the :meth:`importlib.resources.files` interface. Subclasses - :class:`importlib.resources.abc.ResourceReader` and provides - concrete implementations of the :class:`importlib.resources.abc.ResourceReader`'s - abstract methods. Therefore, any loader supplying - :class:`importlib.abc.TraversableResources` also supplies ResourceReader. - - Loaders that wish to support resource reading are expected to - implement this interface. - - .. versionadded:: 3.9 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.TraversableResources` instead. - - .. method:: files() - :abstractmethod: - - Returns a :class:`importlib.resources.abc.Traversable` object for the loaded - package. - - - :mod:`importlib.machinery` -- Importers and path hooks ------------------------------------------------------ diff --git a/Doc/library/msvcrt.rst b/Doc/library/msvcrt.rst index a2c5e375d2cc4f..80f3ae4ee3f5c1 100644 --- a/Doc/library/msvcrt.rst +++ b/Doc/library/msvcrt.rst @@ -7,6 +7,8 @@ .. sectionauthor:: Fred L. Drake, Jr. +**Source code:** :source:`PC/msvcrtmodule.c` + -------------- These functions provide access to some useful capabilities on Windows platforms. diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst index 89def6e2afe088..d167c41ab72c34 100644 --- a/Doc/library/winreg.rst +++ b/Doc/library/winreg.rst @@ -7,6 +7,8 @@ .. sectionauthor:: Mark Hammond +**Source code:** :source:`PC/winreg.c` + -------------- These functions expose the Windows registry API to Python. Instead of using an @@ -25,7 +27,7 @@ to explicitly close them. .. _functions: Functions ------------------- +--------- This module offers the following functions: @@ -554,7 +556,7 @@ This module offers the following functions: .. _constants: Constants ------------------- +--------- The following constants are defined for use in many :mod:`winreg` functions. diff --git a/Doc/library/winsound.rst b/Doc/library/winsound.rst index 93c0c025982076..755b94fc0fbe1e 100644 --- a/Doc/library/winsound.rst +++ b/Doc/library/winsound.rst @@ -8,6 +8,8 @@ .. moduleauthor:: Toby Dickenson .. sectionauthor:: Fred L. Drake, Jr. +**Source code:** :source:`PC/winsound.c` + -------------- The :mod:`winsound` module provides access to the basic sound-playing machinery @@ -16,6 +18,9 @@ provided by Windows platforms. It includes functions and several constants. .. availability:: Windows. +Functions +--------- + .. function:: Beep(frequency, duration) Beep the PC's speaker. The *frequency* parameter specifies frequency, in hertz, @@ -46,6 +51,9 @@ provided by Windows platforms. It includes functions and several constants. error, :exc:`RuntimeError` is raised. +Constants +--------- + .. data:: SND_FILENAME The *sound* parameter is the name of a WAV file. Do not use with diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 32ca965b7d0ed0..5dd47cdac96a5c 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -604,7 +604,7 @@ new ABC for access to, opening, and reading *resources* inside packages. Resources are roughly similar to files inside packages, but they needn't be actual files on the physical file system. Module loaders can provide a :meth:`!get_resource_reader` function which returns -a :class:`importlib.abc.ResourceReader` instance to support this +a :class:`!importlib.abc.ResourceReader` instance to support this new API. Built-in file path loaders and zip file loaders both support this. Contributed by Barry Warsaw and Brett Cannon in :issue:`32248`. @@ -1043,7 +1043,7 @@ window are shown and hidden in the Options menu. importlib --------- -The :class:`importlib.abc.ResourceReader` ABC was introduced to +The :class:`!importlib.abc.ResourceReader` ABC was introduced to support the loading of resources from packages. See also :ref:`whatsnew37_importlib_resources`. (Contributed by Barry Warsaw, Brett Cannon in :issue:`32248`.) @@ -2032,7 +2032,7 @@ both deprecated in Python 3.4 now emit :exc:`DeprecationWarning`. (Contributed by Matthias Bussonnier in :issue:`29576`.) The :class:`importlib.abc.ResourceLoader` ABC has been deprecated in -favour of :class:`importlib.abc.ResourceReader`. +favour of :class:`!importlib.abc.ResourceReader`. locale diff --git a/Misc/NEWS.d/3.7.0a4.rst b/Misc/NEWS.d/3.7.0a4.rst index 2ceb9e78e0421b..691ac0f4a8cec8 100644 --- a/Misc/NEWS.d/3.7.0a4.rst +++ b/Misc/NEWS.d/3.7.0a4.rst @@ -403,7 +403,7 @@ SOCK_CLOEXEC. .. nonce: zmO8G2 .. section: Library -Add :class:`importlib.abc.ResourceReader` as an ABC for loaders to provide a +Add :class:`!importlib.abc.ResourceReader` as an ABC for loaders to provide a unified API for reading resources contained within packages. Also add :mod:`importlib.resources` as the port of ``importlib_resources``. diff --git a/Misc/NEWS.d/3.7.0b1.rst b/Misc/NEWS.d/3.7.0b1.rst index c9786e55c20739..d785f6d8c4c800 100644 --- a/Misc/NEWS.d/3.7.0b1.rst +++ b/Misc/NEWS.d/3.7.0b1.rst @@ -598,7 +598,7 @@ Add socket.getblocking() method. .. nonce: zmO8G2 .. section: Library -Add :mod:`importlib.resources` and :class:`importlib.abc.ResourceReader` as +Add :mod:`importlib.resources` and :class:`!importlib.abc.ResourceReader` as the unified API for reading resources contained within packages. Loaders wishing to support resource reading must implement the :meth:`get_resource_reader` method. File-based and zipimport-based diff --git a/Misc/NEWS.d/3.7.0b4.rst b/Misc/NEWS.d/3.7.0b4.rst index 93627f54900ddd..789d96dffd666f 100644 --- a/Misc/NEWS.d/3.7.0b4.rst +++ b/Misc/NEWS.d/3.7.0b4.rst @@ -152,7 +152,7 @@ Ensure line-endings are respected when using lib2to3. .. section: Library Have :func:`importlib.resources.contents` and -:meth:`importlib.abc.ResourceReader.contents` return an :term:`iterable` +:meth:`!importlib.abc.ResourceReader.contents` return an :term:`iterable` instead of an :term:`iterator`. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 93995bc8feaad7..3a6966deb87825 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -5130,7 +5130,7 @@ Ensure line-endings are respected when using lib2to3. .. section: Library Have :func:`importlib.resources.contents` and -:meth:`importlib.abc.ResourceReader.contents` return an :term:`iterable` +:meth:`!importlib.abc.ResourceReader.contents` return an :term:`iterable` instead of an :term:`iterator`. .. diff --git a/Misc/NEWS.d/next/Library/2026-01-13-16-19-50.gh-issue-143756.LQOra1.rst b/Misc/NEWS.d/next/Library/2026-01-13-16-19-50.gh-issue-143756.LQOra1.rst new file mode 100644 index 00000000000000..fc7eefff8619ae --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-13-16-19-50.gh-issue-143756.LQOra1.rst @@ -0,0 +1 @@ +Fix potential thread safety issues in :mod:`ssl` module. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 2bcf864e759b91..22865bdfc3f727 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -43,14 +43,17 @@ /* Redefined below for Windows debug builds after important #includes */ #define _PySSL_FIX_ERRNO -#define PySSL_BEGIN_ALLOW_THREADS_S(save, mutex) \ - do { (save) = PyEval_SaveThread(); PyMutex_Lock(mutex); } while(0) -#define PySSL_END_ALLOW_THREADS_S(save, mutex) \ - do { PyMutex_Unlock(mutex); PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) +#define PySSL_BEGIN_ALLOW_THREADS_S(save) \ + do { (save) = PyEval_SaveThread(); } while(0) +#define PySSL_END_ALLOW_THREADS_S(save) \ + do { PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) #define PySSL_BEGIN_ALLOW_THREADS(self) { \ PyThreadState *_save = NULL; \ - PySSL_BEGIN_ALLOW_THREADS_S(_save, &self->tstate_mutex); -#define PySSL_END_ALLOW_THREADS(self) PySSL_END_ALLOW_THREADS_S(_save, &self->tstate_mutex); } + PySSL_BEGIN_ALLOW_THREADS_S(_save); \ + PyMutex_Lock(&(self)->tstate_mutex); +#define PySSL_END_ALLOW_THREADS(self) \ + PyMutex_Unlock(&(self)->tstate_mutex); \ + PySSL_END_ALLOW_THREADS_S(_save); } #if defined(HAVE_POLL_H) #include @@ -420,26 +423,6 @@ typedef enum { #define ERRSTR1(x,y,z) (x ":" y ": " z) #define ERRSTR(x) ERRSTR1("_ssl.c", Py_STRINGIFY(__LINE__), x) -// Get the socket from a PySSLSocket, if it has one. -// Return a borrowed reference. -static inline PySocketSockObject* GET_SOCKET(PySSLSocket *obj) { - if (obj->Socket) { - PyObject *sock; - if (PyWeakref_GetRef(obj->Socket, &sock)) { - // GET_SOCKET() returns a borrowed reference - Py_DECREF(sock); - } - else { - // dead weak reference - sock = Py_None; - } - return (PySocketSockObject *)sock; // borrowed reference - } - else { - return NULL; - } -} - /* If sock is NULL, use a timeout of 0 second */ #define GET_SOCKET_TIMEOUT(sock) \ ((sock != NULL) ? (sock)->sock_timeout : 0) @@ -791,6 +774,35 @@ _ssl_deprecated(const char* msg, int stacklevel) { #define PY_SSL_DEPRECATED(name, stacklevel, ret) \ if (_ssl_deprecated((name), (stacklevel)) == -1) return (ret) +// Get the socket from a PySSLSocket, if it has one. +// Stores a strong reference in out_sock. +static int +get_socket(PySSLSocket *obj, PySocketSockObject **out_sock, + const char *filename, int lineno) +{ + if (!obj->Socket) { + *out_sock = NULL; + return 0; + } + PySocketSockObject *sock; + int res = PyWeakref_GetRef(obj->Socket, (PyObject **)&sock); + if (res == 0 || sock->sock_fd == INVALID_SOCKET) { + _setSSLError(get_state_sock(obj), + "Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, filename, lineno); + *out_sock = NULL; + return -1; + } + if (sock != NULL) { + /* just in case the blocking state of the socket has been changed */ + int nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(obj->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(obj->ssl), nonblocking); + } + *out_sock = sock; + return res; +} + /* * SSL objects */ @@ -1018,24 +1030,13 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) int ret; _PySSLError err; PyObject *exc = NULL; - int sockstate, nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); + int sockstate; PyTime_t timeout, deadline = 0; int has_timeout; - if (sock) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2607,22 +2608,12 @@ _ssl__SSLSocket_sendfile_impl(PySSLSocket *self, int fd, Py_off_t offset, int sockstate; _PySSLError err; PyObject *exc = NULL; - PySocketSockObject *sock = GET_SOCKET(self); PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - if ((PyObject *)sock == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - /* just in case the blocking state of the socket has been changed */ - int nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2744,26 +2735,12 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) int sockstate; _PySSLError err; PyObject *exc = NULL; - int nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - } - - if (sock != NULL) { - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2893,8 +2870,6 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, int sockstate; _PySSLError err; PyObject *exc = NULL; - int nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); PyTime_t timeout, deadline = 0; int has_timeout; @@ -2903,14 +2878,9 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, return NULL; } - if (sock != NULL) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } if (!group_right_1) { @@ -2941,13 +2911,6 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, } } - if (sock != NULL) { - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); - } - timeout = GET_SOCKET_TIMEOUT(sock); has_timeout = (timeout > 0); if (has_timeout) @@ -3038,26 +3001,14 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) { _PySSLError err; PyObject *exc = NULL; - int sockstate, nonblocking, ret; + int sockstate, ret; int zeros = 0; - PySocketSockObject *sock = GET_SOCKET(self); PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - /* Guard against closed socket */ - if ((((PyObject*)sock) == Py_None) || (sock->sock_fd == INVALID_SOCKET)) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - - /* Just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -4543,60 +4494,25 @@ _password_callback(char *buf, int size, int rwflag, void *userdata) return -1; } -/*[clinic input] -@critical_section -_ssl._SSLContext.load_cert_chain - certfile: object - keyfile: object = None - password: object = None - -[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=6c7c5e8b73e4264b]*/ +load_cert_chain_lock_held(PySSLContext *self, _PySSLPasswordInfo *pw_info, + PyObject *certfile_bytes, PyObject *keyfile_bytes) { - PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; + int r; + PyObject *ret = NULL; + pem_password_cb *orig_passwd_cb = SSL_CTX_get_default_passwd_cb(self->ctx); void *orig_passwd_userdata = SSL_CTX_get_default_passwd_cb_userdata(self->ctx); - _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; - int r; - errno = 0; - ERR_clear_error(); - if (keyfile == Py_None) - keyfile = NULL; - if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "certfile should be a valid filesystem path"); - } - return NULL; - } - if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "keyfile should be a valid filesystem path"); - } - goto error; - } - if (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, &self->tstate_mutex); + 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 = SSL_CTX_use_certificate_chain_file(self->ctx, PyBytes_AS_STRING(certfile_bytes)); - PySSL_END_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { - if (pw_info.error) { + if (pw_info->error) { ERR_clear_error(); /* the password callback has already set the error information */ } @@ -4609,15 +4525,14 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, } goto error; } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex); + + 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), + PyBytes_AS_STRING(keyfile_bytes ? keyfile_bytes : certfile_bytes), SSL_FILETYPE_PEM); - PySSL_END_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex); - Py_CLEAR(keyfile_bytes); - Py_CLEAR(certfile_bytes); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { - if (pw_info.error) { + if (pw_info->error) { ERR_clear_error(); /* the password callback has already set the error information */ } @@ -4630,25 +4545,74 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, } goto error; } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex); + + 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, &self->tstate_mutex); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); goto 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); - Py_RETURN_NONE; - + ret = Py_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); + return ret; +} + +/*[clinic input] +_ssl._SSLContext.load_cert_chain + certfile: object + keyfile: object = None + password: object = None + +[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=30bc7e967ea01a58]*/ +{ + PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; + _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; + PyObject *ret = NULL; + + errno = 0; + ERR_clear_error(); + if (keyfile == Py_None) + keyfile = NULL; + if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "certfile should be a valid filesystem path"); + } + return NULL; + } + if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "keyfile should be a valid filesystem path"); + } + goto done; + } + if (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 done; + } + } + + PyMutex_Lock(&self->tstate_mutex); + ret = load_cert_chain_lock_held(self, &pw_info, certfile_bytes, keyfile_bytes); + PyMutex_Unlock(&self->tstate_mutex); + +done: PyMem_Free(pw_info.password); Py_XDECREF(keyfile_bytes); Py_XDECREF(certfile_bytes); - return NULL; + return ret; } /* internal helper function, returns -1 on error diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index d1fb024903e157..8c35c8443b775a 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -1829,9 +1829,7 @@ _ssl__SSLContext_load_cert_chain(PyObject *self, PyObject *const *args, Py_ssize } password = args[2]; skip_optional_pos: - Py_BEGIN_CRITICAL_SECTION(self); return_value = _ssl__SSLContext_load_cert_chain_impl((PySSLContext *)self, certfile, keyfile, password); - Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -3325,4 +3323,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=3b6c9cbfc4660ecb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e29d5ada294f97bb input=a9049054013a1b77]*/