Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/sslutil.py @ 44948:ceb7318013d5
sslutil: fix names of variables containing minimum protocol strings
When working in this module, I found it very confusing that "protocol" as a
variable name could mean either "minimum protocol string" or an exact version
(as a string or ssl.PROTOCOL_* value). This patch prefixes variables of the
former type with "minimum".
author | Manuel Jacob <me@manueljacob.de> |
---|---|
date | Sun, 31 May 2020 10:47:38 +0200 |
parents | 95903a8d8c97 |
children | 4942c1bdd080 |
comparison
equal
deleted
inserted
replaced
44947:95903a8d8c97 | 44948:ceb7318013d5 |
---|---|
74 b'legacyfingerprint': False, | 74 b'legacyfingerprint': False, |
75 # PROTOCOL_* constant to use for SSLContext.__init__. | 75 # PROTOCOL_* constant to use for SSLContext.__init__. |
76 b'protocol': None, | 76 b'protocol': None, |
77 # String representation of minimum protocol to be used for UI | 77 # String representation of minimum protocol to be used for UI |
78 # presentation. | 78 # presentation. |
79 b'protocolui': None, | 79 b'minimumprotocolui': None, |
80 # ssl.CERT_* constant used by SSLContext.verify_mode. | 80 # ssl.CERT_* constant used by SSLContext.verify_mode. |
81 b'verifymode': None, | 81 b'verifymode': None, |
82 # Defines extra ssl.OP* bitwise options to set. | 82 # Defines extra ssl.OP* bitwise options to set. |
83 b'ctxoptions': None, | 83 b'ctxoptions': None, |
84 # OpenSSL Cipher List to use (instead of default). | 84 # OpenSSL Cipher List to use (instead of default). |
97 | 97 |
98 # We default to TLS 1.1+ where we can because TLS 1.0 has known | 98 # We default to TLS 1.1+ where we can because TLS 1.0 has known |
99 # vulnerabilities (like BEAST and POODLE). We allow users to downgrade to | 99 # vulnerabilities (like BEAST and POODLE). We allow users to downgrade to |
100 # TLS 1.0+ via config options in case a legacy server is encountered. | 100 # TLS 1.0+ via config options in case a legacy server is encountered. |
101 if b'tls1.1' in supportedprotocols: | 101 if b'tls1.1' in supportedprotocols: |
102 defaultprotocol = b'tls1.1' | 102 defaultminimumprotocol = b'tls1.1' |
103 else: | 103 else: |
104 # Let people know they are borderline secure. | 104 # Let people know they are borderline secure. |
105 # We don't document this config option because we want people to see | 105 # We don't document this config option because we want people to see |
106 # the bold warnings on the web site. | 106 # the bold warnings on the web site. |
107 # internal config: hostsecurity.disabletls10warning | 107 # internal config: hostsecurity.disabletls10warning |
113 b'https://mercurial-scm.org/wiki/SecureConnections for ' | 113 b'https://mercurial-scm.org/wiki/SecureConnections for ' |
114 b'more info\n' | 114 b'more info\n' |
115 ) | 115 ) |
116 % bhostname | 116 % bhostname |
117 ) | 117 ) |
118 defaultprotocol = b'tls1.0' | 118 defaultminimumprotocol = b'tls1.0' |
119 | 119 |
120 key = b'minimumprotocol' | 120 key = b'minimumprotocol' |
121 protocol = ui.config(b'hostsecurity', key, defaultprotocol) | 121 minimumprotocol = ui.config(b'hostsecurity', key, defaultminimumprotocol) |
122 validateprotocol(protocol, key) | 122 validateprotocol(minimumprotocol, key) |
123 | 123 |
124 key = b'%s:minimumprotocol' % bhostname | 124 key = b'%s:minimumprotocol' % bhostname |
125 protocol = ui.config(b'hostsecurity', key, protocol) | 125 minimumprotocol = ui.config(b'hostsecurity', key, minimumprotocol) |
126 validateprotocol(protocol, key) | 126 validateprotocol(minimumprotocol, key) |
127 | 127 |
128 # If --insecure is used, we allow the use of TLS 1.0 despite config options. | 128 # If --insecure is used, we allow the use of TLS 1.0 despite config options. |
129 # We always print a "connection security to %s is disabled..." message when | 129 # We always print a "connection security to %s is disabled..." message when |
130 # --insecure is used. So no need to print anything more here. | 130 # --insecure is used. So no need to print anything more here. |
131 if ui.insecureconnections: | 131 if ui.insecureconnections: |
132 protocol = b'tls1.0' | 132 minimumprotocol = b'tls1.0' |
133 | 133 |
134 s[b'protocolui'] = protocol | 134 s[b'minimumprotocolui'] = minimumprotocol |
135 s[b'protocol'], s[b'ctxoptions'] = protocolsettings(protocol) | 135 s[b'protocol'], s[b'ctxoptions'] = protocolsettings(minimumprotocol) |
136 | 136 |
137 ciphers = ui.config(b'hostsecurity', b'ciphers') | 137 ciphers = ui.config(b'hostsecurity', b'ciphers') |
138 ciphers = ui.config(b'hostsecurity', b'%s:ciphers' % bhostname, ciphers) | 138 ciphers = ui.config(b'hostsecurity', b'%s:ciphers' % bhostname, ciphers) |
139 s[b'ciphers'] = ciphers | 139 s[b'ciphers'] = ciphers |
140 | 140 |
239 assert s[b'verifymode'] is not None | 239 assert s[b'verifymode'] is not None |
240 | 240 |
241 return s | 241 return s |
242 | 242 |
243 | 243 |
244 def protocolsettings(protocol): | 244 def protocolsettings(minimumprotocol): |
245 """Resolve the protocol for a config value. | 245 """Resolve the protocol for a config value. |
246 | 246 |
247 Returns a tuple of (protocol, options) which are values used by SSLContext. | 247 Returns a tuple of (protocol, options) which are values used by SSLContext. |
248 """ | 248 """ |
249 if protocol not in configprotocols: | 249 if minimumprotocol not in configprotocols: |
250 raise ValueError(b'protocol value not supported: %s' % protocol) | 250 raise ValueError(b'protocol value not supported: %s' % minimumprotocol) |
251 | 251 |
252 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol | 252 # Despite its name, PROTOCOL_SSLv23 selects the highest protocol |
253 # that both ends support, including TLS protocols. On legacy stacks, | 253 # that both ends support, including TLS protocols. On legacy stacks, |
254 # the highest it likely goes is TLS 1.0. On modern stacks, it can | 254 # the highest it likely goes is TLS 1.0. On modern stacks, it can |
255 # support TLS 1.2. | 255 # support TLS 1.2. |
257 # The PROTOCOL_TLSv* constants select a specific TLS version | 257 # The PROTOCOL_TLSv* constants select a specific TLS version |
258 # only (as opposed to multiple versions). So the method for | 258 # only (as opposed to multiple versions). So the method for |
259 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and | 259 # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and |
260 # disable protocols via SSLContext.options and OP_NO_* constants. | 260 # disable protocols via SSLContext.options and OP_NO_* constants. |
261 if supportedprotocols == {b'tls1.0'}: | 261 if supportedprotocols == {b'tls1.0'}: |
262 if protocol != b'tls1.0': | 262 if minimumprotocol != b'tls1.0': |
263 raise error.Abort( | 263 raise error.Abort( |
264 _(b'current Python does not support protocol setting %s') | 264 _(b'current Python does not support protocol setting %s') |
265 % protocol, | 265 % minimumprotocol, |
266 hint=_( | 266 hint=_( |
267 b'upgrade Python or disable setting since ' | 267 b'upgrade Python or disable setting since ' |
268 b'only TLS 1.0 is supported' | 268 b'only TLS 1.0 is supported' |
269 ), | 269 ), |
270 ) | 270 ) |
272 return ssl.PROTOCOL_TLSv1, 0 | 272 return ssl.PROTOCOL_TLSv1, 0 |
273 | 273 |
274 # SSLv2 and SSLv3 are broken. We ban them outright. | 274 # SSLv2 and SSLv3 are broken. We ban them outright. |
275 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | 275 options = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 |
276 | 276 |
277 if protocol == b'tls1.0': | 277 if minimumprotocol == b'tls1.0': |
278 # Defaults above are to use TLS 1.0+ | 278 # Defaults above are to use TLS 1.0+ |
279 pass | 279 pass |
280 elif protocol == b'tls1.1': | 280 elif minimumprotocol == b'tls1.1': |
281 options |= ssl.OP_NO_TLSv1 | 281 options |= ssl.OP_NO_TLSv1 |
282 elif protocol == b'tls1.2': | 282 elif minimumprotocol == b'tls1.2': |
283 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | 283 options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 |
284 else: | 284 else: |
285 raise error.Abort(_(b'this should not happen')) | 285 raise error.Abort(_(b'this should not happen')) |
286 | 286 |
287 # Prevent CRIME. | 287 # Prevent CRIME. |
422 # outright. Hopefully the reason for this error is that we require | 422 # outright. Hopefully the reason for this error is that we require |
423 # TLS 1.1+ and the server only supports TLS 1.0. Whatever the | 423 # TLS 1.1+ and the server only supports TLS 1.0. Whatever the |
424 # reason, try to emit an actionable warning. | 424 # reason, try to emit an actionable warning. |
425 if e.reason == 'UNSUPPORTED_PROTOCOL': | 425 if e.reason == 'UNSUPPORTED_PROTOCOL': |
426 # We attempted TLS 1.0+. | 426 # We attempted TLS 1.0+. |
427 if settings[b'protocolui'] == b'tls1.0': | 427 if settings[b'minimumprotocolui'] == b'tls1.0': |
428 # We support more than just TLS 1.0+. If this happens, | 428 # We support more than just TLS 1.0+. If this happens, |
429 # the likely scenario is either the client or the server | 429 # the likely scenario is either the client or the server |
430 # is really old. (e.g. server doesn't support TLS 1.0+ or | 430 # is really old. (e.g. server doesn't support TLS 1.0+ or |
431 # client doesn't support modern TLS versions introduced | 431 # client doesn't support modern TLS versions introduced |
432 # several years from when this comment was written). | 432 # several years from when this comment was written). |
467 b'(could not negotiate a common security protocol (%s+) ' | 467 b'(could not negotiate a common security protocol (%s+) ' |
468 b'with %s; the likely cause is Mercurial is configured ' | 468 b'with %s; the likely cause is Mercurial is configured ' |
469 b'to be more secure than the server can support)\n' | 469 b'to be more secure than the server can support)\n' |
470 ) | 470 ) |
471 % ( | 471 % ( |
472 settings[b'protocolui'], | 472 settings[b'minimumprotocolui'], |
473 pycompat.bytesurl(serverhostname), | 473 pycompat.bytesurl(serverhostname), |
474 ) | 474 ) |
475 ) | 475 ) |
476 ui.warn( | 476 ui.warn( |
477 _( | 477 _( |