Show More
@@ -1000,10 +1000,22 b' For example::' | |||
|
1000 | 1000 | ``hostsecurity`` |
|
1001 | 1001 | ---------------- |
|
1002 | 1002 | |
|
1003 |
Used to specify per-host security settings |
|
|
1004 | ||
|
1005 | Options in this section have the form ``hostname``:``setting``. This allows | |
|
1006 | multiple settings to be defined on a per-host basis. | |
|
1003 | Used to specify global and per-host security settings for connecting to | |
|
1004 | other machines. | |
|
1005 | ||
|
1006 | The following options control default behavior for all hosts. | |
|
1007 | ||
|
1008 | ``minimumprotocol`` | |
|
1009 | Defines the minimum channel encryption protocol to use. | |
|
1010 | ||
|
1011 | By default, the highest version of TLS - 1.0 or greater - supported by | |
|
1012 | both client and server is used. | |
|
1013 | ||
|
1014 | Allowed values are: ``tls1.0`` (the default), ``tls1.1``, ``tls1.2``. | |
|
1015 | ||
|
1016 | Options in the ``[hostsecurity]`` section can have the form | |
|
1017 | ``hostname``:``setting``. This allows multiple settings to be defined on a | |
|
1018 | per-host basis. | |
|
1007 | 1019 | |
|
1008 | 1020 | The following per-host settings can be defined. |
|
1009 | 1021 | |
@@ -1026,6 +1038,10 b' The following per-host settings can be d' | |||
|
1026 | 1038 | |
|
1027 | 1039 | This option takes precedence over ``verifycertsfile``. |
|
1028 | 1040 | |
|
1041 | ``minimumprotocol`` | |
|
1042 | This behaves like ``minimumprotocol`` as described above except it | |
|
1043 | only applies to the host on which it is defined. | |
|
1044 | ||
|
1029 | 1045 | ``verifycertsfile`` |
|
1030 | 1046 | Path to file a containing a list of PEM encoded certificates used to |
|
1031 | 1047 | verify the server certificate. Environment variables and ``~user`` |
@@ -1058,6 +1074,13 b' For example::' | |||
|
1058 | 1074 | hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33 |
|
1059 | 1075 | foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem |
|
1060 | 1076 | |
|
1077 | To change the default minimum protocol version to TLS 1.2 but to allow TLS 1.1 | |
|
1078 | when connecting to ``hg.example.com``:: | |
|
1079 | ||
|
1080 | [hostsecurity] | |
|
1081 | minimumprotocol = tls1.2 | |
|
1082 | hg.example.com:minimumprotocol = tls1.1 | |
|
1083 | ||
|
1061 | 1084 | ``http_proxy`` |
|
1062 | 1085 | -------------- |
|
1063 | 1086 |
@@ -29,14 +29,13 b' from . import (' | |||
|
29 | 29 | # modern/secure or legacy/insecure. Many operations in this module have |
|
30 | 30 | # separate code paths depending on support in Python. |
|
31 | 31 | |
|
32 | hassni = getattr(ssl, 'HAS_SNI', False) | |
|
32 | configprotocols = set([ | |
|
33 | 'tls1.0', | |
|
34 | 'tls1.1', | |
|
35 | 'tls1.2', | |
|
36 | ]) | |
|
33 | 37 | |
|
34 | try: | |
|
35 | OP_NO_SSLv2 = ssl.OP_NO_SSLv2 | |
|
36 | OP_NO_SSLv3 = ssl.OP_NO_SSLv3 | |
|
37 | except AttributeError: | |
|
38 | OP_NO_SSLv2 = 0x1000000 | |
|
39 | OP_NO_SSLv3 = 0x2000000 | |
|
38 | hassni = getattr(ssl, 'HAS_SNI', False) | |
|
40 | 39 | |
|
41 | 40 | try: |
|
42 | 41 | # ssl.SSLContext was added in 2.7.9 and presence indicates modern |
@@ -136,7 +135,7 b' def _hostsettings(ui, hostname):' | |||
|
136 | 135 | |
|
137 | 136 | # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
|
138 | 137 | # that both ends support, including TLS protocols. On legacy stacks, |
|
139 |
# the highest it likely goes i |
|
|
138 | # the highest it likely goes is TLS 1.0. On modern stacks, it can | |
|
140 | 139 | # support TLS 1.2. |
|
141 | 140 | # |
|
142 | 141 | # The PROTOCOL_TLSv* constants select a specific TLS version |
@@ -145,19 +144,26 b' def _hostsettings(ui, hostname):' | |||
|
145 | 144 | # disable protocols via SSLContext.options and OP_NO_* constants. |
|
146 | 145 | # However, SSLContext.options doesn't work unless we have the |
|
147 | 146 | # full/real SSLContext available to us. |
|
148 | if modernssl: | |
|
149 | s['protocol'] = ssl.PROTOCOL_SSLv23 | |
|
150 | else: | |
|
151 | s['protocol'] = ssl.PROTOCOL_TLSv1 | |
|
147 | ||
|
148 | # Allow minimum TLS protocol to be specified in the config. | |
|
149 | def validateprotocol(protocol, key): | |
|
150 | if protocol not in configprotocols: | |
|
151 | raise error.Abort( | |
|
152 | _('unsupported protocol from hostsecurity.%s: %s') % | |
|
153 | (key, protocol), | |
|
154 | hint=_('valid protocols: %s') % | |
|
155 | ' '.join(sorted(configprotocols))) | |
|
152 | 156 | |
|
153 | # SSLv2 and SSLv3 are broken. We ban them outright. | |
|
154 | # WARNING: ctxoptions doesn't have an effect unless the modern ssl module | |
|
155 | # is available. Be careful when adding flags! | |
|
156 | s['ctxoptions'] = OP_NO_SSLv2 | OP_NO_SSLv3 | |
|
157 | key = 'minimumprotocol' | |
|
158 | # Default to TLS 1.0+ as that is what browsers are currently doing. | |
|
159 | protocol = ui.config('hostsecurity', key, 'tls1.0') | |
|
160 | validateprotocol(protocol, key) | |
|
157 | 161 | |
|
158 | # Prevent CRIME. | |
|
159 | # There is no guarantee this attribute is defined on the module. | |
|
160 | s['ctxoptions'] |= getattr(ssl, 'OP_NO_COMPRESSION', 0) | |
|
162 | key = '%s:minimumprotocol' % hostname | |
|
163 | protocol = ui.config('hostsecurity', key, protocol) | |
|
164 | validateprotocol(protocol, key) | |
|
165 | ||
|
166 | s['protocol'], s['ctxoptions'] = protocolsettings(protocol) | |
|
161 | 167 | |
|
162 | 168 | # Look for fingerprints in [hostsecurity] section. Value is a list |
|
163 | 169 | # of <alg>:<fingerprint> strings. |
@@ -250,6 +256,46 b' def _hostsettings(ui, hostname):' | |||
|
250 | 256 | |
|
251 | 257 | return s |
|
252 | 258 | |
|
259 | def protocolsettings(protocol): | |
|
260 | """Resolve the protocol and context options for a config value.""" | |
|
261 | if protocol not in configprotocols: | |
|
262 | raise ValueError('protocol value not supported: %s' % protocol) | |
|
263 | ||
|
264 | # Legacy ssl module only supports up to TLS 1.0. Ideally we'd use | |
|
265 | # PROTOCOL_SSLv23 and options to disable SSLv2 and SSLv3. However, | |
|
266 | # SSLContext.options doesn't work in our implementation since we use | |
|
267 | # a fake SSLContext on these Python versions. | |
|
268 | if not modernssl: | |
|
269 | if protocol != 'tls1.0': | |
|
270 | raise error.Abort(_('current Python does not support protocol ' | |
|
271 | 'setting %s') % protocol, | |
|
272 | hint=_('upgrade Python or disable setting since ' | |
|
273 | 'only TLS 1.0 is supported')) | |
|
274 | ||
|
275 | return ssl.PROTOCOL_TLSv1, 0 | |
|
276 | ||
|
277 | # WARNING: returned options don't work unless the modern ssl module | |
|
278 | # is available. Be careful when adding options here. | |
|
279 | ||
|
280 | # SSLv2 and SSLv3 are broken. We ban them outright. | |
|
281 | options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | |
|
282 | ||
|
283 | if protocol == 'tls1.0': | |
|
284 | # Defaults above are to use TLS 1.0+ | |
|
285 | pass | |
|
286 | elif protocol == 'tls1.1': | |
|
287 | options |= ssl.OP_NO_TLSv1 | |
|
288 | elif protocol == 'tls1.2': | |
|
289 | options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | |
|
290 | else: | |
|
291 | raise error.Abort(_('this should not happen')) | |
|
292 | ||
|
293 | # Prevent CRIME. | |
|
294 | # There is no guarantee this attribute is defined on the module. | |
|
295 | options |= getattr(ssl, 'OP_NO_COMPRESSION', 0) | |
|
296 | ||
|
297 | return ssl.PROTOCOL_SSLv23, options | |
|
298 | ||
|
253 | 299 | def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None): |
|
254 | 300 | """Add SSL/TLS to a socket. |
|
255 | 301 | |
@@ -306,7 +352,7 b' def wrapsocket(sock, keyfile, certfile, ' | |||
|
306 | 352 | |
|
307 | 353 | try: |
|
308 | 354 | sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
|
309 | except ssl.SSLError: | |
|
355 | except ssl.SSLError as e: | |
|
310 | 356 | # If we're doing certificate verification and no CA certs are loaded, |
|
311 | 357 | # that is almost certainly the reason why verification failed. Provide |
|
312 | 358 | # a hint to the user. |
@@ -318,6 +364,13 b' def wrapsocket(sock, keyfile, certfile, ' | |||
|
318 | 364 | 'were loaded; see ' |
|
319 | 365 | 'https://mercurial-scm.org/wiki/SecureConnections for ' |
|
320 | 366 | 'how to configure Mercurial to avoid this error)\n')) |
|
367 | # Try to print more helpful error messages for known failures. | |
|
368 | if util.safehasattr(e, 'reason'): | |
|
369 | if e.reason == 'UNSUPPORTED_PROTOCOL': | |
|
370 | ui.warn(_('(could not negotiate a common protocol; see ' | |
|
371 | 'https://mercurial-scm.org/wiki/SecureConnections ' | |
|
372 | 'for how to configure Mercurial to avoid this ' | |
|
373 | 'error)\n')) | |
|
321 | 374 | raise |
|
322 | 375 | |
|
323 | 376 | # check if wrap_socket failed silently because socket had been |
@@ -349,14 +402,28 b' def wrapserversocket(sock, ui, certfile=' | |||
|
349 | 402 | |
|
350 | 403 | Typically ``cafile`` is only defined if ``requireclientcert`` is true. |
|
351 | 404 | """ |
|
405 | protocol, options = protocolsettings('tls1.0') | |
|
406 | ||
|
407 | # This config option is intended for use in tests only. It is a giant | |
|
408 | # footgun to kill security. Don't define it. | |
|
409 | exactprotocol = ui.config('devel', 'serverexactprotocol') | |
|
410 | if exactprotocol == 'tls1.0': | |
|
411 | protocol = ssl.PROTOCOL_TLSv1 | |
|
412 | elif exactprotocol == 'tls1.1': | |
|
413 | protocol = ssl.PROTOCOL_TLSv1_1 | |
|
414 | elif exactprotocol == 'tls1.2': | |
|
415 | protocol = ssl.PROTOCOL_TLSv1_2 | |
|
416 | elif exactprotocol: | |
|
417 | raise error.Abort(_('invalid value for serverexactprotocol: %s') % | |
|
418 | exactprotocol) | |
|
419 | ||
|
352 | 420 | if modernssl: |
|
353 | 421 | # We /could/ use create_default_context() here since it doesn't load |
|
354 | # CAs when configured for client auth. | |
|
355 | sslcontext = SSLContext(ssl.PROTOCOL_SSLv23) | |
|
356 | # SSLv2 and SSLv3 are broken. Ban them outright. | |
|
357 |
sslcontext.options |= |
|
|
358 | # Prevent CRIME | |
|
359 | sslcontext.options |= getattr(ssl, 'OP_NO_COMPRESSION', 0) | |
|
422 | # CAs when configured for client auth. However, it is hard-coded to | |
|
423 | # use ssl.PROTOCOL_SSLv23 which may not be appropriate here. | |
|
424 | sslcontext = SSLContext(protocol) | |
|
425 | sslcontext.options |= options | |
|
426 | ||
|
360 | 427 | # Improve forward secrecy. |
|
361 | 428 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0) |
|
362 | 429 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0) |
@@ -345,11 +345,79 b' Fingerprints' | |||
|
345 | 345 | $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 |
|
346 | 346 | 5fed3813f7f5 |
|
347 | 347 | |
|
348 |
|
|
|
348 | Ports used by next test. Kill servers. | |
|
349 | ||
|
350 | $ killdaemons.py hg0.pid | |
|
349 | 351 | $ killdaemons.py hg1.pid |
|
352 | $ killdaemons.py hg2.pid | |
|
353 | ||
|
354 | #if sslcontext | |
|
355 | Start servers running supported TLS versions | |
|
356 | ||
|
357 | $ cd test | |
|
358 | $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \ | |
|
359 | > --config devel.serverexactprotocol=tls1.0 | |
|
360 | $ cat ../hg0.pid >> $DAEMON_PIDS | |
|
361 | $ hg serve -p $HGPORT1 -d --pid-file=../hg1.pid --certificate=$PRIV \ | |
|
362 | > --config devel.serverexactprotocol=tls1.1 | |
|
363 | $ cat ../hg1.pid >> $DAEMON_PIDS | |
|
364 | $ hg serve -p $HGPORT2 -d --pid-file=../hg2.pid --certificate=$PRIV \ | |
|
365 | > --config devel.serverexactprotocol=tls1.2 | |
|
366 | $ cat ../hg2.pid >> $DAEMON_PIDS | |
|
367 | $ cd .. | |
|
368 | ||
|
369 | Clients talking same TLS versions work | |
|
370 | ||
|
371 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.0 id https://localhost:$HGPORT/ | |
|
372 | 5fed3813f7f5 | |
|
373 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/ | |
|
374 | 5fed3813f7f5 | |
|
375 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/ | |
|
376 | 5fed3813f7f5 | |
|
377 | ||
|
378 | Clients requiring newer TLS version than what server supports fail | |
|
379 | ||
|
380 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/ | |
|
381 | (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) | |
|
382 | abort: error: *unsupported protocol* (glob) | |
|
383 | [255] | |
|
384 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/ | |
|
385 | (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) | |
|
386 | abort: error: *unsupported protocol* (glob) | |
|
387 | [255] | |
|
388 | $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/ | |
|
389 | (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) | |
|
390 | abort: error: *unsupported protocol* (glob) | |
|
391 | [255] | |
|
392 | ||
|
393 | The per-host config option overrides the default | |
|
394 | ||
|
395 | $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \ | |
|
396 | > --config hostsecurity.minimumprotocol=tls1.2 \ | |
|
397 | > --config hostsecurity.localhost:minimumprotocol=tls1.0 | |
|
398 | 5fed3813f7f5 | |
|
399 | ||
|
400 | The per-host config option by itself works | |
|
401 | ||
|
402 | $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \ | |
|
403 | > --config hostsecurity.localhost:minimumprotocol=tls1.2 | |
|
404 | (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) | |
|
405 | abort: error: *unsupported protocol* (glob) | |
|
406 | [255] | |
|
407 | ||
|
408 | $ killdaemons.py hg0.pid | |
|
409 | $ killdaemons.py hg1.pid | |
|
410 | $ killdaemons.py hg2.pid | |
|
411 | #endif | |
|
350 | 412 | |
|
351 | 413 | Prepare for connecting through proxy |
|
352 | 414 | |
|
415 | $ hg serve -R test -p $HGPORT -d --pid-file=hg0.pid --certificate=$PRIV | |
|
416 | $ cat hg0.pid >> $DAEMON_PIDS | |
|
417 | $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem | |
|
418 | $ cat hg2.pid >> $DAEMON_PIDS | |
|
419 | tinyproxy.py doesn't fully detach, so killing it may result in extra output | |
|
420 | from the shell. So don't kill it. | |
|
353 | 421 | $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 & |
|
354 | 422 | $ while [ ! -f proxy.pid ]; do sleep 0; done |
|
355 | 423 | $ cat proxy.pid >> $DAEMON_PIDS |
General Comments 0
You need to be logged in to leave comments.
Login now