##// END OF EJS Templates
sslutil: move SSLContext.verify_mode value into _hostsettings...
Gregory Szorc -
r29259:ec247e85 default
parent child Browse files
Show More
@@ -1,376 +1,383 b''
1 # sslutil.py - SSL handling for mercurial
1 # sslutil.py - SSL handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import os
12 import os
13 import ssl
13 import ssl
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from . import (
17 from . import (
18 error,
18 error,
19 util,
19 util,
20 )
20 )
21
21
22 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
22 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
23 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
23 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
24 # all exposed via the "ssl" module.
24 # all exposed via the "ssl" module.
25 #
25 #
26 # Depending on the version of Python being used, SSL/TLS support is either
26 # Depending on the version of Python being used, SSL/TLS support is either
27 # modern/secure or legacy/insecure. Many operations in this module have
27 # modern/secure or legacy/insecure. Many operations in this module have
28 # separate code paths depending on support in Python.
28 # separate code paths depending on support in Python.
29
29
30 hassni = getattr(ssl, 'HAS_SNI', False)
30 hassni = getattr(ssl, 'HAS_SNI', False)
31
31
32 try:
32 try:
33 OP_NO_SSLv2 = ssl.OP_NO_SSLv2
33 OP_NO_SSLv2 = ssl.OP_NO_SSLv2
34 OP_NO_SSLv3 = ssl.OP_NO_SSLv3
34 OP_NO_SSLv3 = ssl.OP_NO_SSLv3
35 except AttributeError:
35 except AttributeError:
36 OP_NO_SSLv2 = 0x1000000
36 OP_NO_SSLv2 = 0x1000000
37 OP_NO_SSLv3 = 0x2000000
37 OP_NO_SSLv3 = 0x2000000
38
38
39 try:
39 try:
40 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
40 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
41 # SSL/TLS features are available.
41 # SSL/TLS features are available.
42 SSLContext = ssl.SSLContext
42 SSLContext = ssl.SSLContext
43 modernssl = True
43 modernssl = True
44 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
44 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
45 except AttributeError:
45 except AttributeError:
46 modernssl = False
46 modernssl = False
47 _canloaddefaultcerts = False
47 _canloaddefaultcerts = False
48
48
49 # We implement SSLContext using the interface from the standard library.
49 # We implement SSLContext using the interface from the standard library.
50 class SSLContext(object):
50 class SSLContext(object):
51 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
51 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
52 _supportsciphers = sys.version_info >= (2, 7)
52 _supportsciphers = sys.version_info >= (2, 7)
53
53
54 def __init__(self, protocol):
54 def __init__(self, protocol):
55 # From the public interface of SSLContext
55 # From the public interface of SSLContext
56 self.protocol = protocol
56 self.protocol = protocol
57 self.check_hostname = False
57 self.check_hostname = False
58 self.options = 0
58 self.options = 0
59 self.verify_mode = ssl.CERT_NONE
59 self.verify_mode = ssl.CERT_NONE
60
60
61 # Used by our implementation.
61 # Used by our implementation.
62 self._certfile = None
62 self._certfile = None
63 self._keyfile = None
63 self._keyfile = None
64 self._certpassword = None
64 self._certpassword = None
65 self._cacerts = None
65 self._cacerts = None
66 self._ciphers = None
66 self._ciphers = None
67
67
68 def load_cert_chain(self, certfile, keyfile=None, password=None):
68 def load_cert_chain(self, certfile, keyfile=None, password=None):
69 self._certfile = certfile
69 self._certfile = certfile
70 self._keyfile = keyfile
70 self._keyfile = keyfile
71 self._certpassword = password
71 self._certpassword = password
72
72
73 def load_default_certs(self, purpose=None):
73 def load_default_certs(self, purpose=None):
74 pass
74 pass
75
75
76 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
76 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
77 if capath:
77 if capath:
78 raise error.Abort('capath not supported')
78 raise error.Abort('capath not supported')
79 if cadata:
79 if cadata:
80 raise error.Abort('cadata not supported')
80 raise error.Abort('cadata not supported')
81
81
82 self._cacerts = cafile
82 self._cacerts = cafile
83
83
84 def set_ciphers(self, ciphers):
84 def set_ciphers(self, ciphers):
85 if not self._supportsciphers:
85 if not self._supportsciphers:
86 raise error.Abort('setting ciphers not supported')
86 raise error.Abort('setting ciphers not supported')
87
87
88 self._ciphers = ciphers
88 self._ciphers = ciphers
89
89
90 def wrap_socket(self, socket, server_hostname=None, server_side=False):
90 def wrap_socket(self, socket, server_hostname=None, server_side=False):
91 # server_hostname is unique to SSLContext.wrap_socket and is used
91 # server_hostname is unique to SSLContext.wrap_socket and is used
92 # for SNI in that context. So there's nothing for us to do with it
92 # for SNI in that context. So there's nothing for us to do with it
93 # in this legacy code since we don't support SNI.
93 # in this legacy code since we don't support SNI.
94
94
95 args = {
95 args = {
96 'keyfile': self._keyfile,
96 'keyfile': self._keyfile,
97 'certfile': self._certfile,
97 'certfile': self._certfile,
98 'server_side': server_side,
98 'server_side': server_side,
99 'cert_reqs': self.verify_mode,
99 'cert_reqs': self.verify_mode,
100 'ssl_version': self.protocol,
100 'ssl_version': self.protocol,
101 'ca_certs': self._cacerts,
101 'ca_certs': self._cacerts,
102 }
102 }
103
103
104 if self._supportsciphers:
104 if self._supportsciphers:
105 args['ciphers'] = self._ciphers
105 args['ciphers'] = self._ciphers
106
106
107 return ssl.wrap_socket(socket, **args)
107 return ssl.wrap_socket(socket, **args)
108
108
109 def _hostsettings(ui, hostname):
109 def _hostsettings(ui, hostname):
110 """Obtain security settings for a hostname.
110 """Obtain security settings for a hostname.
111
111
112 Returns a dict of settings relevant to that hostname.
112 Returns a dict of settings relevant to that hostname.
113 """
113 """
114 s = {
114 s = {
115 # List of 2-tuple of (hash algorithm, hash).
115 # List of 2-tuple of (hash algorithm, hash).
116 'certfingerprints': [],
116 'certfingerprints': [],
117 # ssl.CERT_* constant used by SSLContext.verify_mode.
118 'verifymode': None,
117 }
119 }
118
120
119 # Fingerprints from [hostfingerprints] are always SHA-1.
121 # Fingerprints from [hostfingerprints] are always SHA-1.
120 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
122 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
121 fingerprint = fingerprint.replace(':', '').lower()
123 fingerprint = fingerprint.replace(':', '').lower()
122 s['certfingerprints'].append(('sha1', fingerprint))
124 s['certfingerprints'].append(('sha1', fingerprint))
123
125
126 # If a host cert fingerprint is defined, it is the only thing that
127 # matters. No need to validate CA certs.
128 if s['certfingerprints']:
129 s['verifymode'] = ssl.CERT_NONE
130
131 # If --insecure is used, don't take CAs into consideration.
132 elif ui.insecureconnections:
133 s['verifymode'] = ssl.CERT_NONE
134
135 # TODO assert verifymode is not None once we integrate cacert
136 # checking in this function.
137
124 return s
138 return s
125
139
126 def _determinecertoptions(ui, host):
140 def _determinecertoptions(ui, settings):
127 """Determine certificate options for a connections.
141 """Determine certificate options for a connections.
128
142
129 Returns a tuple of (cert_reqs, ca_certs).
143 Returns a tuple of (cert_reqs, ca_certs).
130 """
144 """
131 # If a host key fingerprint is on file, it is the only thing that matters
145 if settings['verifymode'] == ssl.CERT_NONE:
132 # and CA certs don't come into play.
133 hostfingerprint = ui.config('hostfingerprints', host)
134 if hostfingerprint:
135 return ssl.CERT_NONE, None
136
137 # The code below sets up CA verification arguments. If --insecure is
138 # used, we don't take CAs into consideration, so return early.
139 if ui.insecureconnections:
140 return ssl.CERT_NONE, None
146 return ssl.CERT_NONE, None
141
147
142 cacerts = ui.config('web', 'cacerts')
148 cacerts = ui.config('web', 'cacerts')
143
149
144 # If a value is set in the config, validate against a path and load
150 # If a value is set in the config, validate against a path and load
145 # and require those certs.
151 # and require those certs.
146 if cacerts:
152 if cacerts:
147 cacerts = util.expandpath(cacerts)
153 cacerts = util.expandpath(cacerts)
148 if not os.path.exists(cacerts):
154 if not os.path.exists(cacerts):
149 raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
155 raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
150
156
151 return ssl.CERT_REQUIRED, cacerts
157 return ssl.CERT_REQUIRED, cacerts
152
158
153 # No CAs in config. See if we can load defaults.
159 # No CAs in config. See if we can load defaults.
154 cacerts = _defaultcacerts()
160 cacerts = _defaultcacerts()
155
161
156 # We found an alternate CA bundle to use. Load it.
162 # We found an alternate CA bundle to use. Load it.
157 if cacerts:
163 if cacerts:
158 ui.debug('using %s to enable OS X system CA\n' % cacerts)
164 ui.debug('using %s to enable OS X system CA\n' % cacerts)
159 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
165 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
160 return ssl.CERT_REQUIRED, cacerts
166 return ssl.CERT_REQUIRED, cacerts
161
167
162 # FUTURE this can disappear once wrapsocket() is secure by default.
168 # FUTURE this can disappear once wrapsocket() is secure by default.
163 if _canloaddefaultcerts:
169 if _canloaddefaultcerts:
164 return ssl.CERT_REQUIRED, None
170 return ssl.CERT_REQUIRED, None
165
171
166 return ssl.CERT_NONE, None
172 return ssl.CERT_NONE, None
167
173
168 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
174 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
169 """Add SSL/TLS to a socket.
175 """Add SSL/TLS to a socket.
170
176
171 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
177 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
172 choices based on what security options are available.
178 choices based on what security options are available.
173
179
174 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
180 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
175 the following additional arguments:
181 the following additional arguments:
176
182
177 * serverhostname - The expected hostname of the remote server. If the
183 * serverhostname - The expected hostname of the remote server. If the
178 server (and client) support SNI, this tells the server which certificate
184 server (and client) support SNI, this tells the server which certificate
179 to use.
185 to use.
180 """
186 """
181 if not serverhostname:
187 if not serverhostname:
182 raise error.Abort('serverhostname argument is required')
188 raise error.Abort('serverhostname argument is required')
183
189
184 cert_reqs, ca_certs = _determinecertoptions(ui, serverhostname)
190 settings = _hostsettings(ui, serverhostname)
191 cert_reqs, ca_certs = _determinecertoptions(ui, settings)
185
192
186 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
193 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
187 # that both ends support, including TLS protocols. On legacy stacks,
194 # that both ends support, including TLS protocols. On legacy stacks,
188 # the highest it likely goes in TLS 1.0. On modern stacks, it can
195 # the highest it likely goes in TLS 1.0. On modern stacks, it can
189 # support TLS 1.2.
196 # support TLS 1.2.
190 #
197 #
191 # The PROTOCOL_TLSv* constants select a specific TLS version
198 # The PROTOCOL_TLSv* constants select a specific TLS version
192 # only (as opposed to multiple versions). So the method for
199 # only (as opposed to multiple versions). So the method for
193 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
200 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
194 # disable protocols via SSLContext.options and OP_NO_* constants.
201 # disable protocols via SSLContext.options and OP_NO_* constants.
195 # However, SSLContext.options doesn't work unless we have the
202 # However, SSLContext.options doesn't work unless we have the
196 # full/real SSLContext available to us.
203 # full/real SSLContext available to us.
197 #
204 #
198 # SSLv2 and SSLv3 are broken. We ban them outright.
205 # SSLv2 and SSLv3 are broken. We ban them outright.
199 if modernssl:
206 if modernssl:
200 protocol = ssl.PROTOCOL_SSLv23
207 protocol = ssl.PROTOCOL_SSLv23
201 else:
208 else:
202 protocol = ssl.PROTOCOL_TLSv1
209 protocol = ssl.PROTOCOL_TLSv1
203
210
204 # TODO use ssl.create_default_context() on modernssl.
211 # TODO use ssl.create_default_context() on modernssl.
205 sslcontext = SSLContext(protocol)
212 sslcontext = SSLContext(protocol)
206
213
207 # This is a no-op on old Python.
214 # This is a no-op on old Python.
208 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
215 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
209
216
210 # This still works on our fake SSLContext.
217 # This still works on our fake SSLContext.
211 sslcontext.verify_mode = cert_reqs
218 sslcontext.verify_mode = cert_reqs
212
219
213 if certfile is not None:
220 if certfile is not None:
214 def password():
221 def password():
215 f = keyfile or certfile
222 f = keyfile or certfile
216 return ui.getpass(_('passphrase for %s: ') % f, '')
223 return ui.getpass(_('passphrase for %s: ') % f, '')
217 sslcontext.load_cert_chain(certfile, keyfile, password)
224 sslcontext.load_cert_chain(certfile, keyfile, password)
218
225
219 if ca_certs is not None:
226 if ca_certs is not None:
220 sslcontext.load_verify_locations(cafile=ca_certs)
227 sslcontext.load_verify_locations(cafile=ca_certs)
221 caloaded = True
228 caloaded = True
222 else:
229 else:
223 # This is a no-op on old Python.
230 # This is a no-op on old Python.
224 sslcontext.load_default_certs()
231 sslcontext.load_default_certs()
225 caloaded = _canloaddefaultcerts
232 caloaded = _canloaddefaultcerts
226
233
227 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
234 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
228 # check if wrap_socket failed silently because socket had been
235 # check if wrap_socket failed silently because socket had been
229 # closed
236 # closed
230 # - see http://bugs.python.org/issue13721
237 # - see http://bugs.python.org/issue13721
231 if not sslsocket.cipher():
238 if not sslsocket.cipher():
232 raise error.Abort(_('ssl connection failed'))
239 raise error.Abort(_('ssl connection failed'))
233
240
234 sslsocket._hgstate = {
241 sslsocket._hgstate = {
235 'caloaded': caloaded,
242 'caloaded': caloaded,
236 'hostname': serverhostname,
243 'hostname': serverhostname,
237 'settings': _hostsettings(ui, serverhostname),
244 'settings': settings,
238 'ui': ui,
245 'ui': ui,
239 }
246 }
240
247
241 return sslsocket
248 return sslsocket
242
249
243 def _verifycert(cert, hostname):
250 def _verifycert(cert, hostname):
244 '''Verify that cert (in socket.getpeercert() format) matches hostname.
251 '''Verify that cert (in socket.getpeercert() format) matches hostname.
245 CRLs is not handled.
252 CRLs is not handled.
246
253
247 Returns error message if any problems are found and None on success.
254 Returns error message if any problems are found and None on success.
248 '''
255 '''
249 if not cert:
256 if not cert:
250 return _('no certificate received')
257 return _('no certificate received')
251 dnsname = hostname.lower()
258 dnsname = hostname.lower()
252 def matchdnsname(certname):
259 def matchdnsname(certname):
253 return (certname == dnsname or
260 return (certname == dnsname or
254 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
261 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
255
262
256 san = cert.get('subjectAltName', [])
263 san = cert.get('subjectAltName', [])
257 if san:
264 if san:
258 certnames = [value.lower() for key, value in san if key == 'DNS']
265 certnames = [value.lower() for key, value in san if key == 'DNS']
259 for name in certnames:
266 for name in certnames:
260 if matchdnsname(name):
267 if matchdnsname(name):
261 return None
268 return None
262 if certnames:
269 if certnames:
263 return _('certificate is for %s') % ', '.join(certnames)
270 return _('certificate is for %s') % ', '.join(certnames)
264
271
265 # subject is only checked when subjectAltName is empty
272 # subject is only checked when subjectAltName is empty
266 for s in cert.get('subject', []):
273 for s in cert.get('subject', []):
267 key, value = s[0]
274 key, value = s[0]
268 if key == 'commonName':
275 if key == 'commonName':
269 try:
276 try:
270 # 'subject' entries are unicode
277 # 'subject' entries are unicode
271 certname = value.lower().encode('ascii')
278 certname = value.lower().encode('ascii')
272 except UnicodeEncodeError:
279 except UnicodeEncodeError:
273 return _('IDN in certificate not supported')
280 return _('IDN in certificate not supported')
274 if matchdnsname(certname):
281 if matchdnsname(certname):
275 return None
282 return None
276 return _('certificate is for %s') % certname
283 return _('certificate is for %s') % certname
277 return _('no commonName or subjectAltName found in certificate')
284 return _('no commonName or subjectAltName found in certificate')
278
285
279
286
280 # CERT_REQUIRED means fetch the cert from the server all the time AND
287 # CERT_REQUIRED means fetch the cert from the server all the time AND
281 # validate it against the CA store provided in web.cacerts.
288 # validate it against the CA store provided in web.cacerts.
282
289
283 def _plainapplepython():
290 def _plainapplepython():
284 """return true if this seems to be a pure Apple Python that
291 """return true if this seems to be a pure Apple Python that
285 * is unfrozen and presumably has the whole mercurial module in the file
292 * is unfrozen and presumably has the whole mercurial module in the file
286 system
293 system
287 * presumably is an Apple Python that uses Apple OpenSSL which has patches
294 * presumably is an Apple Python that uses Apple OpenSSL which has patches
288 for using system certificate store CAs in addition to the provided
295 for using system certificate store CAs in addition to the provided
289 cacerts file
296 cacerts file
290 """
297 """
291 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
298 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
292 return False
299 return False
293 exe = os.path.realpath(sys.executable).lower()
300 exe = os.path.realpath(sys.executable).lower()
294 return (exe.startswith('/usr/bin/python') or
301 return (exe.startswith('/usr/bin/python') or
295 exe.startswith('/system/library/frameworks/python.framework/'))
302 exe.startswith('/system/library/frameworks/python.framework/'))
296
303
297 def _defaultcacerts():
304 def _defaultcacerts():
298 """return path to default CA certificates or None."""
305 """return path to default CA certificates or None."""
299 if _plainapplepython():
306 if _plainapplepython():
300 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
307 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
301 if os.path.exists(dummycert):
308 if os.path.exists(dummycert):
302 return dummycert
309 return dummycert
303
310
304 return None
311 return None
305
312
306 def validatesocket(sock, strict=False):
313 def validatesocket(sock, strict=False):
307 """Validate a socket meets security requiremnets.
314 """Validate a socket meets security requiremnets.
308
315
309 The passed socket must have been created with ``wrapsocket()``.
316 The passed socket must have been created with ``wrapsocket()``.
310 """
317 """
311 host = sock._hgstate['hostname']
318 host = sock._hgstate['hostname']
312 ui = sock._hgstate['ui']
319 ui = sock._hgstate['ui']
313 settings = sock._hgstate['settings']
320 settings = sock._hgstate['settings']
314
321
315 try:
322 try:
316 peercert = sock.getpeercert(True)
323 peercert = sock.getpeercert(True)
317 peercert2 = sock.getpeercert()
324 peercert2 = sock.getpeercert()
318 except AttributeError:
325 except AttributeError:
319 raise error.Abort(_('%s ssl connection error') % host)
326 raise error.Abort(_('%s ssl connection error') % host)
320
327
321 if not peercert:
328 if not peercert:
322 raise error.Abort(_('%s certificate error: '
329 raise error.Abort(_('%s certificate error: '
323 'no certificate received') % host)
330 'no certificate received') % host)
324
331
325 # If a certificate fingerprint is pinned, use it and only it to
332 # If a certificate fingerprint is pinned, use it and only it to
326 # validate the remote cert.
333 # validate the remote cert.
327 peerfingerprint = util.sha1(peercert).hexdigest()
334 peerfingerprint = util.sha1(peercert).hexdigest()
328 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
335 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
329 for x in xrange(0, len(peerfingerprint), 2)])
336 for x in xrange(0, len(peerfingerprint), 2)])
330 if settings['certfingerprints']:
337 if settings['certfingerprints']:
331 fingerprintmatch = False
338 fingerprintmatch = False
332 for hash, fingerprint in settings['certfingerprints']:
339 for hash, fingerprint in settings['certfingerprints']:
333 if peerfingerprint.lower() == fingerprint:
340 if peerfingerprint.lower() == fingerprint:
334 fingerprintmatch = True
341 fingerprintmatch = True
335 break
342 break
336 if not fingerprintmatch:
343 if not fingerprintmatch:
337 raise error.Abort(_('certificate for %s has unexpected '
344 raise error.Abort(_('certificate for %s has unexpected '
338 'fingerprint %s') % (host, nicefingerprint),
345 'fingerprint %s') % (host, nicefingerprint),
339 hint=_('check hostfingerprint configuration'))
346 hint=_('check hostfingerprint configuration'))
340 ui.debug('%s certificate matched fingerprint %s\n' %
347 ui.debug('%s certificate matched fingerprint %s\n' %
341 (host, nicefingerprint))
348 (host, nicefingerprint))
342 return
349 return
343
350
344 # If insecure connections were explicitly requested via --insecure,
351 # If insecure connections were explicitly requested via --insecure,
345 # print a warning and do no verification.
352 # print a warning and do no verification.
346 #
353 #
347 # It may seem odd that this is checked *after* host fingerprint pinning.
354 # It may seem odd that this is checked *after* host fingerprint pinning.
348 # This is for backwards compatibility (for now). The message is also
355 # This is for backwards compatibility (for now). The message is also
349 # the same as below for BC.
356 # the same as below for BC.
350 if ui.insecureconnections:
357 if ui.insecureconnections:
351 ui.warn(_('warning: %s certificate with fingerprint %s not '
358 ui.warn(_('warning: %s certificate with fingerprint %s not '
352 'verified (check hostfingerprints or web.cacerts '
359 'verified (check hostfingerprints or web.cacerts '
353 'config setting)\n') %
360 'config setting)\n') %
354 (host, nicefingerprint))
361 (host, nicefingerprint))
355 return
362 return
356
363
357 if not sock._hgstate['caloaded']:
364 if not sock._hgstate['caloaded']:
358 if strict:
365 if strict:
359 raise error.Abort(_('%s certificate with fingerprint %s not '
366 raise error.Abort(_('%s certificate with fingerprint %s not '
360 'verified') % (host, nicefingerprint),
367 'verified') % (host, nicefingerprint),
361 hint=_('check hostfingerprints or '
368 hint=_('check hostfingerprints or '
362 'web.cacerts config setting'))
369 'web.cacerts config setting'))
363 else:
370 else:
364 ui.warn(_('warning: %s certificate with fingerprint %s '
371 ui.warn(_('warning: %s certificate with fingerprint %s '
365 'not verified (check hostfingerprints or '
372 'not verified (check hostfingerprints or '
366 'web.cacerts config setting)\n') %
373 'web.cacerts config setting)\n') %
367 (host, nicefingerprint))
374 (host, nicefingerprint))
368
375
369 return
376 return
370
377
371 msg = _verifycert(peercert2, host)
378 msg = _verifycert(peercert2, host)
372 if msg:
379 if msg:
373 raise error.Abort(_('%s certificate error: %s') % (host, msg),
380 raise error.Abort(_('%s certificate error: %s') % (host, msg),
374 hint=_('configure hostfingerprint %s or use '
381 hint=_('configure hostfingerprint %s or use '
375 '--insecure to connect insecurely') %
382 '--insecure to connect insecurely') %
376 nicefingerprint)
383 nicefingerprint)
General Comments 0
You need to be logged in to leave comments. Login now