##// END OF EJS Templates
ssl: remove CERT_REQUIRED constant that was necessary for compatibility
Yuya Nishihara -
r25432:bdc15b3c default
parent child Browse files
Show More
@@ -1,200 +1,199 b''
1 1 # sslutil.py - SSL handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9 import os, sys, ssl
10 10
11 11 from mercurial import util
12 12 from mercurial.i18n import _
13 13
14 14 _canloaddefaultcerts = False
15 CERT_REQUIRED = ssl.CERT_REQUIRED
16 15 try:
17 16 ssl_context = ssl.SSLContext
18 17 _canloaddefaultcerts = util.safehasattr(ssl_context, 'load_default_certs')
19 18
20 19 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
21 20 ca_certs=None, serverhostname=None):
22 21 # Allow any version of SSL starting with TLSv1 and
23 22 # up. Note that specifying TLSv1 here prohibits use of
24 23 # newer standards (like TLSv1_2), so this is the right way
25 24 # to do this. Note that in the future it'd be better to
26 25 # support using ssl.create_default_context(), which sets
27 26 # up a bunch of things in smart ways (strong ciphers,
28 27 # protocol versions, etc) and is upgraded by Python
29 28 # maintainers for us, but that breaks too many things to
30 29 # do it in a hurry.
31 30 sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
32 31 sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3
33 32 if certfile is not None:
34 33 def password():
35 34 f = keyfile or certfile
36 35 return ui.getpass(_('passphrase for %s: ') % f, '')
37 36 sslcontext.load_cert_chain(certfile, keyfile, password)
38 37 sslcontext.verify_mode = cert_reqs
39 38 if ca_certs is not None:
40 39 sslcontext.load_verify_locations(cafile=ca_certs)
41 40 elif _canloaddefaultcerts:
42 41 sslcontext.load_default_certs()
43 42
44 43 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
45 44 # check if wrap_socket failed silently because socket had been
46 45 # closed
47 46 # - see http://bugs.python.org/issue13721
48 47 if not sslsocket.cipher():
49 48 raise util.Abort(_('ssl connection failed'))
50 49 return sslsocket
51 50 except AttributeError:
52 51 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
53 52 ca_certs=None, serverhostname=None):
54 53 sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
55 54 cert_reqs=cert_reqs, ca_certs=ca_certs,
56 55 ssl_version=ssl.PROTOCOL_TLSv1)
57 56 # check if wrap_socket failed silently because socket had been
58 57 # closed
59 58 # - see http://bugs.python.org/issue13721
60 59 if not sslsocket.cipher():
61 60 raise util.Abort(_('ssl connection failed'))
62 61 return sslsocket
63 62
64 63 def _verifycert(cert, hostname):
65 64 '''Verify that cert (in socket.getpeercert() format) matches hostname.
66 65 CRLs is not handled.
67 66
68 67 Returns error message if any problems are found and None on success.
69 68 '''
70 69 if not cert:
71 70 return _('no certificate received')
72 71 dnsname = hostname.lower()
73 72 def matchdnsname(certname):
74 73 return (certname == dnsname or
75 74 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
76 75
77 76 san = cert.get('subjectAltName', [])
78 77 if san:
79 78 certnames = [value.lower() for key, value in san if key == 'DNS']
80 79 for name in certnames:
81 80 if matchdnsname(name):
82 81 return None
83 82 if certnames:
84 83 return _('certificate is for %s') % ', '.join(certnames)
85 84
86 85 # subject is only checked when subjectAltName is empty
87 86 for s in cert.get('subject', []):
88 87 key, value = s[0]
89 88 if key == 'commonName':
90 89 try:
91 90 # 'subject' entries are unicode
92 91 certname = value.lower().encode('ascii')
93 92 except UnicodeEncodeError:
94 93 return _('IDN in certificate not supported')
95 94 if matchdnsname(certname):
96 95 return None
97 96 return _('certificate is for %s') % certname
98 97 return _('no commonName or subjectAltName found in certificate')
99 98
100 99
101 100 # CERT_REQUIRED means fetch the cert from the server all the time AND
102 101 # validate it against the CA store provided in web.cacerts.
103 102
104 103 def _plainapplepython():
105 104 """return true if this seems to be a pure Apple Python that
106 105 * is unfrozen and presumably has the whole mercurial module in the file
107 106 system
108 107 * presumably is an Apple Python that uses Apple OpenSSL which has patches
109 108 for using system certificate store CAs in addition to the provided
110 109 cacerts file
111 110 """
112 111 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
113 112 return False
114 113 exe = os.path.realpath(sys.executable).lower()
115 114 return (exe.startswith('/usr/bin/python') or
116 115 exe.startswith('/system/library/frameworks/python.framework/'))
117 116
118 117 def _defaultcacerts():
119 118 """return path to CA certificates; None for system's store; ! to disable"""
120 119 if _plainapplepython():
121 120 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
122 121 if os.path.exists(dummycert):
123 122 return dummycert
124 123 if _canloaddefaultcerts:
125 124 return None
126 125 return '!'
127 126
128 127 def sslkwargs(ui, host):
129 128 kws = {'ui': ui}
130 129 hostfingerprint = ui.config('hostfingerprints', host)
131 130 if hostfingerprint:
132 131 return kws
133 132 cacerts = ui.config('web', 'cacerts')
134 133 if cacerts == '!':
135 134 pass
136 135 elif cacerts:
137 136 cacerts = util.expandpath(cacerts)
138 137 if not os.path.exists(cacerts):
139 138 raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
140 139 else:
141 140 cacerts = _defaultcacerts()
142 141 if cacerts and cacerts != '!':
143 142 ui.debug('using %s to enable OS X system CA\n' % cacerts)
144 143 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
145 144 if cacerts != '!':
146 145 kws.update({'ca_certs': cacerts,
147 'cert_reqs': CERT_REQUIRED,
146 'cert_reqs': ssl.CERT_REQUIRED,
148 147 })
149 148 return kws
150 149
151 150 class validator(object):
152 151 def __init__(self, ui, host):
153 152 self.ui = ui
154 153 self.host = host
155 154
156 155 def __call__(self, sock, strict=False):
157 156 host = self.host
158 157 cacerts = self.ui.config('web', 'cacerts')
159 158 hostfingerprint = self.ui.config('hostfingerprints', host)
160 159
161 160 if not sock.cipher(): # work around http://bugs.python.org/issue13721
162 161 raise util.Abort(_('%s ssl connection error') % host)
163 162 try:
164 163 peercert = sock.getpeercert(True)
165 164 peercert2 = sock.getpeercert()
166 165 except AttributeError:
167 166 raise util.Abort(_('%s ssl connection error') % host)
168 167
169 168 if not peercert:
170 169 raise util.Abort(_('%s certificate error: '
171 170 'no certificate received') % host)
172 171 peerfingerprint = util.sha1(peercert).hexdigest()
173 172 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
174 173 for x in xrange(0, len(peerfingerprint), 2)])
175 174 if hostfingerprint:
176 175 if peerfingerprint.lower() != \
177 176 hostfingerprint.replace(':', '').lower():
178 177 raise util.Abort(_('certificate for %s has unexpected '
179 178 'fingerprint %s') % (host, nicefingerprint),
180 179 hint=_('check hostfingerprint configuration'))
181 180 self.ui.debug('%s certificate matched fingerprint %s\n' %
182 181 (host, nicefingerprint))
183 182 elif cacerts != '!':
184 183 msg = _verifycert(peercert2, host)
185 184 if msg:
186 185 raise util.Abort(_('%s certificate error: %s') % (host, msg),
187 186 hint=_('configure hostfingerprint %s or use '
188 187 '--insecure to connect insecurely') %
189 188 nicefingerprint)
190 189 self.ui.debug('%s certificate successfully verified\n' % host)
191 190 elif strict:
192 191 raise util.Abort(_('%s certificate with fingerprint %s not '
193 192 'verified') % (host, nicefingerprint),
194 193 hint=_('check hostfingerprints or web.cacerts '
195 194 'config setting'))
196 195 else:
197 196 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
198 197 'verified (check hostfingerprints or web.cacerts '
199 198 'config setting)\n') %
200 199 (host, nicefingerprint))
General Comments 0
You need to be logged in to leave comments. Login now