[PATCH stable] https: verify server certificate with cacert when Python doesn't (issue2407)

Mads Kiilerich mads at kiilerich.com
Thu Sep 30 18:04:56 CDT 2010


  Thomas Arendsen Hein wrote, On 09/30/2010 11:57 AM:
> The doctest is quite verbose. Personally I would prefer something in
> a tests/test-*.py file. Bonus could be to do the tests for notAfter
> and notBefore with a calculated date (e.g. now +- 1
> week/month/year), otherwise the test will mysteriously fail when the
> system clock is wrong or the test is run in late 2037 :)

That will be a very welcome warning that y2k38 is approaching ;-)

>> +    for s in cert.get('subject', []):
>> +        if s[0][0] == 'commonName':
>> +            certname = s[0][1].lower()
>> +            if dnsname == certname or (
>> +                certname.startswith('*.') and
>> +                dnsname.endswith(certname[1:]) and
>> +                '.' not in dnsname[:-len(certname) + 1]
>> +                ):
>> +                return None
>> +            return _('server certificate error: certificate for %s from %s'
>> +                ) % (certname, dnsname)
> Whoa, '.' not in dnsname[:-len(certname) + 1] looks strange, maybe
> this is more readable:
>
>      for s in cert.get('subject', []):
>          key, value = s[0]
>          if key == 'commonName':
>              certname = value.lower()
>              if dnsname == certname or (
>                  certname.startswith('*.') and
>                  dnsname[dnsname.find('.') + 1:] == certname[2:]):
>                  return None
>              return _('server certificate error: certificate for %s from %s'
>                  ) % (certname, dnsname)

Right, but it will fail if no '.' is found in dnsname.

The following will fail less often - but is still not perfect:
     certname == '*' + dnsname[dnsname.find('.'):]

> Side note: As soon as we switch to python>= 2.5 we can use:
>              if dnsname == certname or (
>                  certname.startswith('*.') and
>                  dnsname.partition('.')[2] == certname[2:]):
>                  return None

Or just
	certname == '*.' + dnsname.partition('.')[2]
or until 2.5
	certname == '*.' + (dnsname.split('.', 1) + [''])[1]
- which however also isn't very readable ...


Thanks for the comments.

I pushed an updated patch to stable as f2937d6492c5.


Perhaps we should do this to warn that https without cacerts isn't secure:

--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -532,6 +532,8 @@
                  self.ui.debug(_('%s certificate successfully verified\n') %
                      self.host)
              else:
+                self.ui.warn(_("certificate for %s can't be verified "
+                    "without web.cacerts\n") % self.host)
                  httplib.HTTPSConnection.connect(self)


Finally, as far as I can see all other web.* settings applies to hgweb, so shouldn't web.cacerts have been something like ui.cacerts?

/Mads



More information about the Mercurial-devel mailing list