1 """A library for integrating pyOpenSSL with CherryPy.
2
3 The ssl module must be importable for SSL functionality.
4
5 To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
6 BuiltinSSLAdapter.
7
8 ssl_adapter.certificate: the filename of the server SSL certificate.
9 ssl_adapter.private_key: the filename of the server's private key file.
10 """
11
12 try:
13 import ssl
14 except ImportError:
15 ssl = None
16
17
18 from web import wsgiserver
19
21
22 if not cert:
23 return None
24
25 key_map = { 'countryName':'C',
26 'stateOrProvinceName':'ST',
27 'localityName':'L',
28 'organizationName':'O',
29 'organizationalUnitName':'OU',
30 'commonName':'CN',
31
32
33
34
35
36
37
38
39 'emailAddress':'Email',
40 }
41
42 DN_string = ["subject=",]
43 cert_dict = {}
44
45 for rdn in cert:
46 for key, item in rdn:
47 if key in key_map:
48 cert_dict["%s_%s" % ( prefix, key_map[key] ) ] = item
49 DN_string.append( "%s=%s" % ( key_map[key], item ))
50
51 cert_dict[prefix] = "/".join( DN_string )
52
53 return cert_dict
54
55
57 """A wrapper for integrating Python's builtin ssl module with CherryPy."""
58
59 - def __init__(self, certificate, private_key, certificate_chain=None, client_CA=None):
60 if ssl is None:
61 raise ImportError("You must install the ssl module to use HTTPS.")
62 self.certificate = certificate
63 self.private_key = private_key
64 self.certificate_chain = certificate_chain
65 self.client_CA = client_CA
66
67 - def bind(self, sock):
68 """Wrap and return the given socket."""
69 return sock
70
71 - def wrap(self, sock):
72 """Wrap and return the given socket, plus WSGI environ entries."""
73 try:
74 if self.client_CA:
75 s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
76 server_side=True, certfile=self.certificate,
77 keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23,
78 ca_certs=self.client_CA,
79 cert_reqs=ssl.CERT_REQUIRED)
80 else:
81 s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
82 server_side=True, certfile=self.certificate,
83 keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
84 except ssl.SSLError, e:
85 if e.errno == ssl.SSL_ERROR_EOF:
86
87
88
89 return None, {}
90 elif e.errno == ssl.SSL_ERROR_SSL:
91 if e.args[1].endswith('http request'):
92
93 raise wsgiserver.NoSSLError
94 raise
95 return s, self.get_environ(s)
96
97
99 """Create WSGI environ entries to be merged into each request."""
100 cipher = sock.cipher()
101 ssl_environ = {
102 "wsgi.url_scheme": "https",
103 "HTTPS": "on",
104 'SSL_PROTOCOL': cipher[1],
105 'SSL_CIPHER': cipher[0]
106
107
108 }
109
110
111 client_cert = sock.getpeercert()
112
113 client_cert_subject = decode_cert( "SSL_CLIENT_S_DN", client_cert["subject"] )
114
115
116
117 ssl_environ.update( {
118
119
120
121 "SSL_CLIENT_V_END": client_cert["notAfter"],
122
123
124
125
126
127 } )
128
129 ssl_environ.update( client_cert_subject )
130
131
132 ssl_environ.update( {
133
134
135
136
137
138
139
140 } )
141
142
143 return ssl_environ
144
145 - def makefile(self, sock, mode='r', bufsize=-1):
147