# HG changeset patch # User Matt Harbison # Date 2017-03-30 04:27:46 # Node ID 9a9f95214f4615d9eaa5f453f267a05cd1b2b787 # Parent 14af04391fb9cf6a0a59a9c781a0854f4254a2c5 debug: add a method to check the state of, and built an SSL cert chain This is only useful on Windows, and avoids the need to use Internet Explorer to build the certificate chain. I can see this being extended in the future to print information about the certificate(s) to help debug issues on any platform. Maybe even perform some of the python checks listed on the secure connections wiki page. But for now, all I need is 1) a command that can be invoked in a setup script to ensure the certificate is installed, and 2) a command that the user can run if/when a certificate changes in the future. It would have been nice to leverage the sslutil library to pick up host specific settings, but attempting to use sslutil.wrapsocket() failed the 'not sslsocket.cipher()' check in it and aborted. The output is a little more chatty than some commands, but I've seen the update take 10+ seconds, and this is only a debug command. diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -13,6 +13,7 @@ import operator import os import random import socket +import ssl import string import sys import tempfile @@ -2057,6 +2058,66 @@ def debugsetparents(ui, repo, rev1, rev2 with repo.wlock(): repo.setparents(r1, r2) +@command('debugssl', [], '[SOURCE]', optionalrepo=True) +def debugssl(ui, repo, source=None, **opts): + '''test a secure connection to a server + + This builds the certificate chain for the server on Windows, installing the + missing intermediates and trusted root via Windows Update if necessary. It + does nothing on other platforms. + + If SOURCE is omitted, the 'default' path will be used. If a URL is given, + that server is used. See :hg:`help urls` for more information. + + If the update succeeds, retry the original operation. Otherwise, the cause + of the SSL error is likely another issue. + ''' + if pycompat.osname != 'nt': + raise error.Abort(_('Certificate chain building is only possible on ' + 'Windows')) + + if not source: + source = "default" + elif not repo: + raise error.Abort(_("there is no Mercurial repository here, and no " + "server specified")) + + source, branches = hg.parseurl(ui.expandpath(source)) + url = util.url(source) + addr = None + + if url.scheme == 'https': + addr = (url.host, url.port or 443) + elif url.scheme == 'ssh': + addr = (url.host, url.port or 22) + else: + raise error.Abort(_("Only https and ssh connections are supported")) + + from . import win32 + + s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS, + cert_reqs=ssl.CERT_NONE, ca_certs=None) + + try: + s.connect(addr) + cert = s.getpeercert(True) + + ui.status(_('Checking the certificate chain for %s.\n') % url.host) + + complete = win32.checkcertificatechain(cert, build=False) + + if not complete: + ui.status(_('The certificate chain is incomplete. Updating... ')) + + if not win32.checkcertificatechain(cert): + ui.status(_('Failed.\n')) + else: + ui.status(_('Done.\n')) + else: + ui.status(_('The full certificate chain is available.\n')) + finally: + s.close() + @command('debugsub', [('r', 'rev', '', _('revision to check'), _('REV'))], diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -108,6 +108,7 @@ Show debug commands if there are no othe debugrevlog debugrevspec debugsetparents + debugssl debugsub debugsuccessorssets debugtemplate @@ -283,6 +284,7 @@ Show all commands + options debugrevlog: changelog, manifest, dir, dump debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized debugsetparents: + debugssl: debugsub: rev debugsuccessorssets: closest debugtemplate: rev, define diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -952,6 +952,7 @@ Test list of internal help commands debugrevspec parse and apply a revision specification debugsetparents manually set the parents of the current working directory + debugssl test a secure connection to a server debugsub (no help text available) debugsuccessorssets show set of successors for revision