2 # This module implements a general-purpose server layer for sfa.
3 # The same basic server should be usable on the registry, component, or
6 # TODO: investigate ways to combine this with existing PLC server?
13 from Queue import Queue
17 import SimpleXMLRPCServer
18 from OpenSSL import SSL
20 from sfa.util.sfalogging import logger
21 from sfa.util.config import Config
22 from sfa.util.cache import Cache
23 from sfa.trust.certificate import Certificate
24 from sfa.trust.trustedroots import TrustedRoots
26 # don't hard code an api class anymore here
27 from sfa.generic import Generic
30 # Verification callback for pyOpenSSL. We do our own checking of keys because
31 # we have our own authentication spec. Thus we disable several of the normal
32 # prohibitions that OpenSSL places on certificates
34 def verify_callback(conn, x509, err, depth, preverify):
35 # if the cert has been preverified, then it is ok
41 # the certificate verification done by openssl checks a number of things
42 # that we aren't interested in, so we look out for those error messages
45 # XXX SMBAKER: I don't know what this error is, but it's being returned
48 #print " X509_V_ERR_CERT_NOT_YET_VALID"
51 # allow self-signed certificates
53 #print " X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
56 # allow certs that don't have an issuer
58 #print " X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
61 # allow chained certs with self-signed roots
65 # allow certs that are untrusted
67 #print " X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
70 # allow certs that are untrusted
72 #print " X509_V_ERR_CERT_UNTRUSTED"
75 print " error", err, "in verify_callback"
80 # taken from the web (XXX find reference). Implements HTTPS xmlrpc request handler
81 class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
82 """Secure XML-RPC request handler class.
84 It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
87 self.connection = self.request
88 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
89 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
92 """Handles the HTTPS POST request.
94 It was copied out from SimpleXMLRPCServer.py and modified to shutdown
98 peer_cert = Certificate()
99 peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
100 generic=Generic.the_flavour()
101 self.api = generic.make_api (peer_cert = peer_cert,
102 interface = self.server.interface,
103 key_file = self.server.key_file,
104 cert_file = self.server.cert_file,
106 #logger.info("SecureXMLRpcRequestHandler.do_POST:")
107 #logger.info("interface=%s"%self.server.interface)
108 #logger.info("key_file=%s"%self.server.key_file)
109 #logger.info("api=%s"%self.api)
110 #logger.info("server=%s"%self.server)
111 #logger.info("handler=%s"%self)
113 request = self.rfile.read(int(self.headers["content-length"]))
114 remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
115 self.api.remote_addr = remote_addr
116 response = self.api.handle(remote_addr, request, self.server.method_map)
117 except Exception, fault:
118 # This should only happen if the module is buggy
119 # internal error, report as HTTP server error
120 logger.log_exc("server.do_POST")
121 response = self.api.prepare_response(fault)
122 #self.send_response(500)
125 # got a valid response
126 self.send_response(200)
127 self.send_header("Content-type", "text/xml")
128 self.send_header("Content-length", str(len(response)))
130 self.wfile.write(response)
132 # shut down the connection
134 self.connection.shutdown() # Modified here!
137 # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
138 class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
140 def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):
141 """Secure XML-RPC server.
143 It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
145 logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s"%(server_address,cert_file))
146 self.logRequests = logRequests
147 self.interface = None
148 self.key_file = key_file
149 self.cert_file = cert_file
151 # add cache to the request handler
152 HandlerClass.cache = Cache()
153 #for compatibility with python 2.4 (centos53)
154 if sys.version_info < (2, 5):
155 SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self)
157 SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, True, None)
158 SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
159 ctx = SSL.Context(SSL.SSLv23_METHOD)
160 ctx.use_privatekey_file(key_file)
161 ctx.use_certificate_file(cert_file)
162 # If you wanted to verify certs against known CAs.. this is how you would do it
163 #ctx.load_verify_locations('/etc/sfa/trusted_roots/plc.gpo.gid')
165 trusted_cert_files = TrustedRoots(config.get_trustedroots_dir()).get_file_list()
166 for cert_file in trusted_cert_files:
167 ctx.load_verify_locations(cert_file)
168 ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback)
169 ctx.set_verify_depth(5)
170 ctx.set_app_data(self)
171 self.socket = SSL.Connection(ctx, socket.socket(self.address_family,
174 self.server_activate()
178 # Convert an exception on the server to a full stack trace and send it to
181 def _dispatch(self, method, params):
182 logger.debug("SecureXMLRPCServer._dispatch, method=%s"%method)
184 return SimpleXMLRPCServer.SimpleXMLRPCDispatcher._dispatch(self, method, params)
186 # can't use format_exc() as it is not available in jython yet
188 type, value, tb = sys.exc_info()
189 raise xmlrpclib.Fault(1,''.join(traceback.format_exception(type, value, tb)))
191 # override this one from the python 2.7 code
192 # originally defined in class TCPServer
193 def shutdown_request(self, request):
194 """Called to shutdown and close an individual request."""
196 # the std python 2.7 code just attempts a request.shutdown(socket.SHUT_WR)
197 # this works fine with regular sockets
198 # However we are dealing with an instance of OpenSSL.SSL.Connection instead
199 # This one only supports shutdown(), and in addition this does not
200 # always perform as expected
201 # ---------- std python 2.7 code
203 #explicitly shutdown. socket.close() merely releases
204 #the socket and waits for GC to perform the actual close.
205 request.shutdown(socket.SHUT_WR)
207 pass #some platforms may raise ENOTCONN here
210 # we are dealing with an OpenSSL.Connection object,
211 # try to shut it down but never mind if that fails
212 try: request.shutdown()
215 self.close_request(request)
217 ## From Active State code: http://code.activestate.com/recipes/574454/
218 # This is intended as a drop-in replacement for the ThreadingMixIn class in
219 # module SocketServer of the standard lib. Instead of spawning a new thread
220 # for each request, requests are processed by of pool of reusable threads.
221 class ThreadPoolMixIn(SocketServer.ThreadingMixIn):
223 use a thread pool instead of a new thread on every request
225 # XX TODO: Make this configurable
227 # numThreads = config.SFA_SERVER_NUM_THREADS
229 allow_reuse_address = True # seems to fix socket.error on server restart
231 def serve_forever(self):
233 Handle one request at a time until doomsday.
235 # set up the threadpool
236 self.requests = Queue()
238 for x in range(self.numThreads):
239 t = threading.Thread(target = self.process_request_thread)
245 self.handle_request()
250 def process_request_thread(self):
252 obtain request from queue instead of directly from server socket
255 SocketServer.ThreadingMixIn.process_request_thread(self, *self.requests.get())
258 def handle_request(self):
260 simply collect requests and put them on the queue for the workers.
263 request, client_address = self.get_request()
266 if self.verify_request(request, client_address):
267 self.requests.put((request, client_address))
269 class ThreadedServer(ThreadPoolMixIn, SecureXMLRPCServer):