1
# Copyright (C) 2006-2013, 2016 Canonical Ltd
1
# Copyright (C) 2006-2012 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
77
_ = (ssl.match_hostname, ssl.CertificateError)
78
except AttributeError:
79
# Provide fallbacks for python < 2.7.9
80
def match_hostname(cert, host):
82
'%s cannot be verified, https certificates verification is only'
83
' available for python versions >= 2.7.9' % (host,))
84
ssl.match_hostname = match_hostname
85
ssl.CertificateError = ValueError
73
lazy_import.lazy_import(globals(), """
88
78
# Note for packagers: if there is no package providing certs for your platform,
89
79
# the curl project produces http://curl.haxx.se/ca/cacert.pem weekly.
90
80
_ssl_ca_certs_known_locations = [
91
u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo
92
u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH
93
u'/etc/ssl/ca-bundle.pem', # OpenSuse
94
u'/etc/ssl/cert.pem', # OpenSuse
95
u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
81
u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo
82
u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH
83
u'/etc/ssl/ca-bundle.pem', # OpenSuse
84
u'/etc/ssl/cert.pem', # OpenSuse
85
u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
96
86
# XXX: Needs checking, can't trust the interweb ;) -- vila 2012-01-25
97
u'/etc/openssl/certs/ca-certificates.crt', # Solaris
87
u'/etc/openssl/certs/ca-certificates.crt', # Solaris
101
89
def default_ca_certs():
102
90
if sys.platform == 'win32':
103
91
return os.path.join(os.path.dirname(sys.executable), u"cacert.pem")
126
114
def cert_reqs_from_store(unicode_str):
129
return {"required": ssl.CERT_REQUIRED,
130
"none": ssl.CERT_NONE}[unicode_str]
118
"required": ssl.CERT_REQUIRED,
119
"none": ssl.CERT_NONE
132
122
raise ValueError("invalid value %s" % unicode_str)
135
124
def default_ca_reqs():
136
125
if sys.platform in ('win32', 'darwin'):
137
126
# FIXME: Once we get a native access to root certificates there, this
141
130
return u'required'
143
132
opt_ssl_ca_certs = config.Option('ssl.ca_certs',
144
from_unicode=ca_certs_from_store,
145
default=default_ca_certs,
133
from_unicode=ca_certs_from_store,
134
default=default_ca_certs,
148
137
Path to certification authority certificates to trust.
150
139
This should be a valid path to a bundle containing all root Certificate
156
145
opt_ssl_cert_reqs = config.Option('ssl.cert_reqs',
157
default=default_ca_reqs,
158
from_unicode=cert_reqs_from_store,
146
default=default_ca_reqs,
147
from_unicode=cert_reqs_from_store,
161
150
Whether to require a certificate from the remote side. (default:required)
408
397
self._wrap_socket_for_reporting(self.sock)
400
# These two methods were imported from Python 3.2's ssl module
402
def _dnsname_to_pat(dn):
404
for frag in dn.split(r'.'):
406
# When '*' is a fragment by itself, it matches a non-empty dotless
410
# Otherwise, '*' matches any dotless fragment.
411
frag = re.escape(frag)
412
pats.append(frag.replace(r'\*', '[^.]*'))
413
return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
416
def match_hostname(cert, hostname):
417
"""Verify that *cert* (in decoded format as returned by
418
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
419
are mostly followed, but IP addresses are not accepted for *hostname*.
421
CertificateError is raised on failure. On success, the function
425
raise ValueError("empty or no certificate")
427
san = cert.get('subjectAltName', ())
428
for key, value in san:
430
if _dnsname_to_pat(value).match(hostname):
432
dnsnames.append(value)
434
# The subject is only checked when subjectAltName is empty
435
for sub in cert.get('subject', ()):
436
for key, value in sub:
437
# XXX according to RFC 2818, the most specific Common Name
439
if key == 'commonName':
440
if _dnsname_to_pat(value).match(hostname):
442
dnsnames.append(value)
443
if len(dnsnames) > 1:
444
raise errors.CertificateError(
445
"hostname %r doesn't match either of %s"
446
% (hostname, ', '.join(map(repr, dnsnames))))
447
elif len(dnsnames) == 1:
448
raise errors.CertificateError("hostname %r doesn't match %r" %
449
(hostname, dnsnames[0]))
451
raise errors.CertificateError("no appropriate commonName or "
452
"subjectAltName fields were found")
411
455
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
413
457
def __init__(self, host, port=None, key_file=None, cert_file=None,
439
483
if cert_reqs == ssl.CERT_NONE:
440
ui.ui_factory.show_user_warning('not_checking_ssl_cert', host=host)
441
ui.ui_factory.suppressed_warnings.add('not_checking_ssl_cert')
484
trace.warning("Not checking SSL certificate for %s", host)
444
487
if self.ca_certs is None:
451
494
"'bzr help ssl.ca_certs' for more information on setting "
454
ssl_sock = ssl.wrap_socket(
455
self.sock, self.key_file, self.cert_file,
497
ssl_sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file,
456
498
cert_reqs=cert_reqs, ca_certs=ca_certs)
499
except ssl.SSLError, e:
460
502
"See `bzr help ssl.ca_certs` for how to specify trusted CA"
465
507
if cert_reqs == ssl.CERT_REQUIRED:
466
508
peer_cert = ssl_sock.getpeercert()
467
ssl.match_hostname(peer_cert, host)
509
match_hostname(peer_cert, host)
469
511
# Wrap the ssl socket before anybody use it
470
512
self._wrap_socket_for_reporting(ssl_sock)
782
824
% (request, request.connection.sock.getsockname())
783
825
response = connection.getresponse()
784
826
convert_to_addinfourl = True
785
except (ssl.SSLError, ssl.CertificateError):
827
except (ssl.SSLError, errors.CertificateError):
786
828
# Something is wrong with either the certificate or the hostname,
787
829
# re-trying won't help