mercurial/sslutil.py
changeset 29227 dffe78d80a6c
parent 29226 33006bd6a1d7
child 29228 9b07017ba528
equal deleted inserted replaced
29226:33006bd6a1d7 29227:dffe78d80a6c
   289         kws['cert_reqs'] = ssl.CERT_REQUIRED
   289         kws['cert_reqs'] = ssl.CERT_REQUIRED
   290         return kws
   290         return kws
   291 
   291 
   292     return kws
   292     return kws
   293 
   293 
   294 class validator(object):
   294 def validatesocket(sock, strict=False):
   295     def __init__(self, ui=None, host=None):
   295     """Validate a socket meets security requiremnets.
   296         pass
   296 
   297 
   297     The passed socket must have been created with ``wrapsocket()``.
   298     def __call__(self, sock, strict=False):
   298     """
   299         host = sock._hgstate['hostname']
   299     host = sock._hgstate['hostname']
   300         ui = sock._hgstate['ui']
   300     ui = sock._hgstate['ui']
   301 
   301 
   302         if not sock.cipher(): # work around http://bugs.python.org/issue13721
   302     if not sock.cipher(): # work around http://bugs.python.org/issue13721
   303             raise error.Abort(_('%s ssl connection error') % host)
   303         raise error.Abort(_('%s ssl connection error') % host)
   304         try:
   304     try:
   305             peercert = sock.getpeercert(True)
   305         peercert = sock.getpeercert(True)
   306             peercert2 = sock.getpeercert()
   306         peercert2 = sock.getpeercert()
   307         except AttributeError:
   307     except AttributeError:
   308             raise error.Abort(_('%s ssl connection error') % host)
   308         raise error.Abort(_('%s ssl connection error') % host)
   309 
   309 
   310         if not peercert:
   310     if not peercert:
   311             raise error.Abort(_('%s certificate error: '
   311         raise error.Abort(_('%s certificate error: '
   312                                'no certificate received') % host)
   312                            'no certificate received') % host)
   313 
   313 
   314         # If a certificate fingerprint is pinned, use it and only it to
   314     # If a certificate fingerprint is pinned, use it and only it to
   315         # validate the remote cert.
   315     # validate the remote cert.
   316         hostfingerprints = ui.configlist('hostfingerprints', host)
   316     hostfingerprints = ui.configlist('hostfingerprints', host)
   317         peerfingerprint = util.sha1(peercert).hexdigest()
   317     peerfingerprint = util.sha1(peercert).hexdigest()
   318         nicefingerprint = ":".join([peerfingerprint[x:x + 2]
   318     nicefingerprint = ":".join([peerfingerprint[x:x + 2]
   319             for x in xrange(0, len(peerfingerprint), 2)])
   319         for x in xrange(0, len(peerfingerprint), 2)])
   320         if hostfingerprints:
   320     if hostfingerprints:
   321             fingerprintmatch = False
   321         fingerprintmatch = False
   322             for hostfingerprint in hostfingerprints:
   322         for hostfingerprint in hostfingerprints:
   323                 if peerfingerprint.lower() == \
   323             if peerfingerprint.lower() == \
   324                         hostfingerprint.replace(':', '').lower():
   324                     hostfingerprint.replace(':', '').lower():
   325                     fingerprintmatch = True
   325                 fingerprintmatch = True
   326                     break
   326                 break
   327             if not fingerprintmatch:
   327         if not fingerprintmatch:
   328                 raise error.Abort(_('certificate for %s has unexpected '
   328             raise error.Abort(_('certificate for %s has unexpected '
   329                                    'fingerprint %s') % (host, nicefingerprint),
   329                                'fingerprint %s') % (host, nicefingerprint),
   330                                  hint=_('check hostfingerprint configuration'))
   330                              hint=_('check hostfingerprint configuration'))
   331             ui.debug('%s certificate matched fingerprint %s\n' %
   331         ui.debug('%s certificate matched fingerprint %s\n' %
   332                      (host, nicefingerprint))
   332                  (host, nicefingerprint))
   333             return
   333         return
   334 
   334 
   335         # If insecure connections were explicitly requested via --insecure,
   335     # If insecure connections were explicitly requested via --insecure,
   336         # print a warning and do no verification.
   336     # print a warning and do no verification.
   337         #
   337     #
   338         # It may seem odd that this is checked *after* host fingerprint pinning.
   338     # It may seem odd that this is checked *after* host fingerprint pinning.
   339         # This is for backwards compatibility (for now). The message is also
   339     # This is for backwards compatibility (for now). The message is also
   340         # the same as below for BC.
   340     # the same as below for BC.
   341         if ui.insecureconnections:
   341     if ui.insecureconnections:
   342             ui.warn(_('warning: %s certificate with fingerprint %s not '
   342         ui.warn(_('warning: %s certificate with fingerprint %s not '
   343                       'verified (check hostfingerprints or web.cacerts '
   343                   'verified (check hostfingerprints or web.cacerts '
   344                       'config setting)\n') %
   344                   'config setting)\n') %
       
   345                 (host, nicefingerprint))
       
   346         return
       
   347 
       
   348     if not sock._hgstate['caloaded']:
       
   349         if strict:
       
   350             raise error.Abort(_('%s certificate with fingerprint %s not '
       
   351                                 'verified') % (host, nicefingerprint),
       
   352                               hint=_('check hostfingerprints or '
       
   353                                      'web.cacerts config setting'))
       
   354         else:
       
   355             ui.warn(_('warning: %s certificate with fingerprint %s '
       
   356                       'not verified (check hostfingerprints or '
       
   357                       'web.cacerts config setting)\n') %
   345                     (host, nicefingerprint))
   358                     (host, nicefingerprint))
   346             return
   359 
   347 
   360         return
   348         if not sock._hgstate['caloaded']:
   361 
   349             if strict:
   362     msg = _verifycert(peercert2, host)
   350                 raise error.Abort(_('%s certificate with fingerprint %s not '
   363     if msg:
   351                                     'verified') % (host, nicefingerprint),
   364         raise error.Abort(_('%s certificate error: %s') % (host, msg),
   352                                   hint=_('check hostfingerprints or '
   365                          hint=_('configure hostfingerprint %s or use '
   353                                          'web.cacerts config setting'))
   366                                 '--insecure to connect insecurely') %
   354             else:
   367                               nicefingerprint)
   355                 ui.warn(_('warning: %s certificate with fingerprint %s '
       
   356                           'not verified (check hostfingerprints or '
       
   357                           'web.cacerts config setting)\n') %
       
   358                         (host, nicefingerprint))
       
   359 
       
   360             return
       
   361 
       
   362         msg = _verifycert(peercert2, host)
       
   363         if msg:
       
   364             raise error.Abort(_('%s certificate error: %s') % (host, msg),
       
   365                              hint=_('configure hostfingerprint %s or use '
       
   366                                     '--insecure to connect insecurely') %
       
   367                                   nicefingerprint)