comparison mercurial/sslutil.py @ 25415:21b536f01eda

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() ?
author Yuya Nishihara <yuya@tcha.org>
date Thu, 07 May 2015 17:15:24 +0900
parents 241d98d84aed
children 9d1c61715939
comparison
equal deleted inserted replaced
25414:f7ccbc2776b7 25415:21b536f01eda
19 try: 19 try:
20 ssl_context = ssl.SSLContext 20 ssl_context = ssl.SSLContext
21 _canloaddefaultcerts = util.safehasattr(ssl_context, 21 _canloaddefaultcerts = util.safehasattr(ssl_context,
22 'load_default_certs') 22 'load_default_certs')
23 23
24 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE, 24 def ssl_wrap_socket(sock, keyfile, certfile, ui,
25 cert_reqs=ssl.CERT_NONE,
25 ca_certs=None, serverhostname=None): 26 ca_certs=None, serverhostname=None):
26 # Allow any version of SSL starting with TLSv1 and 27 # Allow any version of SSL starting with TLSv1 and
27 # up. Note that specifying TLSv1 here prohibits use of 28 # up. Note that specifying TLSv1 here prohibits use of
28 # newer standards (like TLSv1_2), so this is the right way 29 # newer standards (like TLSv1_2), so this is the right way
29 # to do this. Note that in the future it'd be better to 30 # to do this. Note that in the future it'd be better to
33 # maintainers for us, but that breaks too many things to 34 # maintainers for us, but that breaks too many things to
34 # do it in a hurry. 35 # do it in a hurry.
35 sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) 36 sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
36 sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3 37 sslcontext.options &= ssl.OP_NO_SSLv2 & ssl.OP_NO_SSLv3
37 if certfile is not None: 38 if certfile is not None:
38 sslcontext.load_cert_chain(certfile, keyfile) 39 def password():
40 f = keyfile or certfile
41 return ui.getpass(_('passphrase for %s: ') % f, '')
42 sslcontext.load_cert_chain(certfile, keyfile, password)
39 sslcontext.verify_mode = cert_reqs 43 sslcontext.verify_mode = cert_reqs
40 if ca_certs is not None: 44 if ca_certs is not None:
41 sslcontext.load_verify_locations(cafile=ca_certs) 45 sslcontext.load_verify_locations(cafile=ca_certs)
42 elif _canloaddefaultcerts: 46 elif _canloaddefaultcerts:
43 sslcontext.load_default_certs() 47 sslcontext.load_default_certs()
49 # - see http://bugs.python.org/issue13721 53 # - see http://bugs.python.org/issue13721
50 if not sslsocket.cipher(): 54 if not sslsocket.cipher():
51 raise util.Abort(_('ssl connection failed')) 55 raise util.Abort(_('ssl connection failed'))
52 return sslsocket 56 return sslsocket
53 except AttributeError: 57 except AttributeError:
54 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=ssl.CERT_NONE, 58 def ssl_wrap_socket(sock, keyfile, certfile, ui,
59 cert_reqs=ssl.CERT_NONE,
55 ca_certs=None, serverhostname=None): 60 ca_certs=None, serverhostname=None):
56 sslsocket = ssl.wrap_socket(sock, keyfile, certfile, 61 sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
57 cert_reqs=cert_reqs, ca_certs=ca_certs, 62 cert_reqs=cert_reqs, ca_certs=ca_certs,
58 ssl_version=ssl.PROTOCOL_TLSv1) 63 ssl_version=ssl.PROTOCOL_TLSv1)
59 # check if wrap_socket failed silently because socket had been 64 # check if wrap_socket failed silently because socket had been
65 except ImportError: 70 except ImportError:
66 CERT_REQUIRED = 2 71 CERT_REQUIRED = 2
67 72
68 import socket, httplib 73 import socket, httplib
69 74
70 def ssl_wrap_socket(sock, keyfile, certfile, cert_reqs=CERT_REQUIRED, 75 def ssl_wrap_socket(sock, keyfile, certfile, ui,
76 cert_reqs=CERT_REQUIRED,
71 ca_certs=None, serverhostname=None): 77 ca_certs=None, serverhostname=None):
72 if not util.safehasattr(socket, 'ssl'): 78 if not util.safehasattr(socket, 'ssl'):
73 raise util.Abort(_('Python SSL support not found')) 79 raise util.Abort(_('Python SSL support not found'))
74 if ca_certs: 80 if ca_certs:
75 raise util.Abort(_( 81 raise util.Abort(_(
144 if _canloaddefaultcerts: 150 if _canloaddefaultcerts:
145 return None 151 return None
146 return '!' 152 return '!'
147 153
148 def sslkwargs(ui, host): 154 def sslkwargs(ui, host):
149 kws = {} 155 kws = {'ui': ui}
150 hostfingerprint = ui.config('hostfingerprints', host) 156 hostfingerprint = ui.config('hostfingerprints', host)
151 if hostfingerprint: 157 if hostfingerprint:
152 return kws 158 return kws
153 cacerts = ui.config('web', 'cacerts') 159 cacerts = ui.config('web', 'cacerts')
154 if cacerts == '!': 160 if cacerts == '!':