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 _(