Patchwork [2,of,3] sslutil: accept serverside argument to wrapsocket

login
register
mail settings
Submitter Gregory Szorc
Date July 12, 2016, 10:32 p.m.
Message ID <a9a5f1ca8f96a1292cf9.1468362746@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/15812/
State Superseded
Headers show

Comments

Gregory Szorc - July 12, 2016, 10:32 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@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).

Patch

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,
     }