comparison mercurial/sslutil.py @ 44939:7dd63a8cb1ee

sslutil: eliminate `_canloaddefaultcerts` by constant-folding code using it
author Manuel Jacob <me@manueljacob.de>
date Sat, 30 May 2020 03:23:58 +0200
parents 035199ba04ee
children 95903a8d8c97
comparison
equal deleted inserted replaced
44938:ab5348bbc55e 44939:7dd63a8cb1ee
49 supportedprotocols = {b'tls1.0'} 49 supportedprotocols = {b'tls1.0'}
50 if util.safehasattr(ssl, b'PROTOCOL_TLSv1_1'): 50 if util.safehasattr(ssl, b'PROTOCOL_TLSv1_1'):
51 supportedprotocols.add(b'tls1.1') 51 supportedprotocols.add(b'tls1.1')
52 if util.safehasattr(ssl, b'PROTOCOL_TLSv1_2'): 52 if util.safehasattr(ssl, b'PROTOCOL_TLSv1_2'):
53 supportedprotocols.add(b'tls1.2') 53 supportedprotocols.add(b'tls1.2')
54
55 _canloaddefaultcerts = True
56 54
57 55
58 def _hostsettings(ui, hostname): 56 def _hostsettings(ui, hostname):
59 """Obtain security settings for a hostname. 57 """Obtain security settings for a hostname.
60 58
225 223
226 s[b'cafile'] = cafile 224 s[b'cafile'] = cafile
227 225
228 # Require certificate validation if CA certs are being loaded and 226 # Require certificate validation if CA certs are being loaded and
229 # verification hasn't been disabled above. 227 # verification hasn't been disabled above.
230 if cafile or (_canloaddefaultcerts and s[b'allowloaddefaultcerts']): 228 if cafile or s[b'allowloaddefaultcerts']:
231 s[b'verifymode'] = ssl.CERT_REQUIRED 229 s[b'verifymode'] = ssl.CERT_REQUIRED
232 else: 230 else:
233 # At this point we don't have a fingerprint, aren't being 231 # At this point we don't have a fingerprint, aren't being
234 # explicitly insecure, and can't load CA certs. Connecting 232 # explicitly insecure, and can't load CA certs. Connecting
235 # is insecure. We allow the connection and abort during 233 # is insecure. We allow the connection and abort during
719 return exe.startswith(b'/usr/bin/python') or exe.startswith( 717 return exe.startswith(b'/usr/bin/python') or exe.startswith(
720 b'/system/library/frameworks/python.framework/' 718 b'/system/library/frameworks/python.framework/'
721 ) 719 )
722 720
723 721
724 _systemcacertpaths = [
725 # RHEL, CentOS, and Fedora
726 b'/etc/pki/tls/certs/ca-bundle.trust.crt',
727 # Debian, Ubuntu, Gentoo
728 b'/etc/ssl/certs/ca-certificates.crt',
729 ]
730
731
732 def _defaultcacerts(ui): 722 def _defaultcacerts(ui):
733 """return path to default CA certificates or None. 723 """return path to default CA certificates or None.
734 724
735 It is assumed this function is called when the returned certificates 725 It is assumed this function is called when the returned certificates
736 file will actually be used to validate connections. Therefore this 726 file will actually be used to validate connections. Therefore this
748 if os.path.exists(certs): 738 if os.path.exists(certs):
749 ui.debug(b'using ca certificates from certifi\n') 739 ui.debug(b'using ca certificates from certifi\n')
750 return pycompat.fsencode(certs) 740 return pycompat.fsencode(certs)
751 except (ImportError, AttributeError): 741 except (ImportError, AttributeError):
752 pass 742 pass
753
754 # On Windows, only the modern ssl module is capable of loading the system
755 # CA certificates. If we're not capable of doing that, emit a warning
756 # because we'll get a certificate verification error later and the lack
757 # of loaded CA certificates will be the reason why.
758 # Assertion: this code is only called if certificates are being verified.
759 if pycompat.iswindows:
760 if not _canloaddefaultcerts:
761 ui.warn(
762 _(
763 b'(unable to load Windows CA certificates; see '
764 b'https://mercurial-scm.org/wiki/SecureConnections for '
765 b'how to configure Mercurial to avoid this message)\n'
766 )
767 )
768
769 return None
770 743
771 # Apple's OpenSSL has patches that allow a specially constructed certificate 744 # Apple's OpenSSL has patches that allow a specially constructed certificate
772 # to load the system CA store. If we're running on Apple Python, use this 745 # to load the system CA store. If we're running on Apple Python, use this
773 # trick. 746 # trick.
774 if _plainapplepython(): 747 if _plainapplepython():
775 dummycert = os.path.join( 748 dummycert = os.path.join(
776 os.path.dirname(pycompat.fsencode(__file__)), b'dummycert.pem' 749 os.path.dirname(pycompat.fsencode(__file__)), b'dummycert.pem'
777 ) 750 )
778 if os.path.exists(dummycert): 751 if os.path.exists(dummycert):
779 return dummycert 752 return dummycert
780
781 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
782 # load system certs, we're out of luck.
783 if pycompat.isdarwin:
784 # FUTURE Consider looking for Homebrew or MacPorts installed certs
785 # files. Also consider exporting the keychain certs to a file during
786 # Mercurial install.
787 if not _canloaddefaultcerts:
788 ui.warn(
789 _(
790 b'(unable to load CA certificates; see '
791 b'https://mercurial-scm.org/wiki/SecureConnections for '
792 b'how to configure Mercurial to avoid this message)\n'
793 )
794 )
795 return None
796
797 # / is writable on Windows. Out of an abundance of caution make sure
798 # we're not on Windows because paths from _systemcacerts could be installed
799 # by non-admin users.
800 assert not pycompat.iswindows
801
802 # Try to find CA certificates in well-known locations. We print a warning
803 # when using a found file because we don't want too much silent magic
804 # for security settings. The expectation is that proper Mercurial
805 # installs will have the CA certs path defined at install time and the
806 # installer/packager will make an appropriate decision on the user's
807 # behalf. We only get here and perform this setting as a feature of
808 # last resort.
809 if not _canloaddefaultcerts:
810 for path in _systemcacertpaths:
811 if os.path.isfile(path):
812 ui.warn(
813 _(
814 b'(using CA certificates from %s; if you see this '
815 b'message, your Mercurial install is not properly '
816 b'configured; see '
817 b'https://mercurial-scm.org/wiki/SecureConnections '
818 b'for how to configure Mercurial to avoid this '
819 b'message)\n'
820 )
821 % path
822 )
823 return path
824
825 ui.warn(
826 _(
827 b'(unable to load CA certificates; see '
828 b'https://mercurial-scm.org/wiki/SecureConnections for '
829 b'how to configure Mercurial to avoid this message)\n'
830 )
831 )
832 753
833 return None 754 return None
834 755
835 756
836 def validatesocket(sock): 757 def validatesocket(sock):