<commit_before>##
# This module implements a general-purpose server layer for sfa.
# The same basic server should be usable on the registry, component, or
# other interfaces.
#
# TODO: investigate ways to combine this with existing PLC server?
##

import sys
import socket
import traceback
import threading
from Queue import Queue
import xmlrpclib
import SocketServer
import BaseHTTPServer
import SimpleXMLRPCServer
from OpenSSL import SSL

from sfa.util.sfalogging import logger
from sfa.util.config import Config
from sfa.util.cache import Cache 
from sfa.trust.certificate import Certificate
from sfa.trust.trustedroots import TrustedRoots

# don't hard code an api class anymore here
from sfa.generic import Generic

##
# Verification callback for pyOpenSSL. We do our own checking of keys because
# we have our own authentication spec. Thus we disable several of the normal
# prohibitions that OpenSSL places on certificates

def verify_callback(conn, x509, err, depth, preverify):
    # if the cert has been preverified, then it is ok
    if preverify:
       #print "  preverified"
       return 1


    # the certificate verification done by openssl checks a number of things
    # that we aren't interested in, so we look out for those error messages
    # and ignore them

    # XXX SMBAKER: I don't know what this error is, but it's being returned
    # xxx thierry: this most likely means the cert has a validity range in the future
    # by newer pl nodes.
    if err == 9:
       #print "  X509_V_ERR_CERT_NOT_YET_VALID"
       return 1

    # allow self-signed certificates
    if err == 18:
       #print "  X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
       return 1

    # allow certs that don't have an issuer
    if err == 20:
       #print "  X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
       return 1

    # allow chained certs with self-signed roots
    if err == 19:
        return 1
    
    # allow certs that are untrusted
    if err == 21:
       #print "  X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
       return 1

    # allow certs that are untrusted
    if err == 27:
       #print "  X509_V_ERR_CERT_UNTRUSTED"
       return 1

    # ignore X509_V_ERR_CERT_SIGNATURE_FAILURE
    if err == 7:
       return 1         

    logger.debug("  error %s in verify_callback"%err)

    return 0

##
# taken from the web (XXX find reference). Implements HTTPS xmlrpc request handler
class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """Secure XML-RPC request handler class.

    It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
    """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """Handles the HTTPS POST request.

        It was copied out from SimpleXMLRPCServer.py and modified to shutdown 
        the socket cleanly.
        """
        try:
            peer_cert = Certificate()
            peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
            generic=Generic.the_flavour()
            self.api = generic.make_api (peer_cert = peer_cert, 
                                         interface = self.server.interface, 
                                         key_file = self.server.key_file, 
                                         cert_file = self.server.cert_file,
                                         cache = self.cache)
            #logger.info("SecureXMLRpcRequestHandler.do_POST:")
            #logger.info("interface=%s"%self.server.interface)
            #logger.info("key_file=%s"%self.server.key_file)
            #logger.info("api=%s"%self.api)
            #logger.info("server=%s"%self.server)
            #logger.info("handler=%s"%self)
            # get arguments
            request = self.rfile.read(int(self.headers["content-length"]))
            remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
            self.api.remote_addr = remote_addr            
            response = self.api.handle(remote_addr, request, self.server.method_map)
        except Exception, fault:
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            logger.log_exc("server.do_POST")
            response = self.api.prepare_response(fault)
            #self.send_response(500)
            #self.end_headers()
       
        # got a valid response
        self.send_response(200)
        self.send_header("Content-type", "text/xml")
        self.send_header("Content-length", str(len(response)))
        self.end_headers()
        self.wfile.write(response)
        self.wfile.flush()
        # close db connection
        self.api.close_dbsession()
        # shut down the connection
        self.connection.shutdown() # Modified here!

##
# Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):

    def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):
        """Secure XML-RPC server.

        It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
        """
        logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s, key_file=%s"%(server_address,cert_file,key_file))<commit_msg>attempt to make api-creation code more robust and leak-proof<commit_after>##
# This module implements a general-purpose server layer for sfa.
# The same basic server should be usable on the registry, component, or
# other interfaces.
#
# TODO: investigate ways to combine this with existing PLC server?
##

import sys
import socket
import traceback
import threading
from Queue import Queue
import xmlrpclib
import SocketServer
import BaseHTTPServer
import SimpleXMLRPCServer
from OpenSSL import SSL

from sfa.util.sfalogging import logger
from sfa.util.config import Config
from sfa.util.cache import Cache 
from sfa.trust.certificate import Certificate
from sfa.trust.trustedroots import TrustedRoots

# don't hard code an api class anymore here
from sfa.generic import Generic

##
# Verification callback for pyOpenSSL. We do our own checking of keys because
# we have our own authentication spec. Thus we disable several of the normal
# prohibitions that OpenSSL places on certificates

def verify_callback(conn, x509, err, depth, preverify):
    # if the cert has been preverified, then it is ok
    if preverify:
       #print "  preverified"
       return 1


    # the certificate verification done by openssl checks a number of things
    # that we aren't interested in, so we look out for those error messages
    # and ignore them

    # XXX SMBAKER: I don't know what this error is, but it's being returned
    # xxx thierry: this most likely means the cert has a validity range in the future
    # by newer pl nodes.
    if err == 9:
       #print "  X509_V_ERR_CERT_NOT_YET_VALID"
       return 1

    # allow self-signed certificates
    if err == 18:
       #print "  X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
       return 1

    # allow certs that don't have an issuer
    if err == 20:
       #print "  X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
       return 1

    # allow chained certs with self-signed roots
    if err == 19:
        return 1
    
    # allow certs that are untrusted
    if err == 21:
       #print "  X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
       return 1

    # allow certs that are untrusted
    if err == 27:
       #print "  X509_V_ERR_CERT_UNTRUSTED"
       return 1

    # ignore X509_V_ERR_CERT_SIGNATURE_FAILURE
    if err == 7:
       return 1         

    logger.debug("  error %s in verify_callback"%err)

    return 0

##
# taken from the web (XXX find reference). Implements HTTPS xmlrpc request handler
class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """Secure XML-RPC request handler class.

    It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
    """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """Handles the HTTPS POST request.

        It was copied out from SimpleXMLRPCServer.py and modified to shutdown 
        the socket cleanly.
        """
        try:
            peer_cert = Certificate()
            peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate())
            generic=Generic.the_flavour()
            self.api = generic.make_api (peer_cert = peer_cert, 
                                         interface = self.server.interface, 
                                         key_file = self.server.key_file, 
                                         cert_file = self.server.cert_file,
                                         cache = self.cache)
            #logger.info("SecureXMLRpcRequestHandler.do_POST:")
            #logger.info("interface=%s"%self.server.interface)
            #logger.info("key_file=%s"%self.server.key_file)
            #logger.info("api=%s"%self.api)
            #logger.info("server=%s"%self.server)
            #logger.info("handler=%s"%self)
            # get arguments
            request = self.rfile.read(int(self.headers["content-length"]))
            remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
            self.api.remote_addr = remote_addr            
            response = self.api.handle(remote_addr, request, self.server.method_map)
        except Exception, fault:
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            logger.log_exc("server.do_POST")
            response = self.api.prepare_response(fault)
            #self.send_response(500)
            #self.end_headers()
       
        # avoid session/connection leaks : do this no matter what 
        finally:
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)
            self.wfile.flush()
            # close db connection
            self.api.close_dbsession()
            # shut down the connection
            self.connection.shutdown() # Modified here!

##
# Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):

    def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):
        """Secure XML-RPC server.

        It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
        """
        logger.debug("SecureXMLRPCServer.__init__, server_address=%s, cert_file=%s, key_file=%s"%(server_address,cert_file,key_file))
{'fraction_non_alphanumeric': 0.07702606212067119, 'fraction_numerical': 0.009103891467333095, 'mean_word_length': 2.571883965572203, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4796108', 'n_tokens_mistral': 3292, 'n_tokens_neox': 3138, 'n_words': 958}