##// END OF EJS Templates
sslutil: store and use hostname and ui in socket instance...
Gregory Szorc -
r29226:33006bd6 default
parent child Browse files
Show More
@@ -1,365 +1,367 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 wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
109 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
110 ca_certs=None, serverhostname=None):
110 ca_certs=None, serverhostname=None):
111 """Add SSL/TLS to a socket.
111 """Add SSL/TLS to a socket.
112
112
113 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
113 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
114 choices based on what security options are available.
114 choices based on what security options are available.
115
115
116 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
116 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
117 the following additional arguments:
117 the following additional arguments:
118
118
119 * serverhostname - The expected hostname of the remote server. If the
119 * serverhostname - The expected hostname of the remote server. If the
120 server (and client) support SNI, this tells the server which certificate
120 server (and client) support SNI, this tells the server which certificate
121 to use.
121 to use.
122 """
122 """
123 if not serverhostname:
123 if not serverhostname:
124 raise error.Abort('serverhostname argument is required')
124 raise error.Abort('serverhostname argument is required')
125
125
126 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
126 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
127 # that both ends support, including TLS protocols. On legacy stacks,
127 # that both ends support, including TLS protocols. On legacy stacks,
128 # the highest it likely goes in TLS 1.0. On modern stacks, it can
128 # the highest it likely goes in TLS 1.0. On modern stacks, it can
129 # support TLS 1.2.
129 # support TLS 1.2.
130 #
130 #
131 # The PROTOCOL_TLSv* constants select a specific TLS version
131 # The PROTOCOL_TLSv* constants select a specific TLS version
132 # only (as opposed to multiple versions). So the method for
132 # only (as opposed to multiple versions). So the method for
133 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
133 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
134 # disable protocols via SSLContext.options and OP_NO_* constants.
134 # disable protocols via SSLContext.options and OP_NO_* constants.
135 # However, SSLContext.options doesn't work unless we have the
135 # However, SSLContext.options doesn't work unless we have the
136 # full/real SSLContext available to us.
136 # full/real SSLContext available to us.
137 #
137 #
138 # SSLv2 and SSLv3 are broken. We ban them outright.
138 # SSLv2 and SSLv3 are broken. We ban them outright.
139 if modernssl:
139 if modernssl:
140 protocol = ssl.PROTOCOL_SSLv23
140 protocol = ssl.PROTOCOL_SSLv23
141 else:
141 else:
142 protocol = ssl.PROTOCOL_TLSv1
142 protocol = ssl.PROTOCOL_TLSv1
143
143
144 # TODO use ssl.create_default_context() on modernssl.
144 # TODO use ssl.create_default_context() on modernssl.
145 sslcontext = SSLContext(protocol)
145 sslcontext = SSLContext(protocol)
146
146
147 # This is a no-op on old Python.
147 # This is a no-op on old Python.
148 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
148 sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
149
149
150 # This still works on our fake SSLContext.
150 # This still works on our fake SSLContext.
151 sslcontext.verify_mode = cert_reqs
151 sslcontext.verify_mode = cert_reqs
152
152
153 if certfile is not None:
153 if certfile is not None:
154 def password():
154 def password():
155 f = keyfile or certfile
155 f = keyfile or certfile
156 return ui.getpass(_('passphrase for %s: ') % f, '')
156 return ui.getpass(_('passphrase for %s: ') % f, '')
157 sslcontext.load_cert_chain(certfile, keyfile, password)
157 sslcontext.load_cert_chain(certfile, keyfile, password)
158
158
159 if ca_certs is not None:
159 if ca_certs is not None:
160 sslcontext.load_verify_locations(cafile=ca_certs)
160 sslcontext.load_verify_locations(cafile=ca_certs)
161 caloaded = True
161 caloaded = True
162 else:
162 else:
163 # This is a no-op on old Python.
163 # This is a no-op on old Python.
164 sslcontext.load_default_certs()
164 sslcontext.load_default_certs()
165 caloaded = _canloaddefaultcerts
165 caloaded = _canloaddefaultcerts
166
166
167 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
167 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
168 # check if wrap_socket failed silently because socket had been
168 # check if wrap_socket failed silently because socket had been
169 # closed
169 # closed
170 # - see http://bugs.python.org/issue13721
170 # - see http://bugs.python.org/issue13721
171 if not sslsocket.cipher():
171 if not sslsocket.cipher():
172 raise error.Abort(_('ssl connection failed'))
172 raise error.Abort(_('ssl connection failed'))
173
173
174 sslsocket._hgstate = {
174 sslsocket._hgstate = {
175 'caloaded': caloaded,
175 'caloaded': caloaded,
176 'hostname': serverhostname,
177 'ui': ui,
176 }
178 }
177
179
178 return sslsocket
180 return sslsocket
179
181
180 def _verifycert(cert, hostname):
182 def _verifycert(cert, hostname):
181 '''Verify that cert (in socket.getpeercert() format) matches hostname.
183 '''Verify that cert (in socket.getpeercert() format) matches hostname.
182 CRLs is not handled.
184 CRLs is not handled.
183
185
184 Returns error message if any problems are found and None on success.
186 Returns error message if any problems are found and None on success.
185 '''
187 '''
186 if not cert:
188 if not cert:
187 return _('no certificate received')
189 return _('no certificate received')
188 dnsname = hostname.lower()
190 dnsname = hostname.lower()
189 def matchdnsname(certname):
191 def matchdnsname(certname):
190 return (certname == dnsname or
192 return (certname == dnsname or
191 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
193 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
192
194
193 san = cert.get('subjectAltName', [])
195 san = cert.get('subjectAltName', [])
194 if san:
196 if san:
195 certnames = [value.lower() for key, value in san if key == 'DNS']
197 certnames = [value.lower() for key, value in san if key == 'DNS']
196 for name in certnames:
198 for name in certnames:
197 if matchdnsname(name):
199 if matchdnsname(name):
198 return None
200 return None
199 if certnames:
201 if certnames:
200 return _('certificate is for %s') % ', '.join(certnames)
202 return _('certificate is for %s') % ', '.join(certnames)
201
203
202 # subject is only checked when subjectAltName is empty
204 # subject is only checked when subjectAltName is empty
203 for s in cert.get('subject', []):
205 for s in cert.get('subject', []):
204 key, value = s[0]
206 key, value = s[0]
205 if key == 'commonName':
207 if key == 'commonName':
206 try:
208 try:
207 # 'subject' entries are unicode
209 # 'subject' entries are unicode
208 certname = value.lower().encode('ascii')
210 certname = value.lower().encode('ascii')
209 except UnicodeEncodeError:
211 except UnicodeEncodeError:
210 return _('IDN in certificate not supported')
212 return _('IDN in certificate not supported')
211 if matchdnsname(certname):
213 if matchdnsname(certname):
212 return None
214 return None
213 return _('certificate is for %s') % certname
215 return _('certificate is for %s') % certname
214 return _('no commonName or subjectAltName found in certificate')
216 return _('no commonName or subjectAltName found in certificate')
215
217
216
218
217 # CERT_REQUIRED means fetch the cert from the server all the time AND
219 # CERT_REQUIRED means fetch the cert from the server all the time AND
218 # validate it against the CA store provided in web.cacerts.
220 # validate it against the CA store provided in web.cacerts.
219
221
220 def _plainapplepython():
222 def _plainapplepython():
221 """return true if this seems to be a pure Apple Python that
223 """return true if this seems to be a pure Apple Python that
222 * is unfrozen and presumably has the whole mercurial module in the file
224 * is unfrozen and presumably has the whole mercurial module in the file
223 system
225 system
224 * presumably is an Apple Python that uses Apple OpenSSL which has patches
226 * presumably is an Apple Python that uses Apple OpenSSL which has patches
225 for using system certificate store CAs in addition to the provided
227 for using system certificate store CAs in addition to the provided
226 cacerts file
228 cacerts file
227 """
229 """
228 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
230 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
229 return False
231 return False
230 exe = os.path.realpath(sys.executable).lower()
232 exe = os.path.realpath(sys.executable).lower()
231 return (exe.startswith('/usr/bin/python') or
233 return (exe.startswith('/usr/bin/python') or
232 exe.startswith('/system/library/frameworks/python.framework/'))
234 exe.startswith('/system/library/frameworks/python.framework/'))
233
235
234 def _defaultcacerts():
236 def _defaultcacerts():
235 """return path to default CA certificates or None."""
237 """return path to default CA certificates or None."""
236 if _plainapplepython():
238 if _plainapplepython():
237 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
239 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
238 if os.path.exists(dummycert):
240 if os.path.exists(dummycert):
239 return dummycert
241 return dummycert
240
242
241 return None
243 return None
242
244
243 def sslkwargs(ui, host):
245 def sslkwargs(ui, host):
244 """Determine arguments to pass to wrapsocket().
246 """Determine arguments to pass to wrapsocket().
245
247
246 ``host`` is the hostname being connected to.
248 ``host`` is the hostname being connected to.
247 """
249 """
248 kws = {'ui': ui}
250 kws = {'ui': ui}
249
251
250 # If a host key fingerprint is on file, it is the only thing that matters
252 # If a host key fingerprint is on file, it is the only thing that matters
251 # and CA certs don't come into play.
253 # and CA certs don't come into play.
252 hostfingerprint = ui.config('hostfingerprints', host)
254 hostfingerprint = ui.config('hostfingerprints', host)
253 if hostfingerprint:
255 if hostfingerprint:
254 return kws
256 return kws
255
257
256 # The code below sets up CA verification arguments. If --insecure is
258 # The code below sets up CA verification arguments. If --insecure is
257 # used, we don't take CAs into consideration, so return early.
259 # used, we don't take CAs into consideration, so return early.
258 if ui.insecureconnections:
260 if ui.insecureconnections:
259 return kws
261 return kws
260
262
261 cacerts = ui.config('web', 'cacerts')
263 cacerts = ui.config('web', 'cacerts')
262
264
263 # If a value is set in the config, validate against a path and load
265 # If a value is set in the config, validate against a path and load
264 # and require those certs.
266 # and require those certs.
265 if cacerts:
267 if cacerts:
266 cacerts = util.expandpath(cacerts)
268 cacerts = util.expandpath(cacerts)
267 if not os.path.exists(cacerts):
269 if not os.path.exists(cacerts):
268 raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
270 raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
269
271
270 kws.update({'ca_certs': cacerts,
272 kws.update({'ca_certs': cacerts,
271 'cert_reqs': ssl.CERT_REQUIRED})
273 'cert_reqs': ssl.CERT_REQUIRED})
272 return kws
274 return kws
273
275
274 # No CAs in config. See if we can load defaults.
276 # No CAs in config. See if we can load defaults.
275 cacerts = _defaultcacerts()
277 cacerts = _defaultcacerts()
276
278
277 # We found an alternate CA bundle to use. Load it.
279 # We found an alternate CA bundle to use. Load it.
278 if cacerts:
280 if cacerts:
279 ui.debug('using %s to enable OS X system CA\n' % cacerts)
281 ui.debug('using %s to enable OS X system CA\n' % cacerts)
280 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
282 ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
281 kws.update({'ca_certs': cacerts,
283 kws.update({'ca_certs': cacerts,
282 'cert_reqs': ssl.CERT_REQUIRED})
284 'cert_reqs': ssl.CERT_REQUIRED})
283 return kws
285 return kws
284
286
285 # FUTURE this can disappear once wrapsocket() is secure by default.
287 # FUTURE this can disappear once wrapsocket() is secure by default.
286 if _canloaddefaultcerts:
288 if _canloaddefaultcerts:
287 kws['cert_reqs'] = ssl.CERT_REQUIRED
289 kws['cert_reqs'] = ssl.CERT_REQUIRED
288 return kws
290 return kws
289
291
290 return kws
292 return kws
291
293
292 class validator(object):
294 class validator(object):
293 def __init__(self, ui, host):
295 def __init__(self, ui=None, host=None):
294 self.ui = ui
296 pass
295 self.host = host
296
297
297 def __call__(self, sock, strict=False):
298 def __call__(self, sock, strict=False):
298 host = self.host
299 host = sock._hgstate['hostname']
300 ui = sock._hgstate['ui']
299
301
300 if not sock.cipher(): # work around http://bugs.python.org/issue13721
302 if not sock.cipher(): # work around http://bugs.python.org/issue13721
301 raise error.Abort(_('%s ssl connection error') % host)
303 raise error.Abort(_('%s ssl connection error') % host)
302 try:
304 try:
303 peercert = sock.getpeercert(True)
305 peercert = sock.getpeercert(True)
304 peercert2 = sock.getpeercert()
306 peercert2 = sock.getpeercert()
305 except AttributeError:
307 except AttributeError:
306 raise error.Abort(_('%s ssl connection error') % host)
308 raise error.Abort(_('%s ssl connection error') % host)
307
309
308 if not peercert:
310 if not peercert:
309 raise error.Abort(_('%s certificate error: '
311 raise error.Abort(_('%s certificate error: '
310 'no certificate received') % host)
312 'no certificate received') % host)
311
313
312 # If a certificate fingerprint is pinned, use it and only it to
314 # If a certificate fingerprint is pinned, use it and only it to
313 # validate the remote cert.
315 # validate the remote cert.
314 hostfingerprints = self.ui.configlist('hostfingerprints', host)
316 hostfingerprints = ui.configlist('hostfingerprints', host)
315 peerfingerprint = util.sha1(peercert).hexdigest()
317 peerfingerprint = util.sha1(peercert).hexdigest()
316 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
318 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
317 for x in xrange(0, len(peerfingerprint), 2)])
319 for x in xrange(0, len(peerfingerprint), 2)])
318 if hostfingerprints:
320 if hostfingerprints:
319 fingerprintmatch = False
321 fingerprintmatch = False
320 for hostfingerprint in hostfingerprints:
322 for hostfingerprint in hostfingerprints:
321 if peerfingerprint.lower() == \
323 if peerfingerprint.lower() == \
322 hostfingerprint.replace(':', '').lower():
324 hostfingerprint.replace(':', '').lower():
323 fingerprintmatch = True
325 fingerprintmatch = True
324 break
326 break
325 if not fingerprintmatch:
327 if not fingerprintmatch:
326 raise error.Abort(_('certificate for %s has unexpected '
328 raise error.Abort(_('certificate for %s has unexpected '
327 'fingerprint %s') % (host, nicefingerprint),
329 'fingerprint %s') % (host, nicefingerprint),
328 hint=_('check hostfingerprint configuration'))
330 hint=_('check hostfingerprint configuration'))
329 self.ui.debug('%s certificate matched fingerprint %s\n' %
331 ui.debug('%s certificate matched fingerprint %s\n' %
330 (host, nicefingerprint))
332 (host, nicefingerprint))
331 return
333 return
332
334
333 # If insecure connections were explicitly requested via --insecure,
335 # If insecure connections were explicitly requested via --insecure,
334 # print a warning and do no verification.
336 # print a warning and do no verification.
335 #
337 #
336 # It may seem odd that this is checked *after* host fingerprint pinning.
338 # It may seem odd that this is checked *after* host fingerprint pinning.
337 # This is for backwards compatibility (for now). The message is also
339 # This is for backwards compatibility (for now). The message is also
338 # the same as below for BC.
340 # the same as below for BC.
339 if self.ui.insecureconnections:
341 if ui.insecureconnections:
340 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
342 ui.warn(_('warning: %s certificate with fingerprint %s not '
341 'verified (check hostfingerprints or web.cacerts '
343 'verified (check hostfingerprints or web.cacerts '
342 'config setting)\n') %
344 'config setting)\n') %
343 (host, nicefingerprint))
345 (host, nicefingerprint))
344 return
346 return
345
347
346 if not sock._hgstate['caloaded']:
348 if not sock._hgstate['caloaded']:
347 if strict:
349 if strict:
348 raise error.Abort(_('%s certificate with fingerprint %s not '
350 raise error.Abort(_('%s certificate with fingerprint %s not '
349 'verified') % (host, nicefingerprint),
351 'verified') % (host, nicefingerprint),
350 hint=_('check hostfingerprints or '
352 hint=_('check hostfingerprints or '
351 'web.cacerts config setting'))
353 'web.cacerts config setting'))
352 else:
354 else:
353 self.ui.warn(_('warning: %s certificate with fingerprint %s '
355 ui.warn(_('warning: %s certificate with fingerprint %s '
354 'not verified (check hostfingerprints or '
356 'not verified (check hostfingerprints or '
355 'web.cacerts config setting)\n') %
357 'web.cacerts config setting)\n') %
356 (host, nicefingerprint))
358 (host, nicefingerprint))
357
359
358 return
360 return
359
361
360 msg = _verifycert(peercert2, host)
362 msg = _verifycert(peercert2, host)
361 if msg:
363 if msg:
362 raise error.Abort(_('%s certificate error: %s') % (host, msg),
364 raise error.Abort(_('%s certificate error: %s') % (host, msg),
363 hint=_('configure hostfingerprint %s or use '
365 hint=_('configure hostfingerprint %s or use '
364 '--insecure to connect insecurely') %
366 '--insecure to connect insecurely') %
365 nicefingerprint)
367 nicefingerprint)
General Comments 0
You need to be logged in to leave comments. Login now