mercurial/sslutil.py
changeset 29249 cca59ef27e60
parent 29248 e6de6ef3e426
child 29253 9da137faaa9c
equal deleted inserted replaced
29248:e6de6ef3e426 29249:cca59ef27e60
   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 def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE,
   109 def _determinecertoptions(ui, host):
   110                ca_certs=None, serverhostname=None):
   110     """Determine certificate options for a connections.
       
   111 
       
   112     Returns a tuple of (cert_reqs, ca_certs).
       
   113     """
       
   114     # If a host key fingerprint is on file, it is the only thing that matters
       
   115     # and CA certs don't come into play.
       
   116     hostfingerprint = ui.config('hostfingerprints', host)
       
   117     if hostfingerprint:
       
   118         return ssl.CERT_NONE, None
       
   119 
       
   120     # The code below sets up CA verification arguments. If --insecure is
       
   121     # used, we don't take CAs into consideration, so return early.
       
   122     if ui.insecureconnections:
       
   123         return ssl.CERT_NONE, None
       
   124 
       
   125     cacerts = ui.config('web', 'cacerts')
       
   126 
       
   127     # If a value is set in the config, validate against a path and load
       
   128     # and require those certs.
       
   129     if cacerts:
       
   130         cacerts = util.expandpath(cacerts)
       
   131         if not os.path.exists(cacerts):
       
   132             raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
       
   133 
       
   134         return ssl.CERT_REQUIRED, cacerts
       
   135 
       
   136     # No CAs in config. See if we can load defaults.
       
   137     cacerts = _defaultcacerts()
       
   138 
       
   139     # We found an alternate CA bundle to use. Load it.
       
   140     if cacerts:
       
   141         ui.debug('using %s to enable OS X system CA\n' % cacerts)
       
   142         ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
       
   143         return ssl.CERT_REQUIRED, cacerts
       
   144 
       
   145     # FUTURE this can disappear once wrapsocket() is secure by default.
       
   146     if _canloaddefaultcerts:
       
   147         return ssl.CERT_REQUIRED, None
       
   148 
       
   149     return ssl.CERT_NONE, None
       
   150 
       
   151 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
   111     """Add SSL/TLS to a socket.
   152     """Add SSL/TLS to a socket.
   112 
   153 
   113     This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
   154     This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
   114     choices based on what security options are available.
   155     choices based on what security options are available.
   115 
   156 
   120       server (and client) support SNI, this tells the server which certificate
   161       server (and client) support SNI, this tells the server which certificate
   121       to use.
   162       to use.
   122     """
   163     """
   123     if not serverhostname:
   164     if not serverhostname:
   124         raise error.Abort('serverhostname argument is required')
   165         raise error.Abort('serverhostname argument is required')
       
   166 
       
   167     cert_reqs, ca_certs = _determinecertoptions(ui, serverhostname)
   125 
   168 
   126     # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
   169     # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
   127     # that both ends support, including TLS protocols. On legacy stacks,
   170     # that both ends support, including TLS protocols. On legacy stacks,
   128     # the highest it likely goes in TLS 1.0. On modern stacks, it can
   171     # the highest it likely goes in TLS 1.0. On modern stacks, it can
   129     # support TLS 1.2.
   172     # support TLS 1.2.
   241             return dummycert
   284             return dummycert
   242 
   285 
   243     return None
   286     return None
   244 
   287 
   245 def sslkwargs(ui, host):
   288 def sslkwargs(ui, host):
   246     """Determine arguments to pass to wrapsocket().
   289     return {}
   247 
       
   248     ``host`` is the hostname being connected to.
       
   249     """
       
   250     kws = {}
       
   251 
       
   252     # If a host key fingerprint is on file, it is the only thing that matters
       
   253     # and CA certs don't come into play.
       
   254     hostfingerprint = ui.config('hostfingerprints', host)
       
   255     if hostfingerprint:
       
   256         return kws
       
   257 
       
   258     # The code below sets up CA verification arguments. If --insecure is
       
   259     # used, we don't take CAs into consideration, so return early.
       
   260     if ui.insecureconnections:
       
   261         return kws
       
   262 
       
   263     cacerts = ui.config('web', 'cacerts')
       
   264 
       
   265     # If a value is set in the config, validate against a path and load
       
   266     # and require those certs.
       
   267     if cacerts:
       
   268         cacerts = util.expandpath(cacerts)
       
   269         if not os.path.exists(cacerts):
       
   270             raise error.Abort(_('could not find web.cacerts: %s') % cacerts)
       
   271 
       
   272         kws.update({'ca_certs': cacerts,
       
   273                     'cert_reqs': ssl.CERT_REQUIRED})
       
   274         return kws
       
   275 
       
   276     # No CAs in config. See if we can load defaults.
       
   277     cacerts = _defaultcacerts()
       
   278 
       
   279     # We found an alternate CA bundle to use. Load it.
       
   280     if cacerts:
       
   281         ui.debug('using %s to enable OS X system CA\n' % cacerts)
       
   282         ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts')
       
   283         kws.update({'ca_certs': cacerts,
       
   284                     'cert_reqs': ssl.CERT_REQUIRED})
       
   285         return kws
       
   286 
       
   287     # FUTURE this can disappear once wrapsocket() is secure by default.
       
   288     if _canloaddefaultcerts:
       
   289         kws['cert_reqs'] = ssl.CERT_REQUIRED
       
   290         return kws
       
   291 
       
   292     return kws
       
   293 
   290 
   294 def validatesocket(sock, strict=False):
   291 def validatesocket(sock, strict=False):
   295     """Validate a socket meets security requiremnets.
   292     """Validate a socket meets security requiremnets.
   296 
   293 
   297     The passed socket must have been created with ``wrapsocket()``.
   294     The passed socket must have been created with ``wrapsocket()``.