##// END OF EJS Templates
sslutil: make keyfile and certfile arguments consistent between 2.6+ and 2.5-
Augie Fackler -
r19808:3b82d412 default
parent child Browse files
Show More
@@ -1,170 +1,170 b''
1 1 # sslutil.py - SSL handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9 import os
10 10
11 11 from mercurial import util
12 12 from mercurial.i18n import _
13 13 try:
14 14 # avoid using deprecated/broken FakeSocket in python 2.6
15 15 import ssl
16 16 CERT_REQUIRED = ssl.CERT_REQUIRED
17 17 PROTOCOL_SSLv23 = ssl.PROTOCOL_SSLv23
18 18 PROTOCOL_TLSv1 = ssl.PROTOCOL_TLSv1
19 19 def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
20 20 cert_reqs=ssl.CERT_NONE, ca_certs=None):
21 21 sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
22 22 cert_reqs=cert_reqs, ca_certs=ca_certs,
23 23 ssl_version=ssl_version)
24 24 # check if wrap_socket failed silently because socket had been closed
25 25 # - see http://bugs.python.org/issue13721
26 26 if not sslsocket.cipher():
27 27 raise util.Abort(_('ssl connection failed'))
28 28 return sslsocket
29 29 except ImportError:
30 30 CERT_REQUIRED = 2
31 31
32 32 PROTOCOL_SSLv23 = 2
33 33 PROTOCOL_TLSv1 = 3
34 34
35 35 import socket, httplib
36 36
37 def ssl_wrap_socket(sock, key_file, cert_file, ssl_version=PROTOCOL_TLSv1,
37 def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
38 38 cert_reqs=CERT_REQUIRED, ca_certs=None):
39 39 if not util.safehasattr(socket, 'ssl'):
40 40 raise util.Abort(_('Python SSL support not found'))
41 41 if ca_certs:
42 42 raise util.Abort(_(
43 43 'certificate checking requires Python 2.6'))
44 44
45 ssl = socket.ssl(sock, key_file, cert_file)
45 ssl = socket.ssl(sock, keyfile, certfile)
46 46 return httplib.FakeSocket(sock, ssl)
47 47
48 48 def _verifycert(cert, hostname):
49 49 '''Verify that cert (in socket.getpeercert() format) matches hostname.
50 50 CRLs is not handled.
51 51
52 52 Returns error message if any problems are found and None on success.
53 53 '''
54 54 if not cert:
55 55 return _('no certificate received')
56 56 dnsname = hostname.lower()
57 57 def matchdnsname(certname):
58 58 return (certname == dnsname or
59 59 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
60 60
61 61 san = cert.get('subjectAltName', [])
62 62 if san:
63 63 certnames = [value.lower() for key, value in san if key == 'DNS']
64 64 for name in certnames:
65 65 if matchdnsname(name):
66 66 return None
67 67 if certnames:
68 68 return _('certificate is for %s') % ', '.join(certnames)
69 69
70 70 # subject is only checked when subjectAltName is empty
71 71 for s in cert.get('subject', []):
72 72 key, value = s[0]
73 73 if key == 'commonName':
74 74 try:
75 75 # 'subject' entries are unicode
76 76 certname = value.lower().encode('ascii')
77 77 except UnicodeEncodeError:
78 78 return _('IDN in certificate not supported')
79 79 if matchdnsname(certname):
80 80 return None
81 81 return _('certificate is for %s') % certname
82 82 return _('no commonName or subjectAltName found in certificate')
83 83
84 84
85 85 # CERT_REQUIRED means fetch the cert from the server all the time AND
86 86 # validate it against the CA store provided in web.cacerts.
87 87 #
88 88 # We COMPLETELY ignore CERT_REQUIRED on Python <= 2.5, as it's totally
89 89 # busted on those versions.
90 90
91 91 def sslkwargs(ui, host):
92 92 cacerts = ui.config('web', 'cacerts')
93 93 forcetls = ui.configbool('ui', 'tls', default=True)
94 94 if forcetls:
95 95 ssl_version = PROTOCOL_TLSv1
96 96 else:
97 97 ssl_version = PROTOCOL_SSLv23
98 98 hostfingerprint = ui.config('hostfingerprints', host)
99 99 kws = {'ssl_version': ssl_version,
100 100 }
101 101 if cacerts and not hostfingerprint:
102 102 cacerts = util.expandpath(cacerts)
103 103 if not os.path.exists(cacerts):
104 104 raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
105 105 kws.update({'ca_certs': cacerts,
106 106 'cert_reqs': CERT_REQUIRED,
107 107 })
108 108 return kws
109 109
110 110 class validator(object):
111 111 def __init__(self, ui, host):
112 112 self.ui = ui
113 113 self.host = host
114 114
115 115 def __call__(self, sock, strict=False):
116 116 host = self.host
117 117 cacerts = self.ui.config('web', 'cacerts')
118 118 hostfingerprint = self.ui.config('hostfingerprints', host)
119 119 if not getattr(sock, 'getpeercert', False): # python 2.5 ?
120 120 if hostfingerprint:
121 121 raise util.Abort(_("host fingerprint for %s can't be "
122 122 "verified (Python too old)") % host)
123 123 if strict:
124 124 raise util.Abort(_("certificate for %s can't be verified "
125 125 "(Python too old)") % host)
126 126 if self.ui.configbool('ui', 'reportoldssl', True):
127 127 self.ui.warn(_("warning: certificate for %s can't be verified "
128 128 "(Python too old)\n") % host)
129 129 return
130 130
131 131 if not sock.cipher(): # work around http://bugs.python.org/issue13721
132 132 raise util.Abort(_('%s ssl connection error') % host)
133 133 try:
134 134 peercert = sock.getpeercert(True)
135 135 peercert2 = sock.getpeercert()
136 136 except AttributeError:
137 137 raise util.Abort(_('%s ssl connection error') % host)
138 138
139 139 if not peercert:
140 140 raise util.Abort(_('%s certificate error: '
141 141 'no certificate received') % host)
142 142 peerfingerprint = util.sha1(peercert).hexdigest()
143 143 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
144 144 for x in xrange(0, len(peerfingerprint), 2)])
145 145 if hostfingerprint:
146 146 if peerfingerprint.lower() != \
147 147 hostfingerprint.replace(':', '').lower():
148 148 raise util.Abort(_('certificate for %s has unexpected '
149 149 'fingerprint %s') % (host, nicefingerprint),
150 150 hint=_('check hostfingerprint configuration'))
151 151 self.ui.debug('%s certificate matched fingerprint %s\n' %
152 152 (host, nicefingerprint))
153 153 elif cacerts:
154 154 msg = _verifycert(peercert2, host)
155 155 if msg:
156 156 raise util.Abort(_('%s certificate error: %s') % (host, msg),
157 157 hint=_('configure hostfingerprint %s or use '
158 158 '--insecure to connect insecurely') %
159 159 nicefingerprint)
160 160 self.ui.debug('%s certificate successfully verified\n' % host)
161 161 elif strict:
162 162 raise util.Abort(_('%s certificate with fingerprint %s not '
163 163 'verified') % (host, nicefingerprint),
164 164 hint=_('check hostfingerprints or web.cacerts '
165 165 'config setting'))
166 166 else:
167 167 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
168 168 'verified (check hostfingerprints or web.cacerts '
169 169 'config setting)\n') %
170 170 (host, nicefingerprint))
General Comments 0
You need to be logged in to leave comments. Login now