# HG changeset patch # User Yuya Nishihara # Date 1430986524 -32400 # Node ID 21b536f01edabee51e75daa4fed882ea80c1cab9 # Parent f7ccbc2776b74bb8e822cc4e788f6d0d58516bf0 ssl: prompt passphrase of client key file via ui.getpass() (issue4648) This is necessary to communicate with third-party tools through command-server channel. This requires SSLContext backported to Python 2.7.9+. It doesn't look nice to pass ui by sslkwargs, but I think it is the only way to do without touching various client codes including httpclient (aka http2). ui is mandatory if certfile is specified, so it has no default value. BTW, test-check-commit-hg.t complains that ssl_wrap_socket() has foo_bar naming. Should I bulk-replace it to sslwrapsocket() ? diff -r f7ccbc2776b7 -r 21b536f01eda mercurial/sslutil.py --- a/mercurial/sslutil.py Thu May 07 17:02:20 2015 +0900 +++ b/mercurial/sslutil.py Thu May 07 17:15:24 2015 +0900 @@ -21,7 +21,8 @@ _canloaddefaultcerts = util.safehasattr(ssl_context, 'load_default_certs') - def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE, + def ssl_wrap_socket(sock, keyfile, certfile, ui, + cert_reqs=ssl.CERT_NONE, ca_certs=None, serverhostname=None): # Allow any version of SSL starting with TLSv1 and # up. Note that specifying TLSv1 here prohibits use of @@ -35,7 +36,10 @@ sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3 if certfile is not None: - sslcontext.load_cert_chain(certfile, keyfile) + def password(): + f = keyfile or certfile + return ui.getpass(_('passphrase for %s: ') % f, '') + sslcontext.load_cert_chain(certfile, keyfile, password) sslcontext.verify_mode = cert_reqs if ca_certs is not None: sslcontext.load_verify_locations(cafile=ca_certs) @@ -51,7 +55,8 @@ raise util.Abort(_('ssl connection failed')) return sslsocket except AttributeError: - def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE, + def ssl_wrap_socket(sock, keyfile, certfile, ui, + cert_reqs=ssl.CERT_NONE, ca_certs=None, serverhostname=None): sslsocket = ssl.wrap_socket(sock, keyfile, certfile, cert_reqs=cert_reqs, ca_certs=ca_certs, @@ -67,7 +72,8 @@ import socket, httplib - def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=CERT_REQUIRED, + def ssl_wrap_socket(sock, keyfile, certfile, ui, + cert_reqs=CERT_REQUIRED, ca_certs=None, serverhostname=None): if not util.safehasattr(socket, 'ssl'): raise util.Abort(_('Python SSL support not found')) @@ -146,7 +152,7 @@ return '!' def sslkwargs(ui, host): - kws = {} + kws = {'ui': ui} hostfingerprint = ui.config('hostfingerprints', host) if hostfingerprint: return kws diff -r f7ccbc2776b7 -r 21b536f01eda mercurial/url.py --- a/mercurial/url.py Thu May 07 17:02:20 2015 +0900 +++ b/mercurial/url.py Thu May 07 17:15:24 2015 +0900 @@ -175,7 +175,7 @@ self.sock.connect((self.host, self.port)) if _generic_proxytunnel(self): # we do not support client X.509 certificates - self.sock = sslutil.ssl_wrap_socket(self.sock, None, None, + self.sock = sslutil.ssl_wrap_socket(self.sock, None, None, None, serverhostname=self.host) else: keepalive.HTTPConnection.connect(self) diff -r f7ccbc2776b7 -r 21b536f01eda tests/test-https.t --- a/tests/test-https.t Thu May 07 17:02:20 2015 +0900 +++ b/tests/test-https.t Thu May 07 17:15:24 2015 +0900 @@ -385,10 +385,19 @@ > [auth] > l.prefix = localhost > l.cert = client-cert.pem + > l.key = client-key.pem > EOT $ P=`pwd` hg id https://localhost:$HGPORT/ \ > --config auth.l.key=client-key-decrypted.pem 5fed3813f7f5 + $ printf '1234\n' | env P=`pwd` hg id https://localhost:$HGPORT/ \ + > --config ui.interactive=True --config ui.nontty=True + passphrase for client-key.pem: 5fed3813f7f5 + + $ env P=`pwd` hg id https://localhost:$HGPORT/ + abort: error: * (glob) + [255] + #endif