mercurial/sslutil.py
changeset 28652 c617614aefd2
parent 28651 4827d07073e6
child 28653 1eb0bd8adf39
equal deleted inserted replaced
28651:4827d07073e6 28652:c617614aefd2
   104             if self._supportsciphers:
   104             if self._supportsciphers:
   105                 args['ciphers'] = self._ciphers
   105                 args['ciphers'] = self._ciphers
   106 
   106 
   107             return ssl.wrap_socket(socket, **args)
   107             return ssl.wrap_socket(socket, **args)
   108 
   108 
   109 try:
   109 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
   110     def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
   110                ca_certs=None, serverhostname=None):
   111                    ca_certs=None, serverhostname=None):
   111     # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
   112         # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
   112     # that both ends support, including TLS protocols. On legacy stacks,
   113         # that both ends support, including TLS protocols. On legacy stacks,
   113     # the highest it likely goes in TLS 1.0. On modern stacks, it can
   114         # the highest it likely goes in TLS 1.0. On modern stacks, it can
   114     # support TLS 1.2.
   115         # support TLS 1.2.
   115     #
   116         #
   116     # The PROTOCOL_TLSv* constants select a specific TLS version
   117         # The PROTOCOL_TLSv* constants select a specific TLS version
   117     # only (as opposed to multiple versions). So the method for
   118         # only (as opposed to multiple versions). So the method for
   118     # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
   119         # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
   119     # disable protocols via SSLContext.options and OP_NO_* constants.
   120         # disable protocols via SSLContext.options and OP_NO_* constants.
   120     # However, SSLContext.options doesn't work unless we have the
   121         # However, SSLContext.options doesn't work unless we have the
   121     # full/real SSLContext available to us.
   122         # full/real SSLContext available to us.
   122     #
   123         #
   123     # SSLv2 and SSLv3 are broken. We ban them outright.
   124         # SSLv2 and SSLv3 are broken. We ban them outright.
   124     if modernssl:
   125         if modernssl:
   125         protocol = ssl.PROTOCOL_SSLv23
   126             protocol = ssl.PROTOCOL_SSLv23
   126     else:
   127         else:
   127         protocol = ssl.PROTOCOL_TLSv1
   128             protocol = ssl.PROTOCOL_TLSv1
   128 
   129 
   129     # TODO use ssl.create_default_context() on modernssl.
   130         # TODO use ssl.create_default_context() on modernssl.
   130     sslcontext = SSLContext(protocol)
   131         sslcontext = SSLContext(protocol)
   131 
   132 
   132     # This is a no-op on old Python.
       
   133     sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
       
   134 
       
   135     if certfile is not None:
       
   136         def password():
       
   137             f = keyfile or certfile
       
   138             return ui.getpass(_('passphrase for %s: ') % f, '')
       
   139         sslcontext.load_cert_chain(certfile, keyfile, password)
       
   140     sslcontext.verify_mode = cert_reqs
       
   141     if ca_certs is not None:
       
   142         sslcontext.load_verify_locations(cafile=ca_certs)
       
   143     else:
   133         # This is a no-op on old Python.
   144         # This is a no-op on old Python.
   134         sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
   145         sslcontext.load_default_certs()
   135 
   146 
   136         if certfile is not None:
   147     sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
   137             def password():
   148     # check if wrap_socket failed silently because socket had been
   138                 f = keyfile or certfile
   149     # closed
   139                 return ui.getpass(_('passphrase for %s: ') % f, '')
   150     # - see http://bugs.python.org/issue13721
   140             sslcontext.load_cert_chain(certfile, keyfile, password)
   151     if not sslsocket.cipher():
   141         sslcontext.verify_mode = cert_reqs
   152         raise error.Abort(_('ssl connection failed'))
   142         if ca_certs is not None:
   153     return sslsocket
   143             sslcontext.load_verify_locations(cafile=ca_certs)
       
   144         else:
       
   145             # This is a no-op on old Python.
       
   146             sslcontext.load_default_certs()
       
   147 
       
   148         sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
       
   149         # check if wrap_socket failed silently because socket had been
       
   150         # closed
       
   151         # - see http://bugs.python.org/issue13721
       
   152         if not sslsocket.cipher():
       
   153             raise error.Abort(_('ssl connection failed'))
       
   154         return sslsocket
       
   155 except AttributeError:
       
   156     raise util.Abort('this should not happen')
       
   157 
   154 
   158 def _verifycert(cert, hostname):
   155 def _verifycert(cert, hostname):
   159     '''Verify that cert (in socket.getpeercert() format) matches hostname.
   156     '''Verify that cert (in socket.getpeercert() format) matches hostname.
   160     CRLs is not handled.
   157     CRLs is not handled.
   161 
   158