add some basic logging output to certmaster
[certmaster.git] / certmaster / minion / AuthedXMLRPCServer.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 # Modifications by Seth Vidal - 2007
17
18 import sys
19 import socket
20 import SimpleXMLRPCServer
21 from func import SSLCommon
22 import OpenSSL
23 import SocketServer
24
25
26 class AuthedSimpleXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
27
28 # For some reason, httplib closes the connection right after headers
29 # have been sent if the connection is _not_ HTTP/1.1, which results in
30 # a "Bad file descriptor" error when the client tries to read from the socket
31 protocol_version = "HTTP/1.1"
32
33 def setup(self):
34 """
35 We need to use socket._fileobject Because SSL.Connection
36 doesn't have a 'dup'. Not exactly sure WHY this is, but
37 this is backed up by comments in socket.py and SSL/connection.c
38 """
39 self.connection = self.request # for doPOST
40 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
41 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
42
43 def do_POST(self):
44 self.server._this_request = (self.request, self.client_address)
45 try:
46 SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
47 except socket.timeout:
48 pass
49 except (socket.error, OpenSSL.SSL.SysCallError), e:
50 print "Error (%s): socket error - '%s'" % (self.client_address, e)
51
52
53 class BaseAuthedXMLRPCServer(SocketServer.ThreadingMixIn):
54 def __init__(self, address, authinfo_callback=None):
55 self.allow_reuse_address = 1
56 self.logRequests = 1
57 self.authinfo_callback = authinfo_callback
58
59 self.funcs = {}
60 self.instance = None
61
62 def get_authinfo(self, request, client_address):
63 print 'down here'
64 if self.authinfo_callback:
65 return self.authinfo_callback(request, client_address)
66 return None
67
68
69 class AuthedSSLXMLRPCServer(BaseAuthedXMLRPCServer, SSLCommon.BaseSSLServer, SimpleXMLRPCServer.SimpleXMLRPCServer):
70 """ Extension to allow more fine-tuned SSL handling """
71
72 def __init__(self, address, pkey, cert, ca_cert, authinfo_callback=None, timeout=None):
73 BaseAuthedXMLRPCServer.__init__(self, address, authinfo_callback)
74 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler)
75 SSLCommon.BaseSSLServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler, pkey, cert, ca_cert, timeout=timeout)
76
77
78
79 class AuthedXMLRPCServer(BaseAuthedXMLRPCServer, SSLCommon.BaseServer, SimpleXMLRPCServer.SimpleXMLRPCServer):
80
81 def __init__(self, address, authinfo_callback=None):
82 BaseAuthedXMLRPCServer.__init__(self, address, authinfo_callback)
83 SSLCommon.BaseServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler)
84
85
86 ###########################################################
87 # Testing stuff
88 ###########################################################
89
90 class ReqHandler:
91 def ping(self, callerid, trynum):
92 print 'clearly not'
93 print callerid
94 print trynum
95 return "pong %d / %d" % (callerid, trynum)
96
97 class TestServer(AuthedSSLXMLRPCServer):
98 """
99 SSL XMLRPC server that authenticates clients based on their certificate.
100 """
101
102 def __init__(self, address, pkey, cert, ca_cert):
103 AuthedSSLXMLRPCServer.__init__(self, address, pkey, cert, ca_cert, self.auth_cb)
104
105 def _dispatch(self, method, params):
106 if method == 'trait_names' or method == '_getAttributeNames':
107 return dir(self)
108 # if we have _this_request then we get the peer cert from it
109 # handling all the authZ checks in _dispatch() means we don't even call the method
110 # for whatever it wants to do and we have the method name.
111
112 if hasattr(self, '_this_request'):
113 r,a = self._this_request
114 p = r.get_peer_certificate()
115 print dir(p)
116 print p.get_subject()
117 else:
118 print 'no cert'
119
120 return "your mom"
121
122 def auth_cb(self, request, client_address):
123 peer_cert = request.get_peer_certificate()
124 return peer_cert.get_subject().CN
125
126
127 if __name__ == '__main__':
128 if len(sys.argv) < 4:
129 print "Usage: python AuthdXMLRPCServer.py key cert ca_cert"
130 sys.exit(1)
131
132 pkey = sys.argv[1]
133 cert = sys.argv[2]
134 ca_cert = sys.argv[3]
135
136 print "Starting the server."
137 server = TestServer(('localhost', 51234), pkey, cert, ca_cert)
138 h = ReqHandler()
139 server.register_instance(h)
140 server.serve_forever()