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