[PATCH 2 of 3] sslutil: accept serverside argument to wrapsocket
Gregory Szorc
gregory.szorc at gmail.com
Tue Jul 12 18:32:26 EDT 2016
# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1468362248 25200
# Tue Jul 12 15:24:08 2016 -0700
# Node ID a9a5f1ca8f96a1292cf90f55e666733285f7b0f0
# Parent 7f26f442cd1db34d72e26a9125e5b4216a71f7f8
sslutil: accept serverside argument to wrapsocket
In preparation for having the built-in HTTP server use the same
function for wrapping sockets (currently it calls into the ssl
module directly and isn't using best practices).
diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -241,37 +241,44 @@ def _hostsettings(ui, hostname):
s['verifymode'] = ssl.CERT_NONE
assert s['protocol'] is not None
assert s['ctxoptions'] is not None
assert s['verifymode'] is not None
return s
-def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
+def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None,
+ serverside=False):
"""Add SSL/TLS to a socket.
This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
choices based on what security options are available.
In addition to the arguments supported by ``ssl.wrap_socket``, we allow
the following additional arguments:
* serverhostname - The expected hostname of the remote server. If the
server (and client) support SNI, this tells the server which certificate
to use.
+
+ * serverside - Whether the socket will be used on servers.
"""
- if not serverhostname:
- raise error.Abort(_('serverhostname argument is required'))
+ if not serverside and not serverhostname:
+ raise error.Abort(_('serverhostname argument is required for client '
+ 'sockets'))
settings = _hostsettings(ui, serverhostname)
if modernssl:
assert settings['protocol'] == ssl.PROTOCOL_SSLv23
- sslcontext = ssl.create_default_context()
+ purpose = ssl.Purpose.SERVER_AUTH
+ if serverside:
+ purpose = ssl.Purpose.CLIENT_AUTH
+ sslcontext = ssl.create_default_context(purpose=purpose)
# We have our own hostname verification code.
sslcontext.check_hostname = False
else:
sslcontext = SSLContext(settings['protocol'])
# This is a no-op unless using modern ssl.
sslcontext.options |= settings['ctxoptions']
@@ -295,17 +302,18 @@ def wrapsocket(sock, keyfile, certfile,
elif settings['allowloaddefaultcerts']:
# This is a no-op on old Python.
sslcontext.load_default_certs()
caloaded = True
else:
caloaded = False
try:
- sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
+ sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname,
+ server_side=serverside)
except ssl.SSLError:
# If we're doing certificate verification and no CA certs are loaded,
# that is almost certainly the reason why verification failed. Provide
# a hint to the user.
# Only modern ssl module exposes SSLContext.get_ca_certs() so we can
# only show this warning if modern ssl is available.
if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
modernssl and not sslcontext.get_ca_certs()):
@@ -313,17 +321,17 @@ def wrapsocket(sock, keyfile, certfile,
'were loaded; see '
'https://mercurial-scm.org/wiki/SecureConnections for '
'how to configure Mercurial to avoid this error)\n'))
raise
# check if wrap_socket failed silently because socket had been
# closed
# - see http://bugs.python.org/issue13721
- if not sslsocket.cipher():
+ if not serverside and not sslsocket.cipher():
raise error.Abort(_('ssl connection failed'))
sslsocket._hgstate = {
'caloaded': caloaded,
'hostname': serverhostname,
'settings': settings,
'ui': ui,
}
More information about the Mercurial-devel
mailing list