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