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