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