optionally allow a passwd callback for opening the ssl keys
[certmaster.git] / certmaster / SSLCommon.py
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
5 #
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
10 #
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14 #
15 # Copyright 2005 Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
16
17 import os, sys
18 from OpenSSL import SSL
19 import SSLConnection
20 import httplib
21 import socket
22 import SocketServer
23
24 def our_verify(connection, x509, errNum, errDepth, preverifyOK):
25 # print "Verify: errNum = %s, errDepth = %s, preverifyOK = %s" % (errNum, errDepth, preverifyOK)
26
27 # preverifyOK should tell us whether or not the client's certificate
28 # correctly authenticates against the CA chain
29 return preverifyOK
30
31
32 def CreateSSLContext(pkey, cert, ca_cert, passwd_callback=None):
33 for f in pkey, cert, ca_cert:
34 if f and not os.access(f, os.R_OK):
35 print "%s does not exist or is not readable." % f
36 os._exit(1)
37
38 ctx = SSL.Context(SSL.SSLv3_METHOD) # SSLv3 only
39 if passwd_callback:
40 ctx.set_passwd_cb = passwd_callback
41
42 ctx.use_certificate_file(cert)
43 ctx.use_privatekey_file(pkey)
44 ctx.load_client_ca(ca_cert)
45 ctx.load_verify_locations(ca_cert)
46 verify = SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT
47 ctx.set_verify(verify, our_verify)
48 ctx.set_verify_depth(10)
49 ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_TLSv1)
50 return ctx
51
52
53
54 class BaseServer(SocketServer.TCPServer):
55 allow_reuse_address = 1
56
57 def __init__(self, server_addr, req_handler):
58 self._quit = False
59 self.allow_reuse_address = 1
60 SocketServer.TCPServer.__init__(self, server_addr, req_handler)
61
62 def stop(self):
63 self._quit = True
64
65 def serve_forever(self):
66 while not self._quit:
67 self.handle_request()
68 self.server_close()
69
70
71 class BaseSSLServer(BaseServer):
72 """ SSL-enabled variant """
73
74 def __init__(self, server_address, req_handler, pkey, cert, ca_cert, timeout=None):
75 self._timeout = timeout
76 self.ssl_ctx = CreateSSLContext(pkey, cert, ca_cert)
77
78 BaseServer.__init__(self, server_address, req_handler)
79
80 sock = socket.socket(self.address_family, self.socket_type)
81 con = SSL.Connection(self.ssl_ctx, sock)
82 self.socket = SSLConnection.SSLConnection(con)
83 if sys.version_info[:3] >= (2, 3, 0):
84 self.socket.settimeout(self._timeout)
85 self.server_bind()
86 self.server_activate()
87
88 host, port = self.socket.getsockname()[:2]
89 self.server_name = socket.getfqdn(host)
90 self.server_port = port
91
92
93 class HTTPSConnection(httplib.HTTPConnection):
94 "This class allows communication via SSL."
95
96 response_class = httplib.HTTPResponse
97
98 def __init__(self, host, port=None, ssl_context=None, strict=None, timeout=None):
99 httplib.HTTPConnection.__init__(self, host, port, strict)
100 self.ssl_ctx = ssl_context
101 self._timeout = timeout
102
103 def connect(self):
104 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
105 con = SSL.Connection(self.ssl_ctx, sock)
106 self.sock = SSLConnection.SSLConnection(con)
107 if sys.version_info[:3] >= (2, 3, 0):
108 self.sock.settimeout(self._timeout)
109 self.sock.connect((self.host, self.port))
110
111
112 class HTTPS(httplib.HTTP):
113 """Compatibility with 1.5 httplib interface
114
115 Python 1.5.2 did not have an HTTPS class, but it defined an
116 interface for sending http requests that is also useful for
117 https.
118 """
119
120 _connection_class = HTTPSConnection
121
122 def __init__(self, host='', port=None, ssl_context=None, strict=None, timeout=None):
123 self._setup(self._connection_class(host, port, ssl_context, strict, timeout))
124