##// END OF EJS Templates
sslutil: print a warning when using TLS 1.0 on legacy Python...
Gregory Szorc -
r29561:1a782fab default
parent child Browse files
Show More
@@ -1,741 +1,751 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 hashlib
12 import hashlib
13 import os
13 import os
14 import re
14 import re
15 import ssl
15 import ssl
16 import sys
16 import sys
17
17
18 from .i18n import _
18 from .i18n import _
19 from . import (
19 from . import (
20 error,
20 error,
21 util,
21 util,
22 )
22 )
23
23
24 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
24 # Python 2.7.9+ overhauled the built-in SSL/TLS features of Python. It added
25 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
25 # support for TLS 1.1, TLS 1.2, SNI, system CA stores, etc. These features are
26 # all exposed via the "ssl" module.
26 # all exposed via the "ssl" module.
27 #
27 #
28 # Depending on the version of Python being used, SSL/TLS support is either
28 # Depending on the version of Python being used, SSL/TLS support is either
29 # modern/secure or legacy/insecure. Many operations in this module have
29 # modern/secure or legacy/insecure. Many operations in this module have
30 # separate code paths depending on support in Python.
30 # separate code paths depending on support in Python.
31
31
32 configprotocols = set([
32 configprotocols = set([
33 'tls1.0',
33 'tls1.0',
34 'tls1.1',
34 'tls1.1',
35 'tls1.2',
35 'tls1.2',
36 ])
36 ])
37
37
38 hassni = getattr(ssl, 'HAS_SNI', False)
38 hassni = getattr(ssl, 'HAS_SNI', False)
39
39
40 try:
40 try:
41 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
41 # ssl.SSLContext was added in 2.7.9 and presence indicates modern
42 # SSL/TLS features are available.
42 # SSL/TLS features are available.
43 SSLContext = ssl.SSLContext
43 SSLContext = ssl.SSLContext
44 modernssl = True
44 modernssl = True
45 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
45 _canloaddefaultcerts = util.safehasattr(SSLContext, 'load_default_certs')
46 except AttributeError:
46 except AttributeError:
47 modernssl = False
47 modernssl = False
48 _canloaddefaultcerts = False
48 _canloaddefaultcerts = False
49
49
50 # We implement SSLContext using the interface from the standard library.
50 # We implement SSLContext using the interface from the standard library.
51 class SSLContext(object):
51 class SSLContext(object):
52 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
52 # ssl.wrap_socket gained the "ciphers" named argument in 2.7.
53 _supportsciphers = sys.version_info >= (2, 7)
53 _supportsciphers = sys.version_info >= (2, 7)
54
54
55 def __init__(self, protocol):
55 def __init__(self, protocol):
56 # From the public interface of SSLContext
56 # From the public interface of SSLContext
57 self.protocol = protocol
57 self.protocol = protocol
58 self.check_hostname = False
58 self.check_hostname = False
59 self.options = 0
59 self.options = 0
60 self.verify_mode = ssl.CERT_NONE
60 self.verify_mode = ssl.CERT_NONE
61
61
62 # Used by our implementation.
62 # Used by our implementation.
63 self._certfile = None
63 self._certfile = None
64 self._keyfile = None
64 self._keyfile = None
65 self._certpassword = None
65 self._certpassword = None
66 self._cacerts = None
66 self._cacerts = None
67 self._ciphers = None
67 self._ciphers = None
68
68
69 def load_cert_chain(self, certfile, keyfile=None, password=None):
69 def load_cert_chain(self, certfile, keyfile=None, password=None):
70 self._certfile = certfile
70 self._certfile = certfile
71 self._keyfile = keyfile
71 self._keyfile = keyfile
72 self._certpassword = password
72 self._certpassword = password
73
73
74 def load_default_certs(self, purpose=None):
74 def load_default_certs(self, purpose=None):
75 pass
75 pass
76
76
77 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
77 def load_verify_locations(self, cafile=None, capath=None, cadata=None):
78 if capath:
78 if capath:
79 raise error.Abort(_('capath not supported'))
79 raise error.Abort(_('capath not supported'))
80 if cadata:
80 if cadata:
81 raise error.Abort(_('cadata not supported'))
81 raise error.Abort(_('cadata not supported'))
82
82
83 self._cacerts = cafile
83 self._cacerts = cafile
84
84
85 def set_ciphers(self, ciphers):
85 def set_ciphers(self, ciphers):
86 if not self._supportsciphers:
86 if not self._supportsciphers:
87 raise error.Abort(_('setting ciphers not supported'))
87 raise error.Abort(_('setting ciphers not supported'))
88
88
89 self._ciphers = ciphers
89 self._ciphers = ciphers
90
90
91 def wrap_socket(self, socket, server_hostname=None, server_side=False):
91 def wrap_socket(self, socket, server_hostname=None, server_side=False):
92 # server_hostname is unique to SSLContext.wrap_socket and is used
92 # server_hostname is unique to SSLContext.wrap_socket and is used
93 # for SNI in that context. So there's nothing for us to do with it
93 # for SNI in that context. So there's nothing for us to do with it
94 # in this legacy code since we don't support SNI.
94 # in this legacy code since we don't support SNI.
95
95
96 args = {
96 args = {
97 'keyfile': self._keyfile,
97 'keyfile': self._keyfile,
98 'certfile': self._certfile,
98 'certfile': self._certfile,
99 'server_side': server_side,
99 'server_side': server_side,
100 'cert_reqs': self.verify_mode,
100 'cert_reqs': self.verify_mode,
101 'ssl_version': self.protocol,
101 'ssl_version': self.protocol,
102 'ca_certs': self._cacerts,
102 'ca_certs': self._cacerts,
103 }
103 }
104
104
105 if self._supportsciphers:
105 if self._supportsciphers:
106 args['ciphers'] = self._ciphers
106 args['ciphers'] = self._ciphers
107
107
108 return ssl.wrap_socket(socket, **args)
108 return ssl.wrap_socket(socket, **args)
109
109
110 def _hostsettings(ui, hostname):
110 def _hostsettings(ui, hostname):
111 """Obtain security settings for a hostname.
111 """Obtain security settings for a hostname.
112
112
113 Returns a dict of settings relevant to that hostname.
113 Returns a dict of settings relevant to that hostname.
114 """
114 """
115 s = {
115 s = {
116 # Whether we should attempt to load default/available CA certs
116 # Whether we should attempt to load default/available CA certs
117 # if an explicit ``cafile`` is not defined.
117 # if an explicit ``cafile`` is not defined.
118 'allowloaddefaultcerts': True,
118 'allowloaddefaultcerts': True,
119 # List of 2-tuple of (hash algorithm, hash).
119 # List of 2-tuple of (hash algorithm, hash).
120 'certfingerprints': [],
120 'certfingerprints': [],
121 # Path to file containing concatenated CA certs. Used by
121 # Path to file containing concatenated CA certs. Used by
122 # SSLContext.load_verify_locations().
122 # SSLContext.load_verify_locations().
123 'cafile': None,
123 'cafile': None,
124 # Whether certificate verification should be disabled.
124 # Whether certificate verification should be disabled.
125 'disablecertverification': False,
125 'disablecertverification': False,
126 # Whether the legacy [hostfingerprints] section has data for this host.
126 # Whether the legacy [hostfingerprints] section has data for this host.
127 'legacyfingerprint': False,
127 'legacyfingerprint': False,
128 # PROTOCOL_* constant to use for SSLContext.__init__.
128 # PROTOCOL_* constant to use for SSLContext.__init__.
129 'protocol': None,
129 'protocol': None,
130 # ssl.CERT_* constant used by SSLContext.verify_mode.
130 # ssl.CERT_* constant used by SSLContext.verify_mode.
131 'verifymode': None,
131 'verifymode': None,
132 # Defines extra ssl.OP* bitwise options to set.
132 # Defines extra ssl.OP* bitwise options to set.
133 'ctxoptions': None,
133 'ctxoptions': None,
134 }
134 }
135
135
136 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
136 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
137 # that both ends support, including TLS protocols. On legacy stacks,
137 # that both ends support, including TLS protocols. On legacy stacks,
138 # the highest it likely goes is TLS 1.0. On modern stacks, it can
138 # the highest it likely goes is TLS 1.0. On modern stacks, it can
139 # support TLS 1.2.
139 # support TLS 1.2.
140 #
140 #
141 # The PROTOCOL_TLSv* constants select a specific TLS version
141 # The PROTOCOL_TLSv* constants select a specific TLS version
142 # only (as opposed to multiple versions). So the method for
142 # only (as opposed to multiple versions). So the method for
143 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
143 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
144 # disable protocols via SSLContext.options and OP_NO_* constants.
144 # disable protocols via SSLContext.options and OP_NO_* constants.
145 # However, SSLContext.options doesn't work unless we have the
145 # However, SSLContext.options doesn't work unless we have the
146 # full/real SSLContext available to us.
146 # full/real SSLContext available to us.
147
147
148 # Allow minimum TLS protocol to be specified in the config.
148 # Allow minimum TLS protocol to be specified in the config.
149 def validateprotocol(protocol, key):
149 def validateprotocol(protocol, key):
150 if protocol not in configprotocols:
150 if protocol not in configprotocols:
151 raise error.Abort(
151 raise error.Abort(
152 _('unsupported protocol from hostsecurity.%s: %s') %
152 _('unsupported protocol from hostsecurity.%s: %s') %
153 (key, protocol),
153 (key, protocol),
154 hint=_('valid protocols: %s') %
154 hint=_('valid protocols: %s') %
155 ' '.join(sorted(configprotocols)))
155 ' '.join(sorted(configprotocols)))
156
156
157 # Legacy Python can only do TLS 1.0. We default to TLS 1.1+ where we
157 # Legacy Python can only do TLS 1.0. We default to TLS 1.1+ where we
158 # can because TLS 1.0 has known vulnerabilities (like BEAST and POODLE).
158 # can because TLS 1.0 has known vulnerabilities (like BEAST and POODLE).
159 # We allow users to downgrade to TLS 1.0+ via config options in case a
159 # We allow users to downgrade to TLS 1.0+ via config options in case a
160 # legacy server is encountered.
160 # legacy server is encountered.
161 if modernssl:
161 if modernssl:
162 defaultprotocol = 'tls1.1'
162 defaultprotocol = 'tls1.1'
163 else:
163 else:
164 # Let people on legacy Python versions know they are borderline
165 # secure.
166 # We don't document this config option because we want people to see
167 # the bold warnings on the web site.
168 # internal config: hostsecurity.disabletls10warning
169 if not ui.configbool('hostsecurity', 'disabletls10warning'):
170 ui.warn(_('warning: connecting to %s using legacy security '
171 'technology (TLS 1.0); see '
172 'https://mercurial-scm.org/wiki/SecureConnections for '
173 'more info\n') % hostname)
164 defaultprotocol = 'tls1.0'
174 defaultprotocol = 'tls1.0'
165
175
166 key = 'minimumprotocol'
176 key = 'minimumprotocol'
167 protocol = ui.config('hostsecurity', key, defaultprotocol)
177 protocol = ui.config('hostsecurity', key, defaultprotocol)
168 validateprotocol(protocol, key)
178 validateprotocol(protocol, key)
169
179
170 key = '%s:minimumprotocol' % hostname
180 key = '%s:minimumprotocol' % hostname
171 protocol = ui.config('hostsecurity', key, protocol)
181 protocol = ui.config('hostsecurity', key, protocol)
172 validateprotocol(protocol, key)
182 validateprotocol(protocol, key)
173
183
174 s['protocol'], s['ctxoptions'] = protocolsettings(protocol)
184 s['protocol'], s['ctxoptions'] = protocolsettings(protocol)
175
185
176 # Look for fingerprints in [hostsecurity] section. Value is a list
186 # Look for fingerprints in [hostsecurity] section. Value is a list
177 # of <alg>:<fingerprint> strings.
187 # of <alg>:<fingerprint> strings.
178 fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
188 fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
179 [])
189 [])
180 for fingerprint in fingerprints:
190 for fingerprint in fingerprints:
181 if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
191 if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
182 raise error.Abort(_('invalid fingerprint for %s: %s') % (
192 raise error.Abort(_('invalid fingerprint for %s: %s') % (
183 hostname, fingerprint),
193 hostname, fingerprint),
184 hint=_('must begin with "sha1:", "sha256:", '
194 hint=_('must begin with "sha1:", "sha256:", '
185 'or "sha512:"'))
195 'or "sha512:"'))
186
196
187 alg, fingerprint = fingerprint.split(':', 1)
197 alg, fingerprint = fingerprint.split(':', 1)
188 fingerprint = fingerprint.replace(':', '').lower()
198 fingerprint = fingerprint.replace(':', '').lower()
189 s['certfingerprints'].append((alg, fingerprint))
199 s['certfingerprints'].append((alg, fingerprint))
190
200
191 # Fingerprints from [hostfingerprints] are always SHA-1.
201 # Fingerprints from [hostfingerprints] are always SHA-1.
192 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
202 for fingerprint in ui.configlist('hostfingerprints', hostname, []):
193 fingerprint = fingerprint.replace(':', '').lower()
203 fingerprint = fingerprint.replace(':', '').lower()
194 s['certfingerprints'].append(('sha1', fingerprint))
204 s['certfingerprints'].append(('sha1', fingerprint))
195 s['legacyfingerprint'] = True
205 s['legacyfingerprint'] = True
196
206
197 # If a host cert fingerprint is defined, it is the only thing that
207 # If a host cert fingerprint is defined, it is the only thing that
198 # matters. No need to validate CA certs.
208 # matters. No need to validate CA certs.
199 if s['certfingerprints']:
209 if s['certfingerprints']:
200 s['verifymode'] = ssl.CERT_NONE
210 s['verifymode'] = ssl.CERT_NONE
201 s['allowloaddefaultcerts'] = False
211 s['allowloaddefaultcerts'] = False
202
212
203 # If --insecure is used, don't take CAs into consideration.
213 # If --insecure is used, don't take CAs into consideration.
204 elif ui.insecureconnections:
214 elif ui.insecureconnections:
205 s['disablecertverification'] = True
215 s['disablecertverification'] = True
206 s['verifymode'] = ssl.CERT_NONE
216 s['verifymode'] = ssl.CERT_NONE
207 s['allowloaddefaultcerts'] = False
217 s['allowloaddefaultcerts'] = False
208
218
209 if ui.configbool('devel', 'disableloaddefaultcerts'):
219 if ui.configbool('devel', 'disableloaddefaultcerts'):
210 s['allowloaddefaultcerts'] = False
220 s['allowloaddefaultcerts'] = False
211
221
212 # If both fingerprints and a per-host ca file are specified, issue a warning
222 # If both fingerprints and a per-host ca file are specified, issue a warning
213 # because users should not be surprised about what security is or isn't
223 # because users should not be surprised about what security is or isn't
214 # being performed.
224 # being performed.
215 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname)
225 cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname)
216 if s['certfingerprints'] and cafile:
226 if s['certfingerprints'] and cafile:
217 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host '
227 ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host '
218 'fingerprints defined; using host fingerprints for '
228 'fingerprints defined; using host fingerprints for '
219 'verification)\n') % hostname)
229 'verification)\n') % hostname)
220
230
221 # Try to hook up CA certificate validation unless something above
231 # Try to hook up CA certificate validation unless something above
222 # makes it not necessary.
232 # makes it not necessary.
223 if s['verifymode'] is None:
233 if s['verifymode'] is None:
224 # Look at per-host ca file first.
234 # Look at per-host ca file first.
225 if cafile:
235 if cafile:
226 cafile = util.expandpath(cafile)
236 cafile = util.expandpath(cafile)
227 if not os.path.exists(cafile):
237 if not os.path.exists(cafile):
228 raise error.Abort(_('path specified by %s does not exist: %s') %
238 raise error.Abort(_('path specified by %s does not exist: %s') %
229 ('hostsecurity.%s:verifycertsfile' % hostname,
239 ('hostsecurity.%s:verifycertsfile' % hostname,
230 cafile))
240 cafile))
231 s['cafile'] = cafile
241 s['cafile'] = cafile
232 else:
242 else:
233 # Find global certificates file in config.
243 # Find global certificates file in config.
234 cafile = ui.config('web', 'cacerts')
244 cafile = ui.config('web', 'cacerts')
235
245
236 if cafile:
246 if cafile:
237 cafile = util.expandpath(cafile)
247 cafile = util.expandpath(cafile)
238 if not os.path.exists(cafile):
248 if not os.path.exists(cafile):
239 raise error.Abort(_('could not find web.cacerts: %s') %
249 raise error.Abort(_('could not find web.cacerts: %s') %
240 cafile)
250 cafile)
241 elif s['allowloaddefaultcerts']:
251 elif s['allowloaddefaultcerts']:
242 # CAs not defined in config. Try to find system bundles.
252 # CAs not defined in config. Try to find system bundles.
243 cafile = _defaultcacerts(ui)
253 cafile = _defaultcacerts(ui)
244 if cafile:
254 if cafile:
245 ui.debug('using %s for CA file\n' % cafile)
255 ui.debug('using %s for CA file\n' % cafile)
246
256
247 s['cafile'] = cafile
257 s['cafile'] = cafile
248
258
249 # Require certificate validation if CA certs are being loaded and
259 # Require certificate validation if CA certs are being loaded and
250 # verification hasn't been disabled above.
260 # verification hasn't been disabled above.
251 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
261 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
252 s['verifymode'] = ssl.CERT_REQUIRED
262 s['verifymode'] = ssl.CERT_REQUIRED
253 else:
263 else:
254 # At this point we don't have a fingerprint, aren't being
264 # At this point we don't have a fingerprint, aren't being
255 # explicitly insecure, and can't load CA certs. Connecting
265 # explicitly insecure, and can't load CA certs. Connecting
256 # is insecure. We allow the connection and abort during
266 # is insecure. We allow the connection and abort during
257 # validation (once we have the fingerprint to print to the
267 # validation (once we have the fingerprint to print to the
258 # user).
268 # user).
259 s['verifymode'] = ssl.CERT_NONE
269 s['verifymode'] = ssl.CERT_NONE
260
270
261 assert s['protocol'] is not None
271 assert s['protocol'] is not None
262 assert s['ctxoptions'] is not None
272 assert s['ctxoptions'] is not None
263 assert s['verifymode'] is not None
273 assert s['verifymode'] is not None
264
274
265 return s
275 return s
266
276
267 def protocolsettings(protocol):
277 def protocolsettings(protocol):
268 """Resolve the protocol and context options for a config value."""
278 """Resolve the protocol and context options for a config value."""
269 if protocol not in configprotocols:
279 if protocol not in configprotocols:
270 raise ValueError('protocol value not supported: %s' % protocol)
280 raise ValueError('protocol value not supported: %s' % protocol)
271
281
272 # Legacy ssl module only supports up to TLS 1.0. Ideally we'd use
282 # Legacy ssl module only supports up to TLS 1.0. Ideally we'd use
273 # PROTOCOL_SSLv23 and options to disable SSLv2 and SSLv3. However,
283 # PROTOCOL_SSLv23 and options to disable SSLv2 and SSLv3. However,
274 # SSLContext.options doesn't work in our implementation since we use
284 # SSLContext.options doesn't work in our implementation since we use
275 # a fake SSLContext on these Python versions.
285 # a fake SSLContext on these Python versions.
276 if not modernssl:
286 if not modernssl:
277 if protocol != 'tls1.0':
287 if protocol != 'tls1.0':
278 raise error.Abort(_('current Python does not support protocol '
288 raise error.Abort(_('current Python does not support protocol '
279 'setting %s') % protocol,
289 'setting %s') % protocol,
280 hint=_('upgrade Python or disable setting since '
290 hint=_('upgrade Python or disable setting since '
281 'only TLS 1.0 is supported'))
291 'only TLS 1.0 is supported'))
282
292
283 return ssl.PROTOCOL_TLSv1, 0
293 return ssl.PROTOCOL_TLSv1, 0
284
294
285 # WARNING: returned options don't work unless the modern ssl module
295 # WARNING: returned options don't work unless the modern ssl module
286 # is available. Be careful when adding options here.
296 # is available. Be careful when adding options here.
287
297
288 # SSLv2 and SSLv3 are broken. We ban them outright.
298 # SSLv2 and SSLv3 are broken. We ban them outright.
289 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
299 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
290
300
291 if protocol == 'tls1.0':
301 if protocol == 'tls1.0':
292 # Defaults above are to use TLS 1.0+
302 # Defaults above are to use TLS 1.0+
293 pass
303 pass
294 elif protocol == 'tls1.1':
304 elif protocol == 'tls1.1':
295 options |= ssl.OP_NO_TLSv1
305 options |= ssl.OP_NO_TLSv1
296 elif protocol == 'tls1.2':
306 elif protocol == 'tls1.2':
297 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
307 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
298 else:
308 else:
299 raise error.Abort(_('this should not happen'))
309 raise error.Abort(_('this should not happen'))
300
310
301 # Prevent CRIME.
311 # Prevent CRIME.
302 # There is no guarantee this attribute is defined on the module.
312 # There is no guarantee this attribute is defined on the module.
303 options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
313 options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
304
314
305 return ssl.PROTOCOL_SSLv23, options
315 return ssl.PROTOCOL_SSLv23, options
306
316
307 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
317 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
308 """Add SSL/TLS to a socket.
318 """Add SSL/TLS to a socket.
309
319
310 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
320 This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
311 choices based on what security options are available.
321 choices based on what security options are available.
312
322
313 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
323 In addition to the arguments supported by ``ssl.wrap_socket``, we allow
314 the following additional arguments:
324 the following additional arguments:
315
325
316 * serverhostname - The expected hostname of the remote server. If the
326 * serverhostname - The expected hostname of the remote server. If the
317 server (and client) support SNI, this tells the server which certificate
327 server (and client) support SNI, this tells the server which certificate
318 to use.
328 to use.
319 """
329 """
320 if not serverhostname:
330 if not serverhostname:
321 raise error.Abort(_('serverhostname argument is required'))
331 raise error.Abort(_('serverhostname argument is required'))
322
332
323 settings = _hostsettings(ui, serverhostname)
333 settings = _hostsettings(ui, serverhostname)
324
334
325 # We can't use ssl.create_default_context() because it calls
335 # We can't use ssl.create_default_context() because it calls
326 # load_default_certs() unless CA arguments are passed to it. We want to
336 # load_default_certs() unless CA arguments are passed to it. We want to
327 # have explicit control over CA loading because implicitly loading
337 # have explicit control over CA loading because implicitly loading
328 # CAs may undermine the user's intent. For example, a user may define a CA
338 # CAs may undermine the user's intent. For example, a user may define a CA
329 # bundle with a specific CA cert removed. If the system/default CA bundle
339 # bundle with a specific CA cert removed. If the system/default CA bundle
330 # is loaded and contains that removed CA, you've just undone the user's
340 # is loaded and contains that removed CA, you've just undone the user's
331 # choice.
341 # choice.
332 sslcontext = SSLContext(settings['protocol'])
342 sslcontext = SSLContext(settings['protocol'])
333
343
334 # This is a no-op unless using modern ssl.
344 # This is a no-op unless using modern ssl.
335 sslcontext.options |= settings['ctxoptions']
345 sslcontext.options |= settings['ctxoptions']
336
346
337 # This still works on our fake SSLContext.
347 # This still works on our fake SSLContext.
338 sslcontext.verify_mode = settings['verifymode']
348 sslcontext.verify_mode = settings['verifymode']
339
349
340 if certfile is not None:
350 if certfile is not None:
341 def password():
351 def password():
342 f = keyfile or certfile
352 f = keyfile or certfile
343 return ui.getpass(_('passphrase for %s: ') % f, '')
353 return ui.getpass(_('passphrase for %s: ') % f, '')
344 sslcontext.load_cert_chain(certfile, keyfile, password)
354 sslcontext.load_cert_chain(certfile, keyfile, password)
345
355
346 if settings['cafile'] is not None:
356 if settings['cafile'] is not None:
347 try:
357 try:
348 sslcontext.load_verify_locations(cafile=settings['cafile'])
358 sslcontext.load_verify_locations(cafile=settings['cafile'])
349 except ssl.SSLError as e:
359 except ssl.SSLError as e:
350 raise error.Abort(_('error loading CA file %s: %s') % (
360 raise error.Abort(_('error loading CA file %s: %s') % (
351 settings['cafile'], e.args[1]),
361 settings['cafile'], e.args[1]),
352 hint=_('file is empty or malformed?'))
362 hint=_('file is empty or malformed?'))
353 caloaded = True
363 caloaded = True
354 elif settings['allowloaddefaultcerts']:
364 elif settings['allowloaddefaultcerts']:
355 # This is a no-op on old Python.
365 # This is a no-op on old Python.
356 sslcontext.load_default_certs()
366 sslcontext.load_default_certs()
357 caloaded = True
367 caloaded = True
358 else:
368 else:
359 caloaded = False
369 caloaded = False
360
370
361 try:
371 try:
362 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
372 sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
363 except ssl.SSLError as e:
373 except ssl.SSLError as e:
364 # If we're doing certificate verification and no CA certs are loaded,
374 # If we're doing certificate verification and no CA certs are loaded,
365 # that is almost certainly the reason why verification failed. Provide
375 # that is almost certainly the reason why verification failed. Provide
366 # a hint to the user.
376 # a hint to the user.
367 # Only modern ssl module exposes SSLContext.get_ca_certs() so we can
377 # Only modern ssl module exposes SSLContext.get_ca_certs() so we can
368 # only show this warning if modern ssl is available.
378 # only show this warning if modern ssl is available.
369 if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
379 if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
370 modernssl and not sslcontext.get_ca_certs()):
380 modernssl and not sslcontext.get_ca_certs()):
371 ui.warn(_('(an attempt was made to load CA certificates but none '
381 ui.warn(_('(an attempt was made to load CA certificates but none '
372 'were loaded; see '
382 'were loaded; see '
373 'https://mercurial-scm.org/wiki/SecureConnections for '
383 'https://mercurial-scm.org/wiki/SecureConnections for '
374 'how to configure Mercurial to avoid this error)\n'))
384 'how to configure Mercurial to avoid this error)\n'))
375 # Try to print more helpful error messages for known failures.
385 # Try to print more helpful error messages for known failures.
376 if util.safehasattr(e, 'reason'):
386 if util.safehasattr(e, 'reason'):
377 if e.reason == 'UNSUPPORTED_PROTOCOL':
387 if e.reason == 'UNSUPPORTED_PROTOCOL':
378 ui.warn(_('(could not negotiate a common protocol; see '
388 ui.warn(_('(could not negotiate a common protocol; see '
379 'https://mercurial-scm.org/wiki/SecureConnections '
389 'https://mercurial-scm.org/wiki/SecureConnections '
380 'for how to configure Mercurial to avoid this '
390 'for how to configure Mercurial to avoid this '
381 'error)\n'))
391 'error)\n'))
382 raise
392 raise
383
393
384 # check if wrap_socket failed silently because socket had been
394 # check if wrap_socket failed silently because socket had been
385 # closed
395 # closed
386 # - see http://bugs.python.org/issue13721
396 # - see http://bugs.python.org/issue13721
387 if not sslsocket.cipher():
397 if not sslsocket.cipher():
388 raise error.Abort(_('ssl connection failed'))
398 raise error.Abort(_('ssl connection failed'))
389
399
390 sslsocket._hgstate = {
400 sslsocket._hgstate = {
391 'caloaded': caloaded,
401 'caloaded': caloaded,
392 'hostname': serverhostname,
402 'hostname': serverhostname,
393 'settings': settings,
403 'settings': settings,
394 'ui': ui,
404 'ui': ui,
395 }
405 }
396
406
397 return sslsocket
407 return sslsocket
398
408
399 def wrapserversocket(sock, ui, certfile=None, keyfile=None, cafile=None,
409 def wrapserversocket(sock, ui, certfile=None, keyfile=None, cafile=None,
400 requireclientcert=False):
410 requireclientcert=False):
401 """Wrap a socket for use by servers.
411 """Wrap a socket for use by servers.
402
412
403 ``certfile`` and ``keyfile`` specify the files containing the certificate's
413 ``certfile`` and ``keyfile`` specify the files containing the certificate's
404 public and private keys, respectively. Both keys can be defined in the same
414 public and private keys, respectively. Both keys can be defined in the same
405 file via ``certfile`` (the private key must come first in the file).
415 file via ``certfile`` (the private key must come first in the file).
406
416
407 ``cafile`` defines the path to certificate authorities.
417 ``cafile`` defines the path to certificate authorities.
408
418
409 ``requireclientcert`` specifies whether to require client certificates.
419 ``requireclientcert`` specifies whether to require client certificates.
410
420
411 Typically ``cafile`` is only defined if ``requireclientcert`` is true.
421 Typically ``cafile`` is only defined if ``requireclientcert`` is true.
412 """
422 """
413 protocol, options = protocolsettings('tls1.0')
423 protocol, options = protocolsettings('tls1.0')
414
424
415 # This config option is intended for use in tests only. It is a giant
425 # This config option is intended for use in tests only. It is a giant
416 # footgun to kill security. Don't define it.
426 # footgun to kill security. Don't define it.
417 exactprotocol = ui.config('devel', 'serverexactprotocol')
427 exactprotocol = ui.config('devel', 'serverexactprotocol')
418 if exactprotocol == 'tls1.0':
428 if exactprotocol == 'tls1.0':
419 protocol = ssl.PROTOCOL_TLSv1
429 protocol = ssl.PROTOCOL_TLSv1
420 elif exactprotocol == 'tls1.1':
430 elif exactprotocol == 'tls1.1':
421 protocol = ssl.PROTOCOL_TLSv1_1
431 protocol = ssl.PROTOCOL_TLSv1_1
422 elif exactprotocol == 'tls1.2':
432 elif exactprotocol == 'tls1.2':
423 protocol = ssl.PROTOCOL_TLSv1_2
433 protocol = ssl.PROTOCOL_TLSv1_2
424 elif exactprotocol:
434 elif exactprotocol:
425 raise error.Abort(_('invalid value for serverexactprotocol: %s') %
435 raise error.Abort(_('invalid value for serverexactprotocol: %s') %
426 exactprotocol)
436 exactprotocol)
427
437
428 if modernssl:
438 if modernssl:
429 # We /could/ use create_default_context() here since it doesn't load
439 # We /could/ use create_default_context() here since it doesn't load
430 # CAs when configured for client auth. However, it is hard-coded to
440 # CAs when configured for client auth. However, it is hard-coded to
431 # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
441 # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
432 sslcontext = SSLContext(protocol)
442 sslcontext = SSLContext(protocol)
433 sslcontext.options |= options
443 sslcontext.options |= options
434
444
435 # Improve forward secrecy.
445 # Improve forward secrecy.
436 sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)
446 sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)
437 sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0)
447 sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0)
438
448
439 # Use the list of more secure ciphers if found in the ssl module.
449 # Use the list of more secure ciphers if found in the ssl module.
440 if util.safehasattr(ssl, '_RESTRICTED_SERVER_CIPHERS'):
450 if util.safehasattr(ssl, '_RESTRICTED_SERVER_CIPHERS'):
441 sslcontext.options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0)
451 sslcontext.options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0)
442 sslcontext.set_ciphers(ssl._RESTRICTED_SERVER_CIPHERS)
452 sslcontext.set_ciphers(ssl._RESTRICTED_SERVER_CIPHERS)
443 else:
453 else:
444 sslcontext = SSLContext(ssl.PROTOCOL_TLSv1)
454 sslcontext = SSLContext(ssl.PROTOCOL_TLSv1)
445
455
446 if requireclientcert:
456 if requireclientcert:
447 sslcontext.verify_mode = ssl.CERT_REQUIRED
457 sslcontext.verify_mode = ssl.CERT_REQUIRED
448 else:
458 else:
449 sslcontext.verify_mode = ssl.CERT_NONE
459 sslcontext.verify_mode = ssl.CERT_NONE
450
460
451 if certfile or keyfile:
461 if certfile or keyfile:
452 sslcontext.load_cert_chain(certfile=certfile, keyfile=keyfile)
462 sslcontext.load_cert_chain(certfile=certfile, keyfile=keyfile)
453
463
454 if cafile:
464 if cafile:
455 sslcontext.load_verify_locations(cafile=cafile)
465 sslcontext.load_verify_locations(cafile=cafile)
456
466
457 return sslcontext.wrap_socket(sock, server_side=True)
467 return sslcontext.wrap_socket(sock, server_side=True)
458
468
459 class wildcarderror(Exception):
469 class wildcarderror(Exception):
460 """Represents an error parsing wildcards in DNS name."""
470 """Represents an error parsing wildcards in DNS name."""
461
471
462 def _dnsnamematch(dn, hostname, maxwildcards=1):
472 def _dnsnamematch(dn, hostname, maxwildcards=1):
463 """Match DNS names according RFC 6125 section 6.4.3.
473 """Match DNS names according RFC 6125 section 6.4.3.
464
474
465 This code is effectively copied from CPython's ssl._dnsname_match.
475 This code is effectively copied from CPython's ssl._dnsname_match.
466
476
467 Returns a bool indicating whether the expected hostname matches
477 Returns a bool indicating whether the expected hostname matches
468 the value in ``dn``.
478 the value in ``dn``.
469 """
479 """
470 pats = []
480 pats = []
471 if not dn:
481 if not dn:
472 return False
482 return False
473
483
474 pieces = dn.split(r'.')
484 pieces = dn.split(r'.')
475 leftmost = pieces[0]
485 leftmost = pieces[0]
476 remainder = pieces[1:]
486 remainder = pieces[1:]
477 wildcards = leftmost.count('*')
487 wildcards = leftmost.count('*')
478 if wildcards > maxwildcards:
488 if wildcards > maxwildcards:
479 raise wildcarderror(
489 raise wildcarderror(
480 _('too many wildcards in certificate DNS name: %s') % dn)
490 _('too many wildcards in certificate DNS name: %s') % dn)
481
491
482 # speed up common case w/o wildcards
492 # speed up common case w/o wildcards
483 if not wildcards:
493 if not wildcards:
484 return dn.lower() == hostname.lower()
494 return dn.lower() == hostname.lower()
485
495
486 # RFC 6125, section 6.4.3, subitem 1.
496 # RFC 6125, section 6.4.3, subitem 1.
487 # The client SHOULD NOT attempt to match a presented identifier in which
497 # The client SHOULD NOT attempt to match a presented identifier in which
488 # the wildcard character comprises a label other than the left-most label.
498 # the wildcard character comprises a label other than the left-most label.
489 if leftmost == '*':
499 if leftmost == '*':
490 # When '*' is a fragment by itself, it matches a non-empty dotless
500 # When '*' is a fragment by itself, it matches a non-empty dotless
491 # fragment.
501 # fragment.
492 pats.append('[^.]+')
502 pats.append('[^.]+')
493 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
503 elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
494 # RFC 6125, section 6.4.3, subitem 3.
504 # RFC 6125, section 6.4.3, subitem 3.
495 # The client SHOULD NOT attempt to match a presented identifier
505 # The client SHOULD NOT attempt to match a presented identifier
496 # where the wildcard character is embedded within an A-label or
506 # where the wildcard character is embedded within an A-label or
497 # U-label of an internationalized domain name.
507 # U-label of an internationalized domain name.
498 pats.append(re.escape(leftmost))
508 pats.append(re.escape(leftmost))
499 else:
509 else:
500 # Otherwise, '*' matches any dotless string, e.g. www*
510 # Otherwise, '*' matches any dotless string, e.g. www*
501 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
511 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
502
512
503 # add the remaining fragments, ignore any wildcards
513 # add the remaining fragments, ignore any wildcards
504 for frag in remainder:
514 for frag in remainder:
505 pats.append(re.escape(frag))
515 pats.append(re.escape(frag))
506
516
507 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
517 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
508 return pat.match(hostname) is not None
518 return pat.match(hostname) is not None
509
519
510 def _verifycert(cert, hostname):
520 def _verifycert(cert, hostname):
511 '''Verify that cert (in socket.getpeercert() format) matches hostname.
521 '''Verify that cert (in socket.getpeercert() format) matches hostname.
512 CRLs is not handled.
522 CRLs is not handled.
513
523
514 Returns error message if any problems are found and None on success.
524 Returns error message if any problems are found and None on success.
515 '''
525 '''
516 if not cert:
526 if not cert:
517 return _('no certificate received')
527 return _('no certificate received')
518
528
519 dnsnames = []
529 dnsnames = []
520 san = cert.get('subjectAltName', [])
530 san = cert.get('subjectAltName', [])
521 for key, value in san:
531 for key, value in san:
522 if key == 'DNS':
532 if key == 'DNS':
523 try:
533 try:
524 if _dnsnamematch(value, hostname):
534 if _dnsnamematch(value, hostname):
525 return
535 return
526 except wildcarderror as e:
536 except wildcarderror as e:
527 return e.args[0]
537 return e.args[0]
528
538
529 dnsnames.append(value)
539 dnsnames.append(value)
530
540
531 if not dnsnames:
541 if not dnsnames:
532 # The subject is only checked when there is no DNS in subjectAltName.
542 # The subject is only checked when there is no DNS in subjectAltName.
533 for sub in cert.get('subject', []):
543 for sub in cert.get('subject', []):
534 for key, value in sub:
544 for key, value in sub:
535 # According to RFC 2818 the most specific Common Name must
545 # According to RFC 2818 the most specific Common Name must
536 # be used.
546 # be used.
537 if key == 'commonName':
547 if key == 'commonName':
538 # 'subject' entries are unicide.
548 # 'subject' entries are unicide.
539 try:
549 try:
540 value = value.encode('ascii')
550 value = value.encode('ascii')
541 except UnicodeEncodeError:
551 except UnicodeEncodeError:
542 return _('IDN in certificate not supported')
552 return _('IDN in certificate not supported')
543
553
544 try:
554 try:
545 if _dnsnamematch(value, hostname):
555 if _dnsnamematch(value, hostname):
546 return
556 return
547 except wildcarderror as e:
557 except wildcarderror as e:
548 return e.args[0]
558 return e.args[0]
549
559
550 dnsnames.append(value)
560 dnsnames.append(value)
551
561
552 if len(dnsnames) > 1:
562 if len(dnsnames) > 1:
553 return _('certificate is for %s') % ', '.join(dnsnames)
563 return _('certificate is for %s') % ', '.join(dnsnames)
554 elif len(dnsnames) == 1:
564 elif len(dnsnames) == 1:
555 return _('certificate is for %s') % dnsnames[0]
565 return _('certificate is for %s') % dnsnames[0]
556 else:
566 else:
557 return _('no commonName or subjectAltName found in certificate')
567 return _('no commonName or subjectAltName found in certificate')
558
568
559 def _plainapplepython():
569 def _plainapplepython():
560 """return true if this seems to be a pure Apple Python that
570 """return true if this seems to be a pure Apple Python that
561 * is unfrozen and presumably has the whole mercurial module in the file
571 * is unfrozen and presumably has the whole mercurial module in the file
562 system
572 system
563 * presumably is an Apple Python that uses Apple OpenSSL which has patches
573 * presumably is an Apple Python that uses Apple OpenSSL which has patches
564 for using system certificate store CAs in addition to the provided
574 for using system certificate store CAs in addition to the provided
565 cacerts file
575 cacerts file
566 """
576 """
567 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
577 if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
568 return False
578 return False
569 exe = os.path.realpath(sys.executable).lower()
579 exe = os.path.realpath(sys.executable).lower()
570 return (exe.startswith('/usr/bin/python') or
580 return (exe.startswith('/usr/bin/python') or
571 exe.startswith('/system/library/frameworks/python.framework/'))
581 exe.startswith('/system/library/frameworks/python.framework/'))
572
582
573 _systemcacertpaths = [
583 _systemcacertpaths = [
574 # RHEL, CentOS, and Fedora
584 # RHEL, CentOS, and Fedora
575 '/etc/pki/tls/certs/ca-bundle.trust.crt',
585 '/etc/pki/tls/certs/ca-bundle.trust.crt',
576 # Debian, Ubuntu, Gentoo
586 # Debian, Ubuntu, Gentoo
577 '/etc/ssl/certs/ca-certificates.crt',
587 '/etc/ssl/certs/ca-certificates.crt',
578 ]
588 ]
579
589
580 def _defaultcacerts(ui):
590 def _defaultcacerts(ui):
581 """return path to default CA certificates or None.
591 """return path to default CA certificates or None.
582
592
583 It is assumed this function is called when the returned certificates
593 It is assumed this function is called when the returned certificates
584 file will actually be used to validate connections. Therefore this
594 file will actually be used to validate connections. Therefore this
585 function may print warnings or debug messages assuming this usage.
595 function may print warnings or debug messages assuming this usage.
586
596
587 We don't print a message when the Python is able to load default
597 We don't print a message when the Python is able to load default
588 CA certs because this scenario is detected at socket connect time.
598 CA certs because this scenario is detected at socket connect time.
589 """
599 """
590 # The "certifi" Python package provides certificates. If it is installed,
600 # The "certifi" Python package provides certificates. If it is installed,
591 # assume the user intends it to be used and use it.
601 # assume the user intends it to be used and use it.
592 try:
602 try:
593 import certifi
603 import certifi
594 certs = certifi.where()
604 certs = certifi.where()
595 ui.debug('using ca certificates from certifi\n')
605 ui.debug('using ca certificates from certifi\n')
596 return certs
606 return certs
597 except ImportError:
607 except ImportError:
598 pass
608 pass
599
609
600 # On Windows, only the modern ssl module is capable of loading the system
610 # On Windows, only the modern ssl module is capable of loading the system
601 # CA certificates. If we're not capable of doing that, emit a warning
611 # CA certificates. If we're not capable of doing that, emit a warning
602 # because we'll get a certificate verification error later and the lack
612 # because we'll get a certificate verification error later and the lack
603 # of loaded CA certificates will be the reason why.
613 # of loaded CA certificates will be the reason why.
604 # Assertion: this code is only called if certificates are being verified.
614 # Assertion: this code is only called if certificates are being verified.
605 if os.name == 'nt':
615 if os.name == 'nt':
606 if not _canloaddefaultcerts:
616 if not _canloaddefaultcerts:
607 ui.warn(_('(unable to load Windows CA certificates; see '
617 ui.warn(_('(unable to load Windows CA certificates; see '
608 'https://mercurial-scm.org/wiki/SecureConnections for '
618 'https://mercurial-scm.org/wiki/SecureConnections for '
609 'how to configure Mercurial to avoid this message)\n'))
619 'how to configure Mercurial to avoid this message)\n'))
610
620
611 return None
621 return None
612
622
613 # Apple's OpenSSL has patches that allow a specially constructed certificate
623 # Apple's OpenSSL has patches that allow a specially constructed certificate
614 # to load the system CA store. If we're running on Apple Python, use this
624 # to load the system CA store. If we're running on Apple Python, use this
615 # trick.
625 # trick.
616 if _plainapplepython():
626 if _plainapplepython():
617 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
627 dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem')
618 if os.path.exists(dummycert):
628 if os.path.exists(dummycert):
619 return dummycert
629 return dummycert
620
630
621 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
631 # The Apple OpenSSL trick isn't available to us. If Python isn't able to
622 # load system certs, we're out of luck.
632 # load system certs, we're out of luck.
623 if sys.platform == 'darwin':
633 if sys.platform == 'darwin':
624 # FUTURE Consider looking for Homebrew or MacPorts installed certs
634 # FUTURE Consider looking for Homebrew or MacPorts installed certs
625 # files. Also consider exporting the keychain certs to a file during
635 # files. Also consider exporting the keychain certs to a file during
626 # Mercurial install.
636 # Mercurial install.
627 if not _canloaddefaultcerts:
637 if not _canloaddefaultcerts:
628 ui.warn(_('(unable to load CA certificates; see '
638 ui.warn(_('(unable to load CA certificates; see '
629 'https://mercurial-scm.org/wiki/SecureConnections for '
639 'https://mercurial-scm.org/wiki/SecureConnections for '
630 'how to configure Mercurial to avoid this message)\n'))
640 'how to configure Mercurial to avoid this message)\n'))
631 return None
641 return None
632
642
633 # / is writable on Windows. Out of an abundance of caution make sure
643 # / is writable on Windows. Out of an abundance of caution make sure
634 # we're not on Windows because paths from _systemcacerts could be installed
644 # we're not on Windows because paths from _systemcacerts could be installed
635 # by non-admin users.
645 # by non-admin users.
636 assert os.name != 'nt'
646 assert os.name != 'nt'
637
647
638 # Try to find CA certificates in well-known locations. We print a warning
648 # Try to find CA certificates in well-known locations. We print a warning
639 # when using a found file because we don't want too much silent magic
649 # when using a found file because we don't want too much silent magic
640 # for security settings. The expectation is that proper Mercurial
650 # for security settings. The expectation is that proper Mercurial
641 # installs will have the CA certs path defined at install time and the
651 # installs will have the CA certs path defined at install time and the
642 # installer/packager will make an appropriate decision on the user's
652 # installer/packager will make an appropriate decision on the user's
643 # behalf. We only get here and perform this setting as a feature of
653 # behalf. We only get here and perform this setting as a feature of
644 # last resort.
654 # last resort.
645 if not _canloaddefaultcerts:
655 if not _canloaddefaultcerts:
646 for path in _systemcacertpaths:
656 for path in _systemcacertpaths:
647 if os.path.isfile(path):
657 if os.path.isfile(path):
648 ui.warn(_('(using CA certificates from %s; if you see this '
658 ui.warn(_('(using CA certificates from %s; if you see this '
649 'message, your Mercurial install is not properly '
659 'message, your Mercurial install is not properly '
650 'configured; see '
660 'configured; see '
651 'https://mercurial-scm.org/wiki/SecureConnections '
661 'https://mercurial-scm.org/wiki/SecureConnections '
652 'for how to configure Mercurial to avoid this '
662 'for how to configure Mercurial to avoid this '
653 'message)\n') % path)
663 'message)\n') % path)
654 return path
664 return path
655
665
656 ui.warn(_('(unable to load CA certificates; see '
666 ui.warn(_('(unable to load CA certificates; see '
657 'https://mercurial-scm.org/wiki/SecureConnections for '
667 'https://mercurial-scm.org/wiki/SecureConnections for '
658 'how to configure Mercurial to avoid this message)\n'))
668 'how to configure Mercurial to avoid this message)\n'))
659
669
660 return None
670 return None
661
671
662 def validatesocket(sock):
672 def validatesocket(sock):
663 """Validate a socket meets security requiremnets.
673 """Validate a socket meets security requiremnets.
664
674
665 The passed socket must have been created with ``wrapsocket()``.
675 The passed socket must have been created with ``wrapsocket()``.
666 """
676 """
667 host = sock._hgstate['hostname']
677 host = sock._hgstate['hostname']
668 ui = sock._hgstate['ui']
678 ui = sock._hgstate['ui']
669 settings = sock._hgstate['settings']
679 settings = sock._hgstate['settings']
670
680
671 try:
681 try:
672 peercert = sock.getpeercert(True)
682 peercert = sock.getpeercert(True)
673 peercert2 = sock.getpeercert()
683 peercert2 = sock.getpeercert()
674 except AttributeError:
684 except AttributeError:
675 raise error.Abort(_('%s ssl connection error') % host)
685 raise error.Abort(_('%s ssl connection error') % host)
676
686
677 if not peercert:
687 if not peercert:
678 raise error.Abort(_('%s certificate error: '
688 raise error.Abort(_('%s certificate error: '
679 'no certificate received') % host)
689 'no certificate received') % host)
680
690
681 if settings['disablecertverification']:
691 if settings['disablecertverification']:
682 # We don't print the certificate fingerprint because it shouldn't
692 # We don't print the certificate fingerprint because it shouldn't
683 # be necessary: if the user requested certificate verification be
693 # be necessary: if the user requested certificate verification be
684 # disabled, they presumably already saw a message about the inability
694 # disabled, they presumably already saw a message about the inability
685 # to verify the certificate and this message would have printed the
695 # to verify the certificate and this message would have printed the
686 # fingerprint. So printing the fingerprint here adds little to no
696 # fingerprint. So printing the fingerprint here adds little to no
687 # value.
697 # value.
688 ui.warn(_('warning: connection security to %s is disabled per current '
698 ui.warn(_('warning: connection security to %s is disabled per current '
689 'settings; communication is susceptible to eavesdropping '
699 'settings; communication is susceptible to eavesdropping '
690 'and tampering\n') % host)
700 'and tampering\n') % host)
691 return
701 return
692
702
693 # If a certificate fingerprint is pinned, use it and only it to
703 # If a certificate fingerprint is pinned, use it and only it to
694 # validate the remote cert.
704 # validate the remote cert.
695 peerfingerprints = {
705 peerfingerprints = {
696 'sha1': hashlib.sha1(peercert).hexdigest(),
706 'sha1': hashlib.sha1(peercert).hexdigest(),
697 'sha256': hashlib.sha256(peercert).hexdigest(),
707 'sha256': hashlib.sha256(peercert).hexdigest(),
698 'sha512': hashlib.sha512(peercert).hexdigest(),
708 'sha512': hashlib.sha512(peercert).hexdigest(),
699 }
709 }
700
710
701 def fmtfingerprint(s):
711 def fmtfingerprint(s):
702 return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
712 return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
703
713
704 nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
714 nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
705
715
706 if settings['certfingerprints']:
716 if settings['certfingerprints']:
707 for hash, fingerprint in settings['certfingerprints']:
717 for hash, fingerprint in settings['certfingerprints']:
708 if peerfingerprints[hash].lower() == fingerprint:
718 if peerfingerprints[hash].lower() == fingerprint:
709 ui.debug('%s certificate matched fingerprint %s:%s\n' %
719 ui.debug('%s certificate matched fingerprint %s:%s\n' %
710 (host, hash, fmtfingerprint(fingerprint)))
720 (host, hash, fmtfingerprint(fingerprint)))
711 return
721 return
712
722
713 # Pinned fingerprint didn't match. This is a fatal error.
723 # Pinned fingerprint didn't match. This is a fatal error.
714 if settings['legacyfingerprint']:
724 if settings['legacyfingerprint']:
715 section = 'hostfingerprint'
725 section = 'hostfingerprint'
716 nice = fmtfingerprint(peerfingerprints['sha1'])
726 nice = fmtfingerprint(peerfingerprints['sha1'])
717 else:
727 else:
718 section = 'hostsecurity'
728 section = 'hostsecurity'
719 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
729 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
720 raise error.Abort(_('certificate for %s has unexpected '
730 raise error.Abort(_('certificate for %s has unexpected '
721 'fingerprint %s') % (host, nice),
731 'fingerprint %s') % (host, nice),
722 hint=_('check %s configuration') % section)
732 hint=_('check %s configuration') % section)
723
733
724 # Security is enabled but no CAs are loaded. We can't establish trust
734 # Security is enabled but no CAs are loaded. We can't establish trust
725 # for the cert so abort.
735 # for the cert so abort.
726 if not sock._hgstate['caloaded']:
736 if not sock._hgstate['caloaded']:
727 raise error.Abort(
737 raise error.Abort(
728 _('unable to verify security of %s (no loaded CA certificates); '
738 _('unable to verify security of %s (no loaded CA certificates); '
729 'refusing to connect') % host,
739 'refusing to connect') % host,
730 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
740 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
731 'how to configure Mercurial to avoid this error or set '
741 'how to configure Mercurial to avoid this error or set '
732 'hostsecurity.%s:fingerprints=%s to trust this server') %
742 'hostsecurity.%s:fingerprints=%s to trust this server') %
733 (host, nicefingerprint))
743 (host, nicefingerprint))
734
744
735 msg = _verifycert(peercert2, host)
745 msg = _verifycert(peercert2, host)
736 if msg:
746 if msg:
737 raise error.Abort(_('%s certificate error: %s') % (host, msg),
747 raise error.Abort(_('%s certificate error: %s') % (host, msg),
738 hint=_('set hostsecurity.%s:certfingerprints=%s '
748 hint=_('set hostsecurity.%s:certfingerprints=%s '
739 'config setting or use --insecure to connect '
749 'config setting or use --insecure to connect '
740 'insecurely') %
750 'insecurely') %
741 (host, nicefingerprint))
751 (host, nicefingerprint))
@@ -1,508 +1,555 b''
1 #require serve ssl
1 #require serve ssl
2
2
3 Proper https client requires the built-in ssl from Python 2.6.
3 Proper https client requires the built-in ssl from Python 2.6.
4
4
5 Make server certificates:
5 Make server certificates:
6
6
7 $ CERTSDIR="$TESTDIR/sslcerts"
7 $ CERTSDIR="$TESTDIR/sslcerts"
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
9 $ PRIV=`pwd`/server.pem
9 $ PRIV=`pwd`/server.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
12
12
13 $ hg init test
13 $ hg init test
14 $ cd test
14 $ cd test
15 $ echo foo>foo
15 $ echo foo>foo
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
17 $ echo foo>foo.d/foo
17 $ echo foo>foo.d/foo
18 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/bAr.hg.d/BaR
19 $ echo bar>foo.d/baR.d.hg/bAR
19 $ echo bar>foo.d/baR.d.hg/bAR
20 $ hg commit -A -m 1
20 $ hg commit -A -m 1
21 adding foo
21 adding foo
22 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/bAr.hg.d/BaR
23 adding foo.d/baR.d.hg/bAR
23 adding foo.d/baR.d.hg/bAR
24 adding foo.d/foo
24 adding foo.d/foo
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
26 $ cat ../hg0.pid >> $DAEMON_PIDS
26 $ cat ../hg0.pid >> $DAEMON_PIDS
27
27
28 cacert not found
28 cacert not found
29
29
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
31 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
31 abort: could not find web.cacerts: no-such.pem
32 abort: could not find web.cacerts: no-such.pem
32 [255]
33 [255]
33
34
34 Test server address cannot be reused
35 Test server address cannot be reused
35
36
36 #if windows
37 #if windows
37 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
38 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
38 abort: cannot start server at ':$HGPORT':
39 abort: cannot start server at ':$HGPORT':
39 [255]
40 [255]
40 #else
41 #else
41 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
42 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
42 abort: cannot start server at ':$HGPORT': Address already in use
43 abort: cannot start server at ':$HGPORT': Address already in use
43 [255]
44 [255]
44 #endif
45 #endif
45 $ cd ..
46 $ cd ..
46
47
47 Our test cert is not signed by a trusted CA. It should fail to verify if
48 Our test cert is not signed by a trusted CA. It should fail to verify if
48 we are able to load CA certs.
49 we are able to load CA certs.
49
50
50 #if sslcontext defaultcacerts no-defaultcacertsloaded
51 #if sslcontext defaultcacerts no-defaultcacertsloaded
51 $ hg clone https://localhost:$HGPORT/ copy-pull
52 $ hg clone https://localhost:$HGPORT/ copy-pull
52 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
53 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
53 abort: error: *certificate verify failed* (glob)
54 abort: error: *certificate verify failed* (glob)
54 [255]
55 [255]
55 #endif
56 #endif
56
57
57 #if no-sslcontext defaultcacerts
58 #if no-sslcontext defaultcacerts
58 $ hg clone https://localhost:$HGPORT/ copy-pull
59 $ hg clone https://localhost:$HGPORT/ copy-pull
60 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
59 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
61 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
60 abort: error: *certificate verify failed* (glob)
62 abort: error: *certificate verify failed* (glob)
61 [255]
63 [255]
62 #endif
64 #endif
63
65
64 #if no-sslcontext windows
66 #if no-sslcontext windows
65 $ hg clone https://localhost:$HGPORT/ copy-pull
67 $ hg clone https://localhost:$HGPORT/ copy-pull
68 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
66 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
69 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
67 abort: error: *certificate verify failed* (glob)
70 abort: error: *certificate verify failed* (glob)
68 [255]
71 [255]
69 #endif
72 #endif
70
73
71 #if no-sslcontext osx
74 #if no-sslcontext osx
72 $ hg clone https://localhost:$HGPORT/ copy-pull
75 $ hg clone https://localhost:$HGPORT/ copy-pull
76 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
73 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
77 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
74 abort: localhost certificate error: no certificate received
78 abort: localhost certificate error: no certificate received
75 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
79 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
76 [255]
80 [255]
77 #endif
81 #endif
78
82
79 #if defaultcacertsloaded
83 #if defaultcacertsloaded
80 $ hg clone https://localhost:$HGPORT/ copy-pull
84 $ hg clone https://localhost:$HGPORT/ copy-pull
81 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
85 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
82 abort: error: *certificate verify failed* (glob)
86 abort: error: *certificate verify failed* (glob)
83 [255]
87 [255]
84 #endif
88 #endif
85
89
86 #if no-defaultcacerts
90 #if no-defaultcacerts
87 $ hg clone https://localhost:$HGPORT/ copy-pull
91 $ hg clone https://localhost:$HGPORT/ copy-pull
92 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
88 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
93 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
89 abort: localhost certificate error: no certificate received
94 abort: localhost certificate error: no certificate received
90 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
95 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
91 [255]
96 [255]
92 #endif
97 #endif
93
98
94 Specifying a per-host certificate file that doesn't exist will abort
99 Specifying a per-host certificate file that doesn't exist will abort
95
100
96 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
101 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
102 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
97 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist
103 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist
98 [255]
104 [255]
99
105
100 A malformed per-host certificate file will raise an error
106 A malformed per-host certificate file will raise an error
101
107
102 $ echo baddata > badca.pem
108 $ echo baddata > badca.pem
103 #if sslcontext
109 #if sslcontext
104 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
110 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
105 abort: error loading CA file badca.pem: * (glob)
111 abort: error loading CA file badca.pem: * (glob)
106 (file is empty or malformed?)
112 (file is empty or malformed?)
107 [255]
113 [255]
108 #else
114 #else
109 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
115 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
116 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
110 abort: error: * (glob)
117 abort: error: * (glob)
111 [255]
118 [255]
112 #endif
119 #endif
113
120
114 A per-host certificate mismatching the server will fail verification
121 A per-host certificate mismatching the server will fail verification
115
122
116 (modern ssl is able to discern whether the loaded cert is a CA cert)
123 (modern ssl is able to discern whether the loaded cert is a CA cert)
117 #if sslcontext
124 #if sslcontext
118 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
125 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
119 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
126 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
120 abort: error: *certificate verify failed* (glob)
127 abort: error: *certificate verify failed* (glob)
121 [255]
128 [255]
122 #else
129 #else
123 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
130 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
131 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
124 abort: error: *certificate verify failed* (glob)
132 abort: error: *certificate verify failed* (glob)
125 [255]
133 [255]
126 #endif
134 #endif
127
135
128 A per-host certificate matching the server's cert will be accepted
136 A per-host certificate matching the server's cert will be accepted
129
137
130 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
138 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
139 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
131 requesting all changes
140 requesting all changes
132 adding changesets
141 adding changesets
133 adding manifests
142 adding manifests
134 adding file changes
143 adding file changes
135 added 1 changesets with 4 changes to 4 files
144 added 1 changesets with 4 changes to 4 files
136
145
137 A per-host certificate with multiple certs and one matching will be accepted
146 A per-host certificate with multiple certs and one matching will be accepted
138
147
139 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
148 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
140 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
149 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
150 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
141 requesting all changes
151 requesting all changes
142 adding changesets
152 adding changesets
143 adding manifests
153 adding manifests
144 adding file changes
154 adding file changes
145 added 1 changesets with 4 changes to 4 files
155 added 1 changesets with 4 changes to 4 files
146
156
147 Defining both per-host certificate and a fingerprint will print a warning
157 Defining both per-host certificate and a fingerprint will print a warning
148
158
149 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
159 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
160 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
150 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
161 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
151 requesting all changes
162 requesting all changes
152 adding changesets
163 adding changesets
153 adding manifests
164 adding manifests
154 adding file changes
165 adding file changes
155 added 1 changesets with 4 changes to 4 files
166 added 1 changesets with 4 changes to 4 files
156
167
157 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
168 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
158
169
159 Inability to verify peer certificate will result in abort
170 Inability to verify peer certificate will result in abort
160
171
161 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
172 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
173 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
162 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
174 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
163 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
175 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
164 [255]
176 [255]
165
177
166 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
178 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
179 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
167 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
180 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
168 requesting all changes
181 requesting all changes
169 adding changesets
182 adding changesets
170 adding manifests
183 adding manifests
171 adding file changes
184 adding file changes
172 added 1 changesets with 4 changes to 4 files
185 added 1 changesets with 4 changes to 4 files
173 updating to branch default
186 updating to branch default
174 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ hg verify -R copy-pull
188 $ hg verify -R copy-pull
176 checking changesets
189 checking changesets
177 checking manifests
190 checking manifests
178 crosschecking files in changesets and manifests
191 crosschecking files in changesets and manifests
179 checking files
192 checking files
180 4 files, 1 changesets, 4 total revisions
193 4 files, 1 changesets, 4 total revisions
181 $ cd test
194 $ cd test
182 $ echo bar > bar
195 $ echo bar > bar
183 $ hg commit -A -d '1 0' -m 2
196 $ hg commit -A -d '1 0' -m 2
184 adding bar
197 adding bar
185 $ cd ..
198 $ cd ..
186
199
187 pull without cacert
200 pull without cacert
188
201
189 $ cd copy-pull
202 $ cd copy-pull
190 $ echo '[hooks]' >> .hg/hgrc
203 $ echo '[hooks]' >> .hg/hgrc
191 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
204 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
192 $ hg pull $DISABLECACERTS
205 $ hg pull $DISABLECACERTS
193 pulling from https://localhost:$HGPORT/
206 pulling from https://localhost:$HGPORT/
207 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
194 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
208 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
195 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
209 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
196 [255]
210 [255]
197
211
198 $ hg pull --insecure
212 $ hg pull --insecure
199 pulling from https://localhost:$HGPORT/
213 pulling from https://localhost:$HGPORT/
214 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
200 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
215 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
201 searching for changes
216 searching for changes
202 adding changesets
217 adding changesets
203 adding manifests
218 adding manifests
204 adding file changes
219 adding file changes
205 added 1 changesets with 1 changes to 1 files
220 added 1 changesets with 1 changes to 1 files
206 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=https://localhost:$HGPORT/ (glob)
221 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=https://localhost:$HGPORT/ (glob)
207 (run 'hg update' to get a working copy)
222 (run 'hg update' to get a working copy)
208 $ cd ..
223 $ cd ..
209
224
210 cacert configured in local repo
225 cacert configured in local repo
211
226
212 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
227 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
213 $ echo "[web]" >> copy-pull/.hg/hgrc
228 $ echo "[web]" >> copy-pull/.hg/hgrc
214 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
229 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
215 $ hg -R copy-pull pull --traceback
230 $ hg -R copy-pull pull --traceback
216 pulling from https://localhost:$HGPORT/
231 pulling from https://localhost:$HGPORT/
232 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
217 searching for changes
233 searching for changes
218 no changes found
234 no changes found
219 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
235 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
220
236
221 cacert configured globally, also testing expansion of environment
237 cacert configured globally, also testing expansion of environment
222 variables in the filename
238 variables in the filename
223
239
224 $ echo "[web]" >> $HGRCPATH
240 $ echo "[web]" >> $HGRCPATH
225 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
241 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
226 $ P="$CERTSDIR" hg -R copy-pull pull
242 $ P="$CERTSDIR" hg -R copy-pull pull
227 pulling from https://localhost:$HGPORT/
243 pulling from https://localhost:$HGPORT/
244 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
228 searching for changes
245 searching for changes
229 no changes found
246 no changes found
230 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
247 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
231 pulling from https://localhost:$HGPORT/
248 pulling from https://localhost:$HGPORT/
249 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
232 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
250 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
233 searching for changes
251 searching for changes
234 no changes found
252 no changes found
235
253
236 empty cacert file
254 empty cacert file
237
255
238 $ touch emptycafile
256 $ touch emptycafile
239
257
240 #if sslcontext
258 #if sslcontext
241 $ hg --config web.cacerts=emptycafile -R copy-pull pull
259 $ hg --config web.cacerts=emptycafile -R copy-pull pull
242 pulling from https://localhost:$HGPORT/
260 pulling from https://localhost:$HGPORT/
243 abort: error loading CA file emptycafile: * (glob)
261 abort: error loading CA file emptycafile: * (glob)
244 (file is empty or malformed?)
262 (file is empty or malformed?)
245 [255]
263 [255]
246 #else
264 #else
247 $ hg --config web.cacerts=emptycafile -R copy-pull pull
265 $ hg --config web.cacerts=emptycafile -R copy-pull pull
248 pulling from https://localhost:$HGPORT/
266 pulling from https://localhost:$HGPORT/
267 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
249 abort: error: * (glob)
268 abort: error: * (glob)
250 [255]
269 [255]
251 #endif
270 #endif
252
271
253 cacert mismatch
272 cacert mismatch
254
273
255 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
274 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
256 > https://127.0.0.1:$HGPORT/
275 > https://127.0.0.1:$HGPORT/
257 pulling from https://127.0.0.1:$HGPORT/ (glob)
276 pulling from https://127.0.0.1:$HGPORT/ (glob)
277 warning: connecting to 127.0.0.1 using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
258 abort: 127.0.0.1 certificate error: certificate is for localhost (glob)
278 abort: 127.0.0.1 certificate error: certificate is for localhost (glob)
259 (set hostsecurity.127.0.0.1:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely) (glob)
279 (set hostsecurity.127.0.0.1:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely) (glob)
260 [255]
280 [255]
261 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
281 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
262 > https://127.0.0.1:$HGPORT/ --insecure
282 > https://127.0.0.1:$HGPORT/ --insecure
263 pulling from https://127.0.0.1:$HGPORT/ (glob)
283 pulling from https://127.0.0.1:$HGPORT/ (glob)
284 warning: connecting to 127.0.0.1 using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
264 warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
285 warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
265 searching for changes
286 searching for changes
266 no changes found
287 no changes found
267 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
288 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
268 pulling from https://localhost:$HGPORT/
289 pulling from https://localhost:$HGPORT/
290 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
269 abort: error: *certificate verify failed* (glob)
291 abort: error: *certificate verify failed* (glob)
270 [255]
292 [255]
271 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
293 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
272 > --insecure
294 > --insecure
273 pulling from https://localhost:$HGPORT/
295 pulling from https://localhost:$HGPORT/
296 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
274 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
297 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
275 searching for changes
298 searching for changes
276 no changes found
299 no changes found
277
300
278 Test server cert which isn't valid yet
301 Test server cert which isn't valid yet
279
302
280 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
303 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
281 $ cat hg1.pid >> $DAEMON_PIDS
304 $ cat hg1.pid >> $DAEMON_PIDS
282 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
305 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
283 > https://localhost:$HGPORT1/
306 > https://localhost:$HGPORT1/
284 pulling from https://localhost:$HGPORT1/
307 pulling from https://localhost:$HGPORT1/
308 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
285 abort: error: *certificate verify failed* (glob)
309 abort: error: *certificate verify failed* (glob)
286 [255]
310 [255]
287
311
288 Test server cert which no longer is valid
312 Test server cert which no longer is valid
289
313
290 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
314 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
291 $ cat hg2.pid >> $DAEMON_PIDS
315 $ cat hg2.pid >> $DAEMON_PIDS
292 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
316 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
293 > https://localhost:$HGPORT2/
317 > https://localhost:$HGPORT2/
294 pulling from https://localhost:$HGPORT2/
318 pulling from https://localhost:$HGPORT2/
319 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
295 abort: error: *certificate verify failed* (glob)
320 abort: error: *certificate verify failed* (glob)
296 [255]
321 [255]
297
322
323 Disabling the TLS 1.0 warning works
324 $ hg -R copy-pull id https://localhost:$HGPORT/ \
325 > --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 \
326 > --config hostsecurity.disabletls10warning=true
327 5fed3813f7f5
328
298 Fingerprints
329 Fingerprints
299
330
300 - works without cacerts (hostkeyfingerprints)
331 - works without cacerts (hostkeyfingerprints)
301 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
332 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
333 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
302 5fed3813f7f5
334 5fed3813f7f5
303
335
304 - works without cacerts (hostsecurity)
336 - works without cacerts (hostsecurity)
305 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
337 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
338 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
306 5fed3813f7f5
339 5fed3813f7f5
307
340
308 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
341 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
342 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
309 5fed3813f7f5
343 5fed3813f7f5
310
344
311 - multiple fingerprints specified and first matches
345 - multiple fingerprints specified and first matches
312 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
346 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
347 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
313 5fed3813f7f5
348 5fed3813f7f5
314
349
315 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
350 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
351 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
316 5fed3813f7f5
352 5fed3813f7f5
317
353
318 - multiple fingerprints specified and last matches
354 - multiple fingerprints specified and last matches
319 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
355 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
356 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
320 5fed3813f7f5
357 5fed3813f7f5
321
358
322 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
359 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
360 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
323 5fed3813f7f5
361 5fed3813f7f5
324
362
325 - multiple fingerprints specified and none match
363 - multiple fingerprints specified and none match
326
364
327 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
365 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
366 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
328 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
367 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
329 (check hostfingerprint configuration)
368 (check hostfingerprint configuration)
330 [255]
369 [255]
331
370
332 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
371 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
372 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
333 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
373 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
334 (check hostsecurity configuration)
374 (check hostsecurity configuration)
335 [255]
375 [255]
336
376
337 - fails when cert doesn't match hostname (port is ignored)
377 - fails when cert doesn't match hostname (port is ignored)
338 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
378 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
379 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
339 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
380 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
340 (check hostfingerprint configuration)
381 (check hostfingerprint configuration)
341 [255]
382 [255]
342
383
343
384
344 - ignores that certificate doesn't match hostname
385 - ignores that certificate doesn't match hostname
345 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
386 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
387 warning: connecting to 127.0.0.1 using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
346 5fed3813f7f5
388 5fed3813f7f5
347
389
348 Ports used by next test. Kill servers.
390 Ports used by next test. Kill servers.
349
391
350 $ killdaemons.py hg0.pid
392 $ killdaemons.py hg0.pid
351 $ killdaemons.py hg1.pid
393 $ killdaemons.py hg1.pid
352 $ killdaemons.py hg2.pid
394 $ killdaemons.py hg2.pid
353
395
354 #if sslcontext
396 #if sslcontext
355 Start servers running supported TLS versions
397 Start servers running supported TLS versions
356
398
357 $ cd test
399 $ cd test
358 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
400 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
359 > --config devel.serverexactprotocol=tls1.0
401 > --config devel.serverexactprotocol=tls1.0
360 $ cat ../hg0.pid >> $DAEMON_PIDS
402 $ cat ../hg0.pid >> $DAEMON_PIDS
361 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
403 $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \
362 > --config devel.serverexactprotocol=tls1.1
404 > --config devel.serverexactprotocol=tls1.1
363 $ cat ../hg1.pid >> $DAEMON_PIDS
405 $ cat ../hg1.pid >> $DAEMON_PIDS
364 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
406 $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \
365 > --config devel.serverexactprotocol=tls1.2
407 > --config devel.serverexactprotocol=tls1.2
366 $ cat ../hg2.pid >> $DAEMON_PIDS
408 $ cat ../hg2.pid >> $DAEMON_PIDS
367 $ cd ..
409 $ cd ..
368
410
369 Clients talking same TLS versions work
411 Clients talking same TLS versions work
370
412
371 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
413 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/
372 5fed3813f7f5
414 5fed3813f7f5
373 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
415 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
374 5fed3813f7f5
416 5fed3813f7f5
375 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
417 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
376 5fed3813f7f5
418 5fed3813f7f5
377
419
378 Clients requiring newer TLS version than what server supports fail
420 Clients requiring newer TLS version than what server supports fail
379
421
380 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
422 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
381 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
423 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
382 abort: error: *unsupported protocol* (glob)
424 abort: error: *unsupported protocol* (glob)
383 [255]
425 [255]
384
426
385 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
427 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
386 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
428 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
387 abort: error: *unsupported protocol* (glob)
429 abort: error: *unsupported protocol* (glob)
388 [255]
430 [255]
389 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
431 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
390 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
432 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
391 abort: error: *unsupported protocol* (glob)
433 abort: error: *unsupported protocol* (glob)
392 [255]
434 [255]
393 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
435 $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
394 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
436 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
395 abort: error: *unsupported protocol* (glob)
437 abort: error: *unsupported protocol* (glob)
396 [255]
438 [255]
397
439
398 The per-host config option overrides the default
440 The per-host config option overrides the default
399
441
400 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
442 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
401 > --config hostsecurity.minimumprotocol=tls1.2 \
443 > --config hostsecurity.minimumprotocol=tls1.2 \
402 > --config hostsecurity.localhost:minimumprotocol=tls1.0
444 > --config hostsecurity.localhost:minimumprotocol=tls1.0
403 5fed3813f7f5
445 5fed3813f7f5
404
446
405 The per-host config option by itself works
447 The per-host config option by itself works
406
448
407 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
449 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
408 > --config hostsecurity.localhost:minimumprotocol=tls1.2
450 > --config hostsecurity.localhost:minimumprotocol=tls1.2
409 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
451 (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
410 abort: error: *unsupported protocol* (glob)
452 abort: error: *unsupported protocol* (glob)
411 [255]
453 [255]
412
454
413 $ killdaemons.py hg0.pid
455 $ killdaemons.py hg0.pid
414 $ killdaemons.py hg1.pid
456 $ killdaemons.py hg1.pid
415 $ killdaemons.py hg2.pid
457 $ killdaemons.py hg2.pid
416 #endif
458 #endif
417
459
418 Prepare for connecting through proxy
460 Prepare for connecting through proxy
419
461
420 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
462 $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV
421 $ cat hg0.pid >> $DAEMON_PIDS
463 $ cat hg0.pid >> $DAEMON_PIDS
422 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
464 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
423 $ cat hg2.pid >> $DAEMON_PIDS
465 $ cat hg2.pid >> $DAEMON_PIDS
424 tinyproxy.py doesn't fully detach, so killing it may result in extra output
466 tinyproxy.py doesn't fully detach, so killing it may result in extra output
425 from the shell. So don't kill it.
467 from the shell. So don't kill it.
426 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
468 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
427 $ while [ ! -f proxy.pid ]; do sleep 0; done
469 $ while [ ! -f proxy.pid ]; do sleep 0; done
428 $ cat proxy.pid >> $DAEMON_PIDS
470 $ cat proxy.pid >> $DAEMON_PIDS
429
471
430 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
472 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
431 $ echo "always=True" >> copy-pull/.hg/hgrc
473 $ echo "always=True" >> copy-pull/.hg/hgrc
432 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
474 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
433 $ echo "localhost =" >> copy-pull/.hg/hgrc
475 $ echo "localhost =" >> copy-pull/.hg/hgrc
434
476
435 Test unvalidated https through proxy
477 Test unvalidated https through proxy
436
478
437 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
479 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
438 pulling from https://localhost:$HGPORT/
480 pulling from https://localhost:$HGPORT/
481 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
439 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
482 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
440 searching for changes
483 searching for changes
441 no changes found
484 no changes found
442
485
443 Test https with cacert and fingerprint through proxy
486 Test https with cacert and fingerprint through proxy
444
487
445 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
488 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
446 > --config web.cacerts="$CERTSDIR/pub.pem"
489 > --config web.cacerts="$CERTSDIR/pub.pem"
447 pulling from https://localhost:$HGPORT/
490 pulling from https://localhost:$HGPORT/
491 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
448 searching for changes
492 searching for changes
449 no changes found
493 no changes found
450 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
494 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
451 pulling from https://127.0.0.1:$HGPORT/ (glob)
495 pulling from https://127.0.0.1:$HGPORT/ (glob)
496 warning: connecting to 127.0.0.1 using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
452 searching for changes
497 searching for changes
453 no changes found
498 no changes found
454
499
455 Test https with cert problems through proxy
500 Test https with cert problems through proxy
456
501
457 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
502 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
458 > --config web.cacerts="$CERTSDIR/pub-other.pem"
503 > --config web.cacerts="$CERTSDIR/pub-other.pem"
459 pulling from https://localhost:$HGPORT/
504 pulling from https://localhost:$HGPORT/
505 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
460 abort: error: *certificate verify failed* (glob)
506 abort: error: *certificate verify failed* (glob)
461 [255]
507 [255]
462 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
508 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
463 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
509 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
464 pulling from https://localhost:$HGPORT2/
510 pulling from https://localhost:$HGPORT2/
511 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
465 abort: error: *certificate verify failed* (glob)
512 abort: error: *certificate verify failed* (glob)
466 [255]
513 [255]
467
514
468
515
469 $ killdaemons.py hg0.pid
516 $ killdaemons.py hg0.pid
470
517
471 #if sslcontext
518 #if sslcontext
472
519
473 Start hgweb that requires client certificates:
520 Start hgweb that requires client certificates:
474
521
475 $ cd test
522 $ cd test
476 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
523 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
477 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
524 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
478 $ cat ../hg0.pid >> $DAEMON_PIDS
525 $ cat ../hg0.pid >> $DAEMON_PIDS
479 $ cd ..
526 $ cd ..
480
527
481 without client certificate:
528 without client certificate:
482
529
483 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
530 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
484 abort: error: *handshake failure* (glob)
531 abort: error: *handshake failure* (glob)
485 [255]
532 [255]
486
533
487 with client certificate:
534 with client certificate:
488
535
489 $ cat << EOT >> $HGRCPATH
536 $ cat << EOT >> $HGRCPATH
490 > [auth]
537 > [auth]
491 > l.prefix = localhost
538 > l.prefix = localhost
492 > l.cert = $CERTSDIR/client-cert.pem
539 > l.cert = $CERTSDIR/client-cert.pem
493 > l.key = $CERTSDIR/client-key.pem
540 > l.key = $CERTSDIR/client-key.pem
494 > EOT
541 > EOT
495
542
496 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
543 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
497 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
544 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
498 5fed3813f7f5
545 5fed3813f7f5
499
546
500 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
547 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
501 > --config ui.interactive=True --config ui.nontty=True
548 > --config ui.interactive=True --config ui.nontty=True
502 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
549 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
503
550
504 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
551 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
505 abort: error: * (glob)
552 abort: error: * (glob)
506 [255]
553 [255]
507
554
508 #endif
555 #endif
@@ -1,123 +1,127 b''
1 #require serve ssl
1 #require serve ssl
2
2
3 Set up SMTP server:
3 Set up SMTP server:
4
4
5 $ CERTSDIR="$TESTDIR/sslcerts"
5 $ CERTSDIR="$TESTDIR/sslcerts"
6 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
6 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
7
7
8 $ python "$TESTDIR/dummysmtpd.py" -p $HGPORT --pid-file a.pid -d \
8 $ python "$TESTDIR/dummysmtpd.py" -p $HGPORT --pid-file a.pid -d \
9 > --tls smtps --certificate `pwd`/server.pem
9 > --tls smtps --certificate `pwd`/server.pem
10 listening at localhost:$HGPORT
10 listening at localhost:$HGPORT
11 $ cat a.pid >> $DAEMON_PIDS
11 $ cat a.pid >> $DAEMON_PIDS
12
12
13 Ensure hg email output is sent to stdout:
13 Ensure hg email output is sent to stdout:
14
14
15 $ unset PAGER
15 $ unset PAGER
16
16
17 Set up repository:
17 Set up repository:
18
18
19 $ hg init t
19 $ hg init t
20 $ cd t
20 $ cd t
21 $ cat <<EOF >> .hg/hgrc
21 $ cat <<EOF >> .hg/hgrc
22 > [extensions]
22 > [extensions]
23 > patchbomb =
23 > patchbomb =
24 > [email]
24 > [email]
25 > method = smtp
25 > method = smtp
26 > [smtp]
26 > [smtp]
27 > host = localhost
27 > host = localhost
28 > port = $HGPORT
28 > port = $HGPORT
29 > tls = smtps
29 > tls = smtps
30 > EOF
30 > EOF
31
31
32 $ echo a > a
32 $ echo a > a
33 $ hg commit -Ama -d '1 0'
33 $ hg commit -Ama -d '1 0'
34 adding a
34 adding a
35
35
36 Utility functions:
36 Utility functions:
37
37
38 $ DISABLECACERTS=
38 $ DISABLECACERTS=
39 $ try () {
39 $ try () {
40 > hg email $DISABLECACERTS -f quux -t foo -c bar -r tip "$@"
40 > hg email $DISABLECACERTS -f quux -t foo -c bar -r tip "$@"
41 > }
41 > }
42
42
43 Our test cert is not signed by a trusted CA. It should fail to verify if
43 Our test cert is not signed by a trusted CA. It should fail to verify if
44 we are able to load CA certs:
44 we are able to load CA certs:
45
45
46 #if sslcontext defaultcacerts no-defaultcacertsloaded
46 #if sslcontext defaultcacerts no-defaultcacertsloaded
47 $ try
47 $ try
48 this patch series consists of 1 patches.
48 this patch series consists of 1 patches.
49
49
50
50
51 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
51 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
52 (?i)abort: .*?certificate.verify.failed.* (re)
52 (?i)abort: .*?certificate.verify.failed.* (re)
53 [255]
53 [255]
54 #endif
54 #endif
55
55
56 #if no-sslcontext defaultcacerts
56 #if no-sslcontext defaultcacerts
57 $ try
57 $ try
58 this patch series consists of 1 patches.
58 this patch series consists of 1 patches.
59
59
60
60
61 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info
61 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
62 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
62 (?i)abort: .*?certificate.verify.failed.* (re)
63 (?i)abort: .*?certificate.verify.failed.* (re)
63 [255]
64 [255]
64 #endif
65 #endif
65
66
66 #if defaultcacertsloaded
67 #if defaultcacertsloaded
67 $ try
68 $ try
68 this patch series consists of 1 patches.
69 this patch series consists of 1 patches.
69
70
70
71
71 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
72 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
72 (?i)abort: .*?certificate.verify.failed.* (re)
73 (?i)abort: .*?certificate.verify.failed.* (re)
73 [255]
74 [255]
74
75
75 #endif
76 #endif
76
77
77 #if no-defaultcacerts
78 #if no-defaultcacerts
78 $ try
79 $ try
79 this patch series consists of 1 patches.
80 this patch series consists of 1 patches.
80
81
81
82
82 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
83 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
83 abort: localhost certificate error: no certificate received
84 abort: localhost certificate error: no certificate received
84 (set hostsecurity.localhost:certfingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 config setting or use --insecure to connect insecurely)
85 (set hostsecurity.localhost:certfingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 config setting or use --insecure to connect insecurely)
85 [255]
86 [255]
86 #endif
87 #endif
87
88
88 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
89 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
89
90
90 Without certificates:
91 Without certificates:
91
92
92 $ try --debug
93 $ try --debug
93 this patch series consists of 1 patches.
94 this patch series consists of 1 patches.
94
95
95
96
96 (using smtps)
97 (using smtps)
97 sending mail: smtp host localhost, port * (glob)
98 sending mail: smtp host localhost, port * (glob)
99 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
98 (verifying remote certificate)
100 (verifying remote certificate)
99 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
101 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
100 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
102 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
101 [255]
103 [255]
102
104
103 With global certificates:
105 With global certificates:
104
106
105 $ try --debug --config web.cacerts="$CERTSDIR/pub.pem"
107 $ try --debug --config web.cacerts="$CERTSDIR/pub.pem"
106 this patch series consists of 1 patches.
108 this patch series consists of 1 patches.
107
109
108
110
109 (using smtps)
111 (using smtps)
110 sending mail: smtp host localhost, port * (glob)
112 sending mail: smtp host localhost, port * (glob)
113 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
111 (verifying remote certificate)
114 (verifying remote certificate)
112 sending [PATCH] a ...
115 sending [PATCH] a ...
113
116
114 With invalid certificates:
117 With invalid certificates:
115
118
116 $ try --config web.cacerts="$CERTSDIR/pub-other.pem"
119 $ try --config web.cacerts="$CERTSDIR/pub-other.pem"
117 this patch series consists of 1 patches.
120 this patch series consists of 1 patches.
118
121
119
122
123 warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?)
120 (?i)abort: .*?certificate.verify.failed.* (re)
124 (?i)abort: .*?certificate.verify.failed.* (re)
121 [255]
125 [255]
122
126
123 $ cd ..
127 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now