From 7756277882e2dddde53df604945d02c74f477f38 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Wed, 29 Sep 2021 03:07:23 +0530 Subject: [PATCH] Workaround for bug in `ssl.SSLContext.load_default_certs` (#1118) * Remove old compat code * Load certificates only when not using nocheckcertificate * Load each certificate individually Closes #1060 Related bugs.python.org/issue35665, bugs.python.org/issue4531 --- yt_dlp/utils.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py index eba89fb8b..4aa36a116 100644 --- a/yt_dlp/utils.py +++ b/yt_dlp/utils.py @@ -2352,29 +2352,35 @@ def formatSeconds(secs, delim=':', msec=False): return '%s.%03d' % (ret, secs % 1) if msec else ret -def make_HTTPS_handler(params, **kwargs): - opts_no_check_certificate = params.get('nocheckcertificate', False) - if hasattr(ssl, 'create_default_context'): # Python >= 3.4 or 2.7.9 - context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) - if opts_no_check_certificate: - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE +def _ssl_load_windows_store_certs(ssl_context, storename): + # Code adapted from _load_windows_store_certs in https://github.com/python/cpython/blob/main/Lib/ssl.py + try: + certs = [cert for cert, encoding, trust in ssl.enum_certificates(storename) + if encoding == 'x509_asn' and ( + trust is True or ssl.Purpose.SERVER_AUTH.oid in trust)] + except PermissionError: + return + for cert in certs: try: - return YoutubeDLHTTPSHandler(params, context=context, **kwargs) - except TypeError: - # Python 2.7.8 - # (create_default_context present but HTTPSHandler has no context=) + ssl_context.load_verify_locations(cadata=cert) + except ssl.SSLError: pass - if sys.version_info < (3, 2): - return YoutubeDLHTTPSHandler(params, **kwargs) - else: # Python < 3.4 - context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - context.verify_mode = (ssl.CERT_NONE - if opts_no_check_certificate - else ssl.CERT_REQUIRED) + +def make_HTTPS_handler(params, **kwargs): + opts_check_certificate = not params.get('nocheckcertificate') + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = opts_check_certificate + context.verify_mode = ssl.CERT_REQUIRED if opts_check_certificate else ssl.CERT_NONE + if opts_check_certificate: + # Work around the issue in load_default_certs when there are bad certificates. See: + # https://github.com/yt-dlp/yt-dlp/issues/1060, + # https://bugs.python.org/issue35665, https://bugs.python.org/issue4531 + if sys.platform == 'win32': + for storename in ('CA', 'ROOT'): + _ssl_load_windows_store_certs(context, storename) context.set_default_verify_paths() - return YoutubeDLHTTPSHandler(params, context=context, **kwargs) + return YoutubeDLHTTPSHandler(params, context=context, **kwargs) def bug_reports_message(before=';'):