Show More
@@ -1000,10 +1000,22 For example:: | |||||
1000 | ``hostsecurity`` |
|
1000 | ``hostsecurity`` | |
1001 | ---------------- |
|
1001 | ---------------- | |
1002 |
|
1002 | |||
1003 |
Used to specify per-host security settings |
|
1003 | Used to specify global and per-host security settings for connecting to | |
1004 |
|
1004 | other machines. | ||
1005 | Options in this section have the form ``hostname``:``setting``. This allows |
|
1005 | ||
1006 | multiple settings to be defined on a per-host basis. |
|
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 | The following per-host settings can be defined. |
|
1020 | The following per-host settings can be defined. | |
1009 |
|
1021 | |||
@@ -1026,6 +1038,10 The following per-host settings can be d | |||||
1026 |
|
1038 | |||
1027 | This option takes precedence over ``verifycertsfile``. |
|
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 | ``verifycertsfile`` |
|
1045 | ``verifycertsfile`` | |
1030 | Path to file a containing a list of PEM encoded certificates used to |
|
1046 | Path to file a containing a list of PEM encoded certificates used to | |
1031 | verify the server certificate. Environment variables and ``~user`` |
|
1047 | verify the server certificate. Environment variables and ``~user`` | |
@@ -1058,6 +1074,13 For example:: | |||||
1058 | 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 |
|
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 | foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem |
|
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 | ``http_proxy`` |
|
1084 | ``http_proxy`` | |
1062 | -------------- |
|
1085 | -------------- | |
1063 |
|
1086 |
@@ -29,14 +29,13 from . import ( | |||||
29 | # modern/secure or legacy/insecure. Many operations in this module have |
|
29 | # modern/secure or legacy/insecure. Many operations in this module have | |
30 | # separate code paths depending on support in Python. |
|
30 | # separate code paths depending on support in Python. | |
31 |
|
31 | |||
32 | hassni = getattr(ssl, 'HAS_SNI', False) |
|
32 | configprotocols = set([ | |
|
33 | 'tls1.0', | |||
|
34 | 'tls1.1', | |||
|
35 | 'tls1.2', | |||
|
36 | ]) | |||
33 |
|
37 | |||
34 | try: |
|
38 | hassni = getattr(ssl, 'HAS_SNI', False) | |
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 |
|
|||
40 |
|
39 | |||
41 | try: |
|
40 | try: | |
42 | # ssl.SSLContext was added in 2.7.9 and presence indicates modern |
|
41 | # ssl.SSLContext was added in 2.7.9 and presence indicates modern | |
@@ -136,7 +135,7 def _hostsettings(ui, hostname): | |||||
136 |
|
135 | |||
137 | # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
|
136 | # Despite its name, PROTOCOL_SSLv23 selects the highest protocol | |
138 | # that both ends support, including TLS protocols. On legacy stacks, |
|
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 | # support TLS 1.2. |
|
139 | # support TLS 1.2. | |
141 | # |
|
140 | # | |
142 | # The PROTOCOL_TLSv* constants select a specific TLS version |
|
141 | # The PROTOCOL_TLSv* constants select a specific TLS version | |
@@ -145,19 +144,26 def _hostsettings(ui, hostname): | |||||
145 | # disable protocols via SSLContext.options and OP_NO_* constants. |
|
144 | # disable protocols via SSLContext.options and OP_NO_* constants. | |
146 | # However, SSLContext.options doesn't work unless we have the |
|
145 | # However, SSLContext.options doesn't work unless we have the | |
147 | # full/real SSLContext available to us. |
|
146 | # full/real SSLContext available to us. | |
148 | if modernssl: |
|
147 | ||
149 | s['protocol'] = ssl.PROTOCOL_SSLv23 |
|
148 | # Allow minimum TLS protocol to be specified in the config. | |
150 | else: |
|
149 | def validateprotocol(protocol, key): | |
151 | s['protocol'] = ssl.PROTOCOL_TLSv1 |
|
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. |
|
157 | key = 'minimumprotocol' | |
154 | # WARNING: ctxoptions doesn't have an effect unless the modern ssl module |
|
158 | # Default to TLS 1.0+ as that is what browsers are currently doing. | |
155 | # is available. Be careful when adding flags! |
|
159 | protocol = ui.config('hostsecurity', key, 'tls1.0') | |
156 | s['ctxoptions'] = OP_NO_SSLv2 | OP_NO_SSLv3 |
|
160 | validateprotocol(protocol, key) | |
157 |
|
161 | |||
158 | # Prevent CRIME. |
|
162 | key = '%s:minimumprotocol' % hostname | |
159 | # There is no guarantee this attribute is defined on the module. |
|
163 | protocol = ui.config('hostsecurity', key, protocol) | |
160 | s['ctxoptions'] |= getattr(ssl, 'OP_NO_COMPRESSION', 0) |
|
164 | validateprotocol(protocol, key) | |
|
165 | ||||
|
166 | s['protocol'], s['ctxoptions'] = protocolsettings(protocol) | |||
161 |
|
167 | |||
162 | # Look for fingerprints in [hostsecurity] section. Value is a list |
|
168 | # Look for fingerprints in [hostsecurity] section. Value is a list | |
163 | # of <alg>:<fingerprint> strings. |
|
169 | # of <alg>:<fingerprint> strings. | |
@@ -250,6 +256,46 def _hostsettings(ui, hostname): | |||||
250 |
|
256 | |||
251 | return s |
|
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 | def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None): |
|
299 | def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None): | |
254 | """Add SSL/TLS to a socket. |
|
300 | """Add SSL/TLS to a socket. | |
255 |
|
301 | |||
@@ -306,7 +352,7 def wrapsocket(sock, keyfile, certfile, | |||||
306 |
|
352 | |||
307 | try: |
|
353 | try: | |
308 | sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) |
|
354 | sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) | |
309 | except ssl.SSLError: |
|
355 | except ssl.SSLError as e: | |
310 | # If we're doing certificate verification and no CA certs are loaded, |
|
356 | # If we're doing certificate verification and no CA certs are loaded, | |
311 | # that is almost certainly the reason why verification failed. Provide |
|
357 | # that is almost certainly the reason why verification failed. Provide | |
312 | # a hint to the user. |
|
358 | # a hint to the user. | |
@@ -318,6 +364,13 def wrapsocket(sock, keyfile, certfile, | |||||
318 | 'were loaded; see ' |
|
364 | 'were loaded; see ' | |
319 | 'https://mercurial-scm.org/wiki/SecureConnections for ' |
|
365 | 'https://mercurial-scm.org/wiki/SecureConnections for ' | |
320 | 'how to configure Mercurial to avoid this error)\n')) |
|
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 | raise |
|
374 | raise | |
322 |
|
375 | |||
323 | # check if wrap_socket failed silently because socket had been |
|
376 | # check if wrap_socket failed silently because socket had been | |
@@ -349,14 +402,28 def wrapserversocket(sock, ui, certfile= | |||||
349 |
|
402 | |||
350 | Typically ``cafile`` is only defined if ``requireclientcert`` is true. |
|
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 | if modernssl: |
|
420 | if modernssl: | |
353 | # We /could/ use create_default_context() here since it doesn't load |
|
421 | # We /could/ use create_default_context() here since it doesn't load | |
354 | # CAs when configured for client auth. |
|
422 | # CAs when configured for client auth. However, it is hard-coded to | |
355 | sslcontext = SSLContext(ssl.PROTOCOL_SSLv23) |
|
423 | # use ssl.PROTOCOL_SSLv23 which may not be appropriate here. | |
356 | # SSLv2 and SSLv3 are broken. Ban them outright. |
|
424 | sslcontext = SSLContext(protocol) | |
357 |
sslcontext.options |= |
|
425 | sslcontext.options |= options | |
358 | # Prevent CRIME |
|
426 | ||
359 | sslcontext.options |= getattr(ssl, 'OP_NO_COMPRESSION', 0) |
|
|||
360 | # Improve forward secrecy. |
|
427 | # Improve forward secrecy. | |
361 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0) |
|
428 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0) | |
362 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0) |
|
429 | sslcontext.options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0) |
@@ -345,11 +345,79 Fingerprints | |||||
345 | $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 |
|
345 | $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 | |
346 | 5fed3813f7f5 |
|
346 | 5fed3813f7f5 | |
347 |
|
347 | |||
348 |
|
|
348 | Ports used by next test. Kill servers. | |
|
349 | ||||
|
350 | $ killdaemons.py hg0.pid | |||
349 | $ killdaemons.py hg1.pid |
|
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 | Prepare for connecting through proxy |
|
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 | $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 & |
|
421 | $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 & | |
354 | $ while [ ! -f proxy.pid ]; do sleep 0; done |
|
422 | $ while [ ! -f proxy.pid ]; do sleep 0; done | |
355 | $ cat proxy.pid >> $DAEMON_PIDS |
|
423 | $ cat proxy.pid >> $DAEMON_PIDS |
General Comments 0
You need to be logged in to leave comments.
Login now