##// END OF EJS Templates
ssl: refactor sslkwargs - move things around a bit, preparing for next change
Mads Kiilerich -
r22574:a00a7951 default
parent child Browse files
Show More
@@ -1,170 +1,172 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 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 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 cacerts = ui.config('web', 'cacerts')
93 92 forcetls = ui.configbool('ui', 'tls', default=True)
94 93 if forcetls:
95 94 ssl_version = PROTOCOL_TLSv1
96 95 else:
97 96 ssl_version = PROTOCOL_SSLv23
98 hostfingerprint = ui.config('hostfingerprints', host)
99 97 kws = {'ssl_version': ssl_version,
100 98 }
101 if cacerts and not hostfingerprint:
99 hostfingerprint = ui.config('hostfingerprints', host)
100 if hostfingerprint:
101 return kws
102 cacerts = ui.config('web', 'cacerts')
103 if cacerts:
102 104 cacerts = util.expandpath(cacerts)
103 105 if not os.path.exists(cacerts):
104 106 raise util.Abort(_('could not find web.cacerts: %s') % cacerts)
105 107 kws.update({'ca_certs': cacerts,
106 108 'cert_reqs': CERT_REQUIRED,
107 109 })
108 110 return kws
109 111
110 112 class validator(object):
111 113 def __init__(self, ui, host):
112 114 self.ui = ui
113 115 self.host = host
114 116
115 117 def __call__(self, sock, strict=False):
116 118 host = self.host
117 119 cacerts = self.ui.config('web', 'cacerts')
118 120 hostfingerprint = self.ui.config('hostfingerprints', host)
119 121 if not getattr(sock, 'getpeercert', False): # python 2.5 ?
120 122 if hostfingerprint:
121 123 raise util.Abort(_("host fingerprint for %s can't be "
122 124 "verified (Python too old)") % host)
123 125 if strict:
124 126 raise util.Abort(_("certificate for %s can't be verified "
125 127 "(Python too old)") % host)
126 128 if self.ui.configbool('ui', 'reportoldssl', True):
127 129 self.ui.warn(_("warning: certificate for %s can't be verified "
128 130 "(Python too old)\n") % host)
129 131 return
130 132
131 133 if not sock.cipher(): # work around http://bugs.python.org/issue13721
132 134 raise util.Abort(_('%s ssl connection error') % host)
133 135 try:
134 136 peercert = sock.getpeercert(True)
135 137 peercert2 = sock.getpeercert()
136 138 except AttributeError:
137 139 raise util.Abort(_('%s ssl connection error') % host)
138 140
139 141 if not peercert:
140 142 raise util.Abort(_('%s certificate error: '
141 143 'no certificate received') % host)
142 144 peerfingerprint = util.sha1(peercert).hexdigest()
143 145 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
144 146 for x in xrange(0, len(peerfingerprint), 2)])
145 147 if hostfingerprint:
146 148 if peerfingerprint.lower() != \
147 149 hostfingerprint.replace(':', '').lower():
148 150 raise util.Abort(_('certificate for %s has unexpected '
149 151 'fingerprint %s') % (host, nicefingerprint),
150 152 hint=_('check hostfingerprint configuration'))
151 153 self.ui.debug('%s certificate matched fingerprint %s\n' %
152 154 (host, nicefingerprint))
153 155 elif cacerts:
154 156 msg = _verifycert(peercert2, host)
155 157 if msg:
156 158 raise util.Abort(_('%s certificate error: %s') % (host, msg),
157 159 hint=_('configure hostfingerprint %s or use '
158 160 '--insecure to connect insecurely') %
159 161 nicefingerprint)
160 162 self.ui.debug('%s certificate successfully verified\n' % host)
161 163 elif strict:
162 164 raise util.Abort(_('%s certificate with fingerprint %s not '
163 165 'verified') % (host, nicefingerprint),
164 166 hint=_('check hostfingerprints or web.cacerts '
165 167 'config setting'))
166 168 else:
167 169 self.ui.warn(_('warning: %s certificate with fingerprint %s not '
168 170 'verified (check hostfingerprints or web.cacerts '
169 171 'config setting)\n') %
170 172 (host, nicefingerprint))
General Comments 0
You need to be logged in to leave comments. Login now