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