##// END OF EJS Templates
ssl: resolve symlink before checking for Apple python executable (issue4588)...
ssl: resolve symlink before checking for Apple python executable (issue4588) test-https.t was broken at 07fafcd4bc74 if /usr/bin/pythonX.Y is used on Mac OS X. If python executable is not named as "python", run-tests.py creates a symlink and hghave uses it. On the other hand, the installed hg executable knows the real path to the system Python. Therefore, there was an inconsistency that hghave said it was not an Apple python but hg knew it was.

File last commit:

r24614:241d98d8 default
r24614:241d98d8 default
Show More
sslutil.py
231 lines | 9.7 KiB | text/x-python | PythonLexer
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 # sslutil.py - SSL handling for mercurial
#
# Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
# Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Mads Kiilerich
ssl: on OS X, use a dummy cert to trick Python/OpenSSL to use system CA certs...
r22575 import os, sys
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204
from mercurial import util
from mercurial.i18n import _
Yuya Nishihara
ssl: load CA certificates from system's store by default on Python 2.7.9...
r24291
_canloaddefaultcerts = False
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 try:
# avoid using deprecated/broken FakeSocket in python 2.6
import ssl
CERT_REQUIRED = ssl.CERT_REQUIRED
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834 try:
ssl_context = ssl.SSLContext
Yuya Nishihara
ssl: load CA certificates from system's store by default on Python 2.7.9...
r24291 _canloaddefaultcerts = util.safehasattr(ssl_context,
'load_default_certs')
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834
Augie Fackler
sslutil: drop support for clients of sslutil specifying a TLS version...
r23849 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE,
ca_certs=None, serverhostname=None):
Augie Fackler
sslutil: use saner TLS settings on Python 2.7.9...
r23850 # Allow any version of SSL starting with TLSv1 and
# up. Note that specifying TLSv1 here prohibits use of
# newer standards (like TLSv1_2), so this is the right way
# to do this. Note that in the future it'd be better to
# support using ssl.create_default_context(), which sets
# up a bunch of things in smart ways (strong ciphers,
# protocol versions, etc) and is upgraded by Python
# maintainers for us, but that breaks too many things to
# do it in a hurry.
sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834 if certfile is not None:
sslcontext.load_cert_chain(certfile, keyfile)
sslcontext.verify_mode = cert_reqs
if ca_certs is not None:
sslcontext.load_verify_locations(cafile=ca_certs)
Yuya Nishihara
ssl: load CA certificates from system's store by default on Python 2.7.9...
r24291 elif _canloaddefaultcerts:
sslcontext.load_default_certs()
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834
sslsocket = sslcontext.wrap_socket(sock,
server_hostname=serverhostname)
# check if wrap_socket failed silently because socket had been
# closed
# - see http://bugs.python.org/issue13721
if not sslsocket.cipher():
raise util.Abort(_('ssl connection failed'))
return sslsocket
except AttributeError:
Augie Fackler
sslutil: drop support for clients of sslutil specifying a TLS version...
r23849 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE,
ca_certs=None, serverhostname=None):
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834 sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
cert_reqs=cert_reqs, ca_certs=ca_certs,
Augie Fackler
sslutil: drop defunct ssl version constants...
r23851 ssl_version=ssl.PROTOCOL_TLSv1)
Alex Orange
https: support tls sni (server name indication) for https urls (issue3090)...
r23834 # check if wrap_socket failed silently because socket had been
# closed
# - see http://bugs.python.org/issue13721
if not sslsocket.cipher():
raise util.Abort(_('ssl connection failed'))
return sslsocket
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 except ImportError:
CERT_REQUIRED = 2
Stephen Thorne
sslutil: Restore missing imports of socket and httplib to sslutil...
r14616 import socket, httplib
Augie Fackler
sslutil: drop support for clients of sslutil specifying a TLS version...
r23849 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=CERT_REQUIRED,
ca_certs=None, serverhostname=None):
Mads Kiilerich
sslutil: abort when ssl module is needed but not found...
r15160 if not util.safehasattr(socket, 'ssl'):
raise util.Abort(_('Python SSL support not found'))
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 if ca_certs:
raise util.Abort(_(
'certificate checking requires Python 2.6'))
Augie Fackler
sslutil: make keyfile and certfile arguments consistent between 2.6+ and 2.5-
r19808 ssl = socket.ssl(sock, keyfile, certfile)
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 return httplib.FakeSocket(sock, ssl)
def _verifycert(cert, hostname):
'''Verify that cert (in socket.getpeercert() format) matches hostname.
CRLs is not handled.
Returns error message if any problems are found and None on success.
'''
if not cert:
return _('no certificate received')
dnsname = hostname.lower()
def matchdnsname(certname):
return (certname == dnsname or
'.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
san = cert.get('subjectAltName', [])
if san:
certnames = [value.lower() for key, value in san if key == 'DNS']
for name in certnames:
if matchdnsname(name):
return None
Nicolas Bareil
sslutil: fall back to commonName when no dNSName in subjectAltName (issue2798)...
r14666 if certnames:
return _('certificate is for %s') % ', '.join(certnames)
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204
# subject is only checked when subjectAltName is empty
for s in cert.get('subject', []):
key, value = s[0]
if key == 'commonName':
try:
# 'subject' entries are unicode
certname = value.lower().encode('ascii')
except UnicodeEncodeError:
return _('IDN in certificate not supported')
if matchdnsname(certname):
return None
return _('certificate is for %s') % certname
return _('no commonName or subjectAltName found in certificate')
# CERT_REQUIRED means fetch the cert from the server all the time AND
# validate it against the CA store provided in web.cacerts.
#
# We COMPLETELY ignore CERT_REQUIRED on Python <= 2.5, as it's totally
# busted on those versions.
Mads Kiilerich
ssl: only use the dummy cert hack if using an Apple Python (issue4410)...
r23042 def _plainapplepython():
"""return true if this seems to be a pure Apple Python that
* is unfrozen and presumably has the whole mercurial module in the file
system
* presumably is an Apple Python that uses Apple OpenSSL which has patches
for using system certificate store CAs in addition to the provided
cacerts file
"""
Yuya Nishihara
ssl: resolve symlink before checking for Apple python executable (issue4588)...
r24614 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
Mads Kiilerich
ssl: only use the dummy cert hack if using an Apple Python (issue4410)...
r23042 return False
Yuya Nishihara
ssl: resolve symlink before checking for Apple python executable (issue4588)...
r24614 exe = os.path.realpath(sys.executable).lower()
Mads Kiilerich
ssl: only use the dummy cert hack if using an Apple Python (issue4410)...
r23042 return (exe.startswith('/usr/bin/python') or
exe.startswith('/system/library/frameworks/python.framework/'))
Yuya Nishihara
ssl: extract function that returns dummycert path on Apple python...
r24288 def _defaultcacerts():
Yuya Nishihara
ssl: load CA certificates from system's store by default on Python 2.7.9...
r24291 """return path to CA certificates; None for system's store; ! to disable"""
Yuya Nishihara
ssl: extract function that returns dummycert path on Apple python...
r24288 if _plainapplepython():
dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
if os.path.exists(dummycert):
return dummycert
Yuya Nishihara
ssl: load CA certificates from system's store by default on Python 2.7.9...
r24291 if _canloaddefaultcerts:
return None
Yuya Nishihara
ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)...
r24290 return '!'
Yuya Nishihara
ssl: extract function that returns dummycert path on Apple python...
r24288
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 def sslkwargs(ui, host):
Augie Fackler
sslutil: drop support for clients of sslutil specifying a TLS version...
r23849 kws = {}
Mads Kiilerich
ssl: refactor sslkwargs - move things around a bit, preparing for next change
r22574 hostfingerprint = ui.config('hostfingerprints', host)
if hostfingerprint:
return kws
cacerts = ui.config('web', 'cacerts')
Yuya Nishihara
ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)...
r24290 if cacerts == '!':
pass
elif cacerts:
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 cacerts = util.expandpath(cacerts)
if not os.path.exists(cacerts):
raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
Yuya Nishihara
ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)...
r24290 else:
cacerts = _defaultcacerts()
if cacerts and cacerts != '!':
ui.debug('using %s to enable OS X system CA\n' % cacerts)
ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
if cacerts != '!':
Augie Fackler
sslutil: add a config knob to support TLS (default) or SSLv23 (bc) (issue4038)...
r19806 kws.update({'ca_certs': cacerts,
'cert_reqs': CERT_REQUIRED,
})
return kws
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204
class validator(object):
def __init__(self, ui, host):
self.ui = ui
self.host = host
FUJIWARA Katsunori
sslutil: abort if peer certificate is not verified for secure use...
r18887 def __call__(self, sock, strict=False):
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 host = self.host
cacerts = self.ui.config('web', 'cacerts')
hostfingerprint = self.ui.config('hostfingerprints', host)
Mads Kiilerich
sslutil: handle setups without .getpeercert() early in the validator...
r15813 if not getattr(sock, 'getpeercert', False): # python 2.5 ?
if hostfingerprint:
raise util.Abort(_("host fingerprint for %s can't be "
"verified (Python too old)") % host)
FUJIWARA Katsunori
sslutil: abort if peer certificate is not verified for secure use...
r18887 if strict:
raise util.Abort(_("certificate for %s can't be verified "
"(Python too old)") % host)
Steven Stallion
ui: optionally quiesce ssl verification warnings on python 2.5...
r16391 if self.ui.configbool('ui', 'reportoldssl', True):
self.ui.warn(_("warning: certificate for %s can't be verified "
"(Python too old)\n") % host)
Mads Kiilerich
sslutil: handle setups without .getpeercert() early in the validator...
r15813 return
Matt Mackall
sslutil: try harder to avoid getpeercert problems...
r18879
Mads Kiilerich
sslutil: work around validator crash getting certificate on failed sockets...
r15816 if not sock.cipher(): # work around http://bugs.python.org/issue13721
raise util.Abort(_('%s ssl connection error') % host)
Matt Mackall
sslutil: try harder to avoid getpeercert problems...
r18879 try:
peercert = sock.getpeercert(True)
peercert2 = sock.getpeercert()
except AttributeError:
raise util.Abort(_('%s ssl connection error') % host)
Mads Kiilerich
sslutil: abort properly if no certificate received for https connection...
r15817 if not peercert:
raise util.Abort(_('%s certificate error: '
'no certificate received') % host)
Mads Kiilerich
sslutil: show fingerprint when cacerts validation fails
r15814 peerfingerprint = util.sha1(peercert).hexdigest()
nicefingerprint = ":".join([peerfingerprint[x:x + 2]
for x in xrange(0, len(peerfingerprint), 2)])
Mads Kiilerich
sslutil: reorder validator code to make it more readable
r15815 if hostfingerprint:
if peerfingerprint.lower() != \
hostfingerprint.replace(':', '').lower():
Matt Mackall
sslutil: more helpful fingerprint mismatch message...
r15997 raise util.Abort(_('certificate for %s has unexpected '
'fingerprint %s') % (host, nicefingerprint),
hint=_('check hostfingerprint configuration'))
Mads Kiilerich
sslutil: reorder validator code to make it more readable
r15815 self.ui.debug('%s certificate matched fingerprint %s\n' %
(host, nicefingerprint))
Yuya Nishihara
ssl: set explicit symbol "!" to web.cacerts to disable SSL verification (BC)...
r24290 elif cacerts != '!':
Matt Mackall
sslutil: try harder to avoid getpeercert problems...
r18879 msg = _verifycert(peercert2, host)
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 if msg:
Mads Kiilerich
sslutil: show fingerprint when cacerts validation fails
r15814 raise util.Abort(_('%s certificate error: %s') % (host, msg),
hint=_('configure hostfingerprint %s or use '
'--insecure to connect insecurely') %
nicefingerprint)
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 self.ui.debug('%s certificate successfully verified\n' % host)
FUJIWARA Katsunori
sslutil: abort if peer certificate is not verified for secure use...
r18887 elif strict:
raise util.Abort(_('%s certificate with fingerprint %s not '
'verified') % (host, nicefingerprint),
hint=_('check hostfingerprints or web.cacerts '
'config setting'))
Augie Fackler
sslutil: extracted ssl methods from httpsconnection in url.py...
r14204 else:
Mads Kiilerich
sslutil: reorder validator code to make it more readable
r15815 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
'verified (check hostfingerprints or web.cacerts '
'config setting)\n') %
(host, nicefingerprint))