/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2009-01-28 11:23:12 UTC
  • mto: This revision was merged to the branch mainline in revision 327.
  • Revision ID: teddy@fukt.bsnet.se-20090128112312-fccq15ef11z330u3
Start of new pipe-based IPC mechanism.

* mandos (TCP_handler.handle): Open the IPC pipe, write messages to it
                               and close it when done.
  (ForkingMixInWithPipe): New.
  (IPv6_TCPServer): Inherit from "ForkingMixInWithPipe" instead of
                   "SocketServer.ForkingMixIn".
  (IPv6_TCPServer.handle_ipc): New.

Show diffs side-by-side

added added

removed removed

Lines of Context:
656
656
    def handle(self):
657
657
        logger.info(u"TCP connection from: %s",
658
658
                    unicode(self.client_address))
659
 
        session = (gnutls.connection
660
 
                   .ClientSession(self.request,
661
 
                                  gnutls.connection
662
 
                                  .X509Credentials()))
663
 
        
664
 
        line = self.request.makefile().readline()
665
 
        logger.debug(u"Protocol version: %r", line)
666
 
        try:
667
 
            if int(line.strip().split()[0]) > 1:
668
 
                raise RuntimeError
669
 
        except (ValueError, IndexError, RuntimeError), error:
670
 
            logger.error(u"Unknown protocol version: %s", error)
671
 
            return
672
 
        
673
 
        # Note: gnutls.connection.X509Credentials is really a generic
674
 
        # GnuTLS certificate credentials object so long as no X.509
675
 
        # keys are added to it.  Therefore, we can use it here despite
676
 
        # using OpenPGP certificates.
677
 
        
678
 
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
679
 
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
680
 
        #                "+DHE-DSS"))
681
 
        # Use a fallback default, since this MUST be set.
682
 
        priority = self.server.settings.get("priority", "NORMAL")
683
 
        (gnutls.library.functions
684
 
         .gnutls_priority_set_direct(session._c_object,
685
 
                                     priority, None))
686
 
        
687
 
        try:
688
 
            session.handshake()
689
 
        except gnutls.errors.GNUTLSError, error:
690
 
            logger.warning(u"Handshake failed: %s", error)
691
 
            # Do not run session.bye() here: the session is not
692
 
            # established.  Just abandon the request.
693
 
            return
694
 
        logger.debug(u"Handshake succeeded")
695
 
        try:
696
 
            fpr = fingerprint(peer_certificate(session))
697
 
        except (TypeError, gnutls.errors.GNUTLSError), error:
698
 
            logger.warning(u"Bad certificate: %s", error)
699
 
            session.bye()
700
 
            return
701
 
        logger.debug(u"Fingerprint: %s", fpr)
702
 
        for c in self.server.clients:
703
 
            if c.fingerprint == fpr:
704
 
                client = c
705
 
                break
706
 
        else:
707
 
            logger.warning(u"Client not found for fingerprint: %s",
708
 
                           fpr)
709
 
            session.bye()
710
 
            return
711
 
        # Have to check if client.still_valid(), since it is possible
712
 
        # that the client timed out while establishing the GnuTLS
713
 
        # session.
714
 
        if not client.still_valid():
715
 
            logger.warning(u"Client %(name)s is invalid",
716
 
                           vars(client))
717
 
            session.bye()
718
 
            return
719
 
        ## This won't work here, since we're in a fork.
720
 
        # client.checked_ok()
721
 
        sent_size = 0
722
 
        while sent_size < len(client.secret):
723
 
            sent = session.send(client.secret[sent_size:])
724
 
            logger.debug(u"Sent: %d, remaining: %d",
725
 
                         sent, len(client.secret)
726
 
                         - (sent_size + sent))
727
 
            sent_size += sent
728
 
        session.bye()
729
 
 
730
 
 
731
 
class IPv6_TCPServer(SocketServer.ForkingMixIn,
 
659
        logger.debug(u"Pipe: %d", self.server.pipe[1])
 
660
        # Open IPC pipe to parent process
 
661
        with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
 
662
            session = (gnutls.connection
 
663
                       .ClientSession(self.request,
 
664
                                      gnutls.connection
 
665
                                      .X509Credentials()))
 
666
            
 
667
            line = self.request.makefile().readline()
 
668
            logger.debug(u"Protocol version: %r", line)
 
669
            try:
 
670
                if int(line.strip().split()[0]) > 1:
 
671
                    raise RuntimeError
 
672
            except (ValueError, IndexError, RuntimeError), error:
 
673
                logger.error(u"Unknown protocol version: %s", error)
 
674
                return
 
675
            
 
676
            # Note: gnutls.connection.X509Credentials is really a
 
677
            # generic GnuTLS certificate credentials object so long as
 
678
            # no X.509 keys are added to it.  Therefore, we can use it
 
679
            # here despite using OpenPGP certificates.
 
680
            
 
681
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
682
            #                     "+AES-256-CBC", "+SHA1",
 
683
            #                     "+COMP-NULL", "+CTYPE-OPENPGP",
 
684
            #                     "+DHE-DSS"))
 
685
            # Use a fallback default, since this MUST be set.
 
686
            priority = self.server.settings.get("priority", "NORMAL")
 
687
            (gnutls.library.functions
 
688
             .gnutls_priority_set_direct(session._c_object,
 
689
                                         priority, None))
 
690
 
 
691
            try:
 
692
                session.handshake()
 
693
            except gnutls.errors.GNUTLSError, error:
 
694
                logger.warning(u"Handshake failed: %s", error)
 
695
                # Do not run session.bye() here: the session is not
 
696
                # established.  Just abandon the request.
 
697
                return
 
698
            logger.debug(u"Handshake succeeded")
 
699
            try:
 
700
                fpr = fingerprint(peer_certificate(session))
 
701
            except (TypeError, gnutls.errors.GNUTLSError), error:
 
702
                logger.warning(u"Bad certificate: %s", error)
 
703
                session.bye()
 
704
                return
 
705
            logger.debug(u"Fingerprint: %s", fpr)
 
706
            for c in self.server.clients:
 
707
                if c.fingerprint == fpr:
 
708
                    client = c
 
709
                    break
 
710
            else:
 
711
                logger.warning(u"Client not found for fingerprint: %s",
 
712
                               fpr)
 
713
                ipc.write("NOTFOUND %s\n" % fpr)
 
714
                session.bye()
 
715
                return
 
716
            # Have to check if client.still_valid(), since it is
 
717
            # possible that the client timed out while establishing
 
718
            # the GnuTLS session.
 
719
            if not client.still_valid():
 
720
                logger.warning(u"Client %(name)s is invalid",
 
721
                               vars(client))
 
722
                ipc.write("INVALID %s\n" % client.name)
 
723
                session.bye()
 
724
                return
 
725
            ipc.write("SENDING %s\n" % client.name)
 
726
            ## This won't work here, since we're in a fork.
 
727
            # client.checked_ok()
 
728
            sent_size = 0
 
729
            while sent_size < len(client.secret):
 
730
                sent = session.send(client.secret[sent_size:])
 
731
                logger.debug(u"Sent: %d, remaining: %d",
 
732
                             sent, len(client.secret)
 
733
                             - (sent_size + sent))
 
734
                sent_size += sent
 
735
            session.bye()
 
736
 
 
737
 
 
738
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
 
739
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
 
740
    Assumes a gobject.MainLoop event loop.
 
741
    """
 
742
    def process_request(self, request, client_address):
 
743
        """This overrides and wraps the original process_request().
 
744
        This function creates a new pipe in self.pipe 
 
745
        """
 
746
        self.pipe = os.pipe()
 
747
        super(ForkingMixInWithPipe,
 
748
              self).process_request(request, client_address)
 
749
        os.close(self.pipe[1])  # close write end
 
750
        # Call "handle_ipc" for both data and EOF events
 
751
        gobject.io_add_watch(self.pipe[0],
 
752
                             gobject.IO_IN | gobject.IO_HUP,
 
753
                             self.handle_ipc)
 
754
    def handle_ipc(source, condition):
 
755
        """Dummy function; override as necessary"""
 
756
        os.close(source)
 
757
        return False
 
758
 
 
759
 
 
760
class IPv6_TCPServer(ForkingMixInWithPipe,
732
761
                     SocketServer.TCPServer, object):
733
762
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
734
763
    Attributes:
786
815
            return super(IPv6_TCPServer, self).server_activate()
787
816
    def enable(self):
788
817
        self.enabled = True
 
818
    def handle_ipc(self, source, condition, file_objects={}):
 
819
        logger.debug("Handling IPC: %r : %r", source, condition)
 
820
        
 
821
        # Turn a file descriptor into a Python file object
 
822
        if source not in file_objects:
 
823
            file_objects[source] = os.fdopen(source, "r", 1)
 
824
        
 
825
        # Read a line from the file object
 
826
        cmdline = file_objects[source].readline()
 
827
        if not cmdline:             # Empty line means end of file
 
828
            # close the IPC pipe
 
829
            logger.debug("Closing: %r", source)
 
830
            file_objects[source].close()
 
831
            del file_objects[source]
 
832
 
 
833
            # Stop calling this function
 
834
            return False
 
835
        
 
836
        logger.debug("IPC command: %r\n" % cmdline)
 
837
        
 
838
        # Parse and act on command
 
839
        cmd, args = cmdline.split(None, 1)
 
840
        if cmd == "NOTFOUND":
 
841
            pass                # xxx
 
842
        elif cmd == "INVALID":
 
843
            pass                # xxx
 
844
        elif cmd == "SENDING":
 
845
            pass                # xxx
 
846
        else:
 
847
            logger.error("Unknown IPC command: %r", cmdline)
 
848
        
 
849
        # Keep calling this function
 
850
        return True
789
851
 
790
852
 
791
853
def string_to_delta(interval):