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