[PATCH 2 of 2] url: debug print ssl certificate if connection failed
Yuya Nishihara
yuya at tcha.org
Sat Jan 8 23:52:56 CST 2011
Mads Kiilerich wrote:
> Yuya Nishihara wrote, On 01/08/2011 02:08 PM:
> > # HG changeset patch
> > # User Yuya Nishihara<yuya at tcha.org>
> > # Date 1294491295 -32400
> > # Branch stable
> > # Node ID e541b8d268395ece35aff45f8116c6041d22d91f
> > # Parent 160f24a7970a402d0f7df1912b92419d7acbd8f3
> > url: debug print ssl certificate if connection failed
> >
> > If a server provides self-signed certificate,
>
> More precisely: If a server provides certificate that can't be verified
> by the configured cacerts ...
>
> > ssl.wrap_socket raises exception
> > like "routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed".
> >
> > As of Python 2.6 or 2.7, it doesn't provide a legitimate
>
> More precisely: "supported" or "official"
>
> > way to decode
> > certificate to human-readable text without established ssl socket.
> > So _printservercert() tries to use _ssl._test_decode_cert(), which is available
> > at least on Python 2.6.6 or 2.7.1.
>
> Interesting ;-)
>
> I guess this also could be used to show the content of cacerts files and
> make them less opaque.
>
> Code in this area is however not simple to maintain, and using these
> internal functions will not improve the situation ...
Yeah, that's why I tought this patch could be unacceptable.
> > diff --git a/mercurial/url.py b/mercurial/url.py
> > --- a/mercurial/url.py
> > +++ b/mercurial/url.py
> > @@ -290,8 +290,11 @@ if has_https:
> > import ssl
> > _ssl_wrap_socket = ssl.wrap_socket
> > CERT_REQUIRED = ssl.CERT_REQUIRED
> > + SSLError = ssl.SSLError
> > except ImportError:
> > CERT_REQUIRED = 2
> > + class SSLError(Exception):
> > + pass
> >
> > def _ssl_wrap_socket(sock, key_file, cert_file,
> > cert_reqs=CERT_REQUIRED, ca_certs=None):
> > @@ -521,6 +524,27 @@ def _printcert(cert, write):
> > for key, value in cert.get('subjectAltName', []):
> > write(' subjectAltName.%s: %s\n' % (key, value))
> >
> > +def _printservercert(addr, write):
> > + """Fetch server certificate and print human-readable details"""
> > + import tempfile
>
> We usually do imports at the top of the file. Demandimport makes it cheap.
>
> > + try: # needs Python 2.6
> > + import ssl, _ssl
> > + decode_certfile = _ssl._test_decode_cert
> > + except (ImportError, AttributeError):
> > + return
> > + der = ssl.get_server_certificate(addr)
> > + if not der:
> > + return
> > + fh, fn = tempfile.mkstemp(prefix='hg-servercert-')
> > + try:
> > + f = os.fdopen(fh, 'w')
> > + f.write(der)
> > + f.close()
> > + cert = decode_certfile(fn)
> > + finally:
> > + os.unlink(fn)
> > + _printcert(cert, write)
> > +
> > if has_https:
> > class BetterHTTPS(httplib.HTTPSConnection):
> > send = keepalive.safesend
> > @@ -535,9 +559,15 @@ if has_https:
> >
> > if cacerts:
> > sock = _create_connection((self.host, self.port))
> > - self.sock = _ssl_wrap_socket(sock, self.key_file,
> > - self.cert_file, cert_reqs=CERT_REQUIRED,
> > - ca_certs=cacerts)
> > + try:
> > + self.sock = _ssl_wrap_socket(sock, self.key_file,
> > + self.cert_file, cert_reqs=CERT_REQUIRED,
> > + ca_certs=cacerts)
> > + except SSLError:
> > + if self.ui.debugflag:
> > + _printservercert((self.host, self.port),
> > + self.ui.debug)
> > + raise
> > msg = _verifycert(self.sock.getpeercert(), self.host)
> > if msg:
> > if self.ui.debugflag:
>
> It would be nice to have some tests for this. It might not be feasible
> to do it in the test suite as long as we don't have conditional sections
> inside .t files, but could you either add some tests to test-https.t and
> disable them (for example by putting "#" at the beginning of the lines)
> or provide a working fragment in the commit message?
I'll try putting some tests on test-https.t.
For now, I've posted some example output in [PATCH 0] email.
> With this patch it is possible to show much of the server certificate
> info. Cool. But in which cases can that be used? Can you describe some
> use cases? What do the user see and how do he understand it and what
> will he then do?
We could ask a user to attach --debug output on bug report.
Currently it needs extra effort to get to know why Mercurial complains
about certificate.
> (If the subject is the same as the issuer then the certificate is
> self-signed and the certificate from get_server_certificate can thus
> probably be used in cacerts. That case can however also just be detected
> with two consecutive calls to get_server_certificate.)
like this? This seems more solid than poking internal _ssl module.
f.write(ssl.get_server_certificate(addr))
f.close()
ssl.get_server_certificate(ssl.get_server_certificate(addr),
ca_certs=f.name)
Yuya,
More information about the Mercurial-devel
mailing list