From 714f5224ad222d47dc958b9517f058e81f65f257 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 18 Apr 2015 20:07:09 +0200 Subject: [PATCH] force tls for https mercurial hooks, see issue #704 --- .../resources/sonia/scm/python/scmhooks.py | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py index e9a58d589f..0a216d56b1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/python/scmhooks.py @@ -36,7 +36,43 @@ # changegroup.scm = python:scmhooks.callback # -import os, urllib, urllib2 +import os, urllib, urllib2, httplib, socket, ssl + +# Python 2's urllib2 does not allow you to select the TLS dialect, +# and by default uses a SSLv23 compatibility negotiation implementation. +# Besides being vulnerable to POODLE, the OSX implementation doesn't +# work correctly, failing to connect to servers that respond only to +# TLS1.0+. These classes help set up TLS support for urllib2. +# source: https://gist.github.com/flandr/74be22d1c3d7c1dfefdd + +class TLS1Connection(httplib.HTTPSConnection): + """Like HTTPSConnection but more specific""" + def __init__(self, host, **kwargs): + httplib.HTTPSConnection.__init__(self, host, **kwargs) + + def connect(self): + """Overrides HTTPSConnection.connect to specify TLS version""" + # Standard implementation from HTTPSConnection, which is not + # designed for extension, unfortunately + sock = socket.create_connection((self.host, self.port), + self.timeout, self.source_address) + if getattr(self, '_tunnel_host', None): + self.sock = sock + self._tunnel() + + # This is the only difference; default wrap_socket uses SSLv23 + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + ssl_version=ssl.PROTOCOL_TLSv1) + +class TLS1Handler(urllib2.HTTPSHandler): + """Like HTTPSHandler but more specific""" + def __init__(self): + urllib2.HTTPSHandler.__init__(self) + + def https_open(self, req): + return self.do_open(TLS1Connection, req) + +# hook begin baseUrl = os.environ['SCM_URL'] challenge = os.environ['SCM_CHALLENGE'] @@ -56,7 +92,11 @@ def callHookUrl(ui, repo, hooktype, node): data = urllib.urlencode({'node': node, 'challenge': challenge, 'credentials': credentials, 'repositoryPath': repo.root}) # open url but ignore proxy settings proxy_handler = urllib2.ProxyHandler({}) - opener = urllib2.build_opener(proxy_handler) + # use tls for https connections + if url.startswith("https"): + opener = urllib2.build_opener(proxy_handler, TLS1Handler()) + else: + opener = urllib2.build_opener(proxy_handler) req = urllib2.Request(url, data) conn = opener.open(req) if conn.code >= 200 and conn.code < 300: