/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-18 00:16:57 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090118001657-6mo3ae73ldy5tegf
* debian/mandos-client.postinst: Converted to Bourne shell.  Also
                                 minor message change.
* debian/mandos-client.postrm: Minor message change.
* debian/mandos.postinst: Converted to Bourne shell.  Also minor
                          message change.
* debian/mandos.prerm: Minor message change.

Show diffs side-by-side

added added

removed removed

Lines of Context:
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.8"
 
69
version = "1.0.5"
70
70
 
71
71
logger = logging.Logger('mandos')
72
72
syslogger = (logging.handlers.SysLogHandler
73
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
74
74
              address = "/dev/log"))
75
75
syslogger.setFormatter(logging.Formatter
76
 
                       ('Mandos [%(process)d]: %(levelname)s:'
77
 
                        ' %(message)s'))
 
76
                       ('Mandos: %(levelname)s: %(message)s'))
78
77
logger.addHandler(syslogger)
79
78
 
80
79
console = logging.StreamHandler()
81
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
82
 
                                       ' %(levelname)s: %(message)s'))
 
80
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
 
81
                                       ' %(message)s'))
83
82
logger.addHandler(console)
84
83
 
85
84
class AvahiError(Exception):
114
113
    """
115
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
116
115
                 servicetype = None, port = None, TXT = None,
117
 
                 domain = "", host = "", max_renames = 32768,
118
 
                 protocol = avahi.PROTO_UNSPEC):
 
116
                 domain = "", host = "", max_renames = 32768):
119
117
        self.interface = interface
120
118
        self.name = name
121
119
        self.type = servicetype
125
123
        self.host = host
126
124
        self.rename_count = 0
127
125
        self.max_renames = max_renames
128
 
        self.protocol = protocol
129
126
    def rename(self):
130
127
        """Derived from the Avahi example code"""
131
128
        if self.rename_count >= self.max_renames:
137
134
        logger.info(u"Changing Zeroconf service name to %r ...",
138
135
                    str(self.name))
139
136
        syslogger.setFormatter(logging.Formatter
140
 
                               ('Mandos (%s) [%%(process)d]:'
141
 
                                ' %%(levelname)s: %%(message)s'
142
 
                                % self.name))
 
137
                               ('Mandos (%s): %%(levelname)s:'
 
138
                                ' %%(message)s' % self.name))
143
139
        self.remove()
144
140
        self.add()
145
141
        self.rename_count += 1
161
157
                     service.name, service.type)
162
158
        group.AddService(
163
159
                self.interface,         # interface
164
 
                self.protocol,          # protocol
 
160
                avahi.PROTO_INET6,      # protocol
165
161
                dbus.UInt32(0),         # flags
166
162
                self.name, self.type,
167
163
                self.domain, self.host,
182
178
class Client(dbus.service.Object):
183
179
    """A representation of a client host served by this server.
184
180
    Attributes:
185
 
    name:       string; from the config file, used in log messages and
186
 
                        D-Bus identifiers
 
181
    name:       string; from the config file, used in log messages
187
182
    fingerprint: string (40 or 32 hexadecimal digits); used to
188
183
                 uniquely identify the client
189
184
    secret:     bytestring; sent verbatim (over TLS) to client
206
201
                     client lives.  %() expansions are done at
207
202
                     runtime with vars(self) as dict, so that for
208
203
                     instance %(name)s can be used in the command.
209
 
    current_checker_command: string; current running checker_command
210
204
    use_dbus: bool(); Whether to provide D-Bus interface and signals
211
205
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
212
206
    """
231
225
        if config is None:
232
226
            config = {}
233
227
        logger.debug(u"Creating client %r", self.name)
234
 
        self.use_dbus = False   # During __init__
 
228
        self.use_dbus = use_dbus
 
229
        if self.use_dbus:
 
230
            self.dbus_object_path = (dbus.ObjectPath
 
231
                                     ("/Mandos/clients/"
 
232
                                      + self.name.replace(".", "_")))
 
233
            dbus.service.Object.__init__(self, bus,
 
234
                                         self.dbus_object_path)
235
235
        # Uppercase and remove spaces from fingerprint for later
236
236
        # comparison purposes with return value from the fingerprint()
237
237
        # function
261
261
        self.disable_initiator_tag = None
262
262
        self.checker_callback_tag = None
263
263
        self.checker_command = config["checker"]
264
 
        self.current_checker_command = None
265
 
        self.last_connect = None
266
 
        # Only now, when this client is initialized, can it show up on
267
 
        # the D-Bus
268
 
        self.use_dbus = use_dbus
269
 
        if self.use_dbus:
270
 
            self.dbus_object_path = (dbus.ObjectPath
271
 
                                     ("/clients/"
272
 
                                      + self.name.replace(".", "_")))
273
 
            dbus.service.Object.__init__(self, bus,
274
 
                                         self.dbus_object_path)
275
264
    
276
265
    def enable(self):
277
266
        """Start this client's checker and timeout hooks"""
330
319
            # Emit D-Bus signal
331
320
            self.PropertyChanged(dbus.String(u"checker_running"),
332
321
                                 dbus.Boolean(False, variant_level=1))
333
 
        if os.WIFEXITED(condition):
334
 
            exitstatus = os.WEXITSTATUS(condition)
335
 
            if exitstatus == 0:
336
 
                logger.info(u"Checker for %(name)s succeeded",
337
 
                            vars(self))
338
 
                self.checked_ok()
339
 
            else:
340
 
                logger.info(u"Checker for %(name)s failed",
341
 
                            vars(self))
 
322
        if (os.WIFEXITED(condition)
 
323
            and (os.WEXITSTATUS(condition) == 0)):
 
324
            logger.info(u"Checker for %(name)s succeeded",
 
325
                        vars(self))
342
326
            if self.use_dbus:
343
327
                # Emit D-Bus signal
344
 
                self.CheckerCompleted(dbus.Int16(exitstatus),
345
 
                                      dbus.Int64(condition),
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
346
330
                                      dbus.String(command))
347
 
        else:
 
331
            self.bump_timeout()
 
332
        elif not os.WIFEXITED(condition):
348
333
            logger.warning(u"Checker for %(name)s crashed?",
349
334
                           vars(self))
350
335
            if self.use_dbus:
351
336
                # Emit D-Bus signal
352
 
                self.CheckerCompleted(dbus.Int16(-1),
353
 
                                      dbus.Int64(condition),
 
337
                self.CheckerCompleted(dbus.Boolean(False),
 
338
                                      dbus.UInt16(condition),
 
339
                                      dbus.String(command))
 
340
        else:
 
341
            logger.info(u"Checker for %(name)s failed",
 
342
                        vars(self))
 
343
            if self.use_dbus:
 
344
                # Emit D-Bus signal
 
345
                self.CheckerCompleted(dbus.Boolean(False),
 
346
                                      dbus.UInt16(condition),
354
347
                                      dbus.String(command))
355
348
    
356
 
    def checked_ok(self):
 
349
    def bump_timeout(self):
357
350
        """Bump up the timeout for this client.
358
351
        This should only be called when the client has been seen,
359
352
        alive and well.
382
375
        # checkers alone, the checker would have to take more time
383
376
        # than 'timeout' for the client to be declared invalid, which
384
377
        # is as it should be.
385
 
        
386
 
        # If a checker exists, make sure it is not a zombie
387
 
        if self.checker is not None:
388
 
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
389
 
            if pid:
390
 
                logger.warning("Checker was a zombie")
391
 
                gobject.source_remove(self.checker_callback_tag)
392
 
                self.checker_callback(pid, status,
393
 
                                      self.current_checker_command)
394
 
        # Start a new checker if needed
395
378
        if self.checker is None:
396
379
            try:
397
380
                # In case checker_command has exactly one % operator
407
390
                    logger.error(u'Could not format string "%s":'
408
391
                                 u' %s', self.checker_command, error)
409
392
                    return True # Try again later
410
 
                self.current_checker_command = command
411
393
            try:
412
394
                logger.info(u"Starting checker %r for %s",
413
395
                            command, self.name)
428
410
                                             (self.checker.pid,
429
411
                                              self.checker_callback,
430
412
                                              data=command))
431
 
                # The checker may have completed before the gobject
432
 
                # watch was added.  Check for this.
433
 
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
434
 
                if pid:
435
 
                    gobject.source_remove(self.checker_callback_tag)
436
 
                    self.checker_callback(pid, status, command)
437
413
            except OSError, error:
438
414
                logger.error(u"Failed to start subprocess: %s",
439
415
                             error)
472
448
            return now < (self.last_checked_ok + self.timeout)
473
449
    
474
450
    ## D-Bus methods & signals
475
 
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
451
    _interface = u"org.mandos_system.Mandos.Client"
476
452
    
477
 
    # CheckedOK - method
478
 
    CheckedOK = dbus.service.method(_interface)(checked_ok)
479
 
    CheckedOK.__name__ = "CheckedOK"
 
453
    # BumpTimeout - method
 
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
    BumpTimeout.__name__ = "BumpTimeout"
480
456
    
481
457
    # CheckerCompleted - signal
482
 
    @dbus.service.signal(_interface, signature="nxs")
483
 
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
484
460
        "D-Bus signal"
485
461
        pass
486
462
    
527
503
                dbus.String("checker_running"):
528
504
                    dbus.Boolean(self.checker is not None,
529
505
                                 variant_level=1),
530
 
                dbus.String("object_path"):
531
 
                    dbus.ObjectPath(self.dbus_object_path,
532
 
                                    variant_level=1)
533
506
                }, signature="sv")
534
507
    
535
508
    # IsStillValid - method
543
516
        "D-Bus signal"
544
517
        pass
545
518
    
546
 
    # ReceivedSecret - signal
547
 
    @dbus.service.signal(_interface)
548
 
    def ReceivedSecret(self):
549
 
        "D-Bus signal"
550
 
        pass
551
 
    
552
 
    # Rejected - signal
553
 
    @dbus.service.signal(_interface)
554
 
    def Rejected(self):
555
 
        "D-Bus signal"
556
 
        pass
557
 
    
558
519
    # SetChecker - method
559
520
    @dbus.service.method(_interface, in_signature="s")
560
521
    def SetChecker(self, checker):
630
591
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
631
592
        # ...do the normal thing
632
593
        return session.peer_certificate
633
 
    list_size = ctypes.c_uint(1)
 
594
    list_size = ctypes.c_uint()
634
595
    cert_list = (gnutls.library.functions
635
596
                 .gnutls_certificate_get_peers
636
597
                 (session._c_object, ctypes.byref(list_size)))
637
 
    if not bool(cert_list) and list_size.value != 0:
638
 
        raise gnutls.errors.GNUTLSError("error getting peer"
639
 
                                        " certificate")
640
598
    if list_size.value == 0:
641
599
        return None
642
600
    cert = cert_list[0]
691
649
    def handle(self):
692
650
        logger.info(u"TCP connection from: %s",
693
651
                    unicode(self.client_address))
694
 
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
695
 
        # Open IPC pipe to parent process
696
 
        with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
697
 
            session = (gnutls.connection
698
 
                       .ClientSession(self.request,
699
 
                                      gnutls.connection
700
 
                                      .X509Credentials()))
701
 
            
702
 
            line = self.request.makefile().readline()
703
 
            logger.debug(u"Protocol version: %r", line)
704
 
            try:
705
 
                if int(line.strip().split()[0]) > 1:
706
 
                    raise RuntimeError
707
 
            except (ValueError, IndexError, RuntimeError), error:
708
 
                logger.error(u"Unknown protocol version: %s", error)
709
 
                return
710
 
            
711
 
            # Note: gnutls.connection.X509Credentials is really a
712
 
            # generic GnuTLS certificate credentials object so long as
713
 
            # no X.509 keys are added to it.  Therefore, we can use it
714
 
            # here despite using OpenPGP certificates.
715
 
            
716
 
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
717
 
            #                     "+AES-256-CBC", "+SHA1",
718
 
            #                     "+COMP-NULL", "+CTYPE-OPENPGP",
719
 
            #                     "+DHE-DSS"))
720
 
            # Use a fallback default, since this MUST be set.
721
 
            priority = self.server.settings.get("priority", "NORMAL")
722
 
            (gnutls.library.functions
723
 
             .gnutls_priority_set_direct(session._c_object,
724
 
                                         priority, None))
725
 
            
726
 
            try:
727
 
                session.handshake()
728
 
            except gnutls.errors.GNUTLSError, error:
729
 
                logger.warning(u"Handshake failed: %s", error)
730
 
                # Do not run session.bye() here: the session is not
731
 
                # established.  Just abandon the request.
732
 
                return
733
 
            logger.debug(u"Handshake succeeded")
734
 
            try:
735
 
                fpr = fingerprint(peer_certificate(session))
736
 
            except (TypeError, gnutls.errors.GNUTLSError), error:
737
 
                logger.warning(u"Bad certificate: %s", error)
738
 
                session.bye()
739
 
                return
740
 
            logger.debug(u"Fingerprint: %s", fpr)
741
 
            
742
 
            for c in self.server.clients:
743
 
                if c.fingerprint == fpr:
744
 
                    client = c
745
 
                    break
746
 
            else:
747
 
                logger.warning(u"Client not found for fingerprint: %s",
748
 
                               fpr)
749
 
                ipc.write("NOTFOUND %s\n" % fpr)
750
 
                session.bye()
751
 
                return
752
 
            # Have to check if client.still_valid(), since it is
753
 
            # possible that the client timed out while establishing
754
 
            # the GnuTLS session.
755
 
            if not client.still_valid():
756
 
                logger.warning(u"Client %(name)s is invalid",
757
 
                               vars(client))
758
 
                ipc.write("INVALID %s\n" % client.name)
759
 
                session.bye()
760
 
                return
761
 
            ipc.write("SENDING %s\n" % client.name)
762
 
            sent_size = 0
763
 
            while sent_size < len(client.secret):
764
 
                sent = session.send(client.secret[sent_size:])
765
 
                logger.debug(u"Sent: %d, remaining: %d",
766
 
                             sent, len(client.secret)
767
 
                             - (sent_size + sent))
768
 
                sent_size += sent
769
 
            session.bye()
770
 
 
771
 
 
772
 
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
773
 
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
774
 
    Assumes a gobject.MainLoop event loop.
775
 
    """
776
 
    def process_request(self, request, client_address):
777
 
        """This overrides and wraps the original process_request().
778
 
        This function creates a new pipe in self.pipe 
779
 
        """
780
 
        self.pipe = os.pipe()
781
 
        super(ForkingMixInWithPipe,
782
 
              self).process_request(request, client_address)
783
 
        os.close(self.pipe[1])  # close write end
784
 
        # Call "handle_ipc" for both data and EOF events
785
 
        gobject.io_add_watch(self.pipe[0],
786
 
                             gobject.IO_IN | gobject.IO_HUP,
787
 
                             self.handle_ipc)
788
 
    def handle_ipc(source, condition):
789
 
        """Dummy function; override as necessary"""
790
 
        os.close(source)
791
 
        return False
792
 
 
793
 
 
794
 
class IPv6_TCPServer(ForkingMixInWithPipe,
 
652
        session = (gnutls.connection
 
653
                   .ClientSession(self.request,
 
654
                                  gnutls.connection
 
655
                                  .X509Credentials()))
 
656
        
 
657
        line = self.request.makefile().readline()
 
658
        logger.debug(u"Protocol version: %r", line)
 
659
        try:
 
660
            if int(line.strip().split()[0]) > 1:
 
661
                raise RuntimeError
 
662
        except (ValueError, IndexError, RuntimeError), error:
 
663
            logger.error(u"Unknown protocol version: %s", error)
 
664
            return
 
665
        
 
666
        # Note: gnutls.connection.X509Credentials is really a generic
 
667
        # GnuTLS certificate credentials object so long as no X.509
 
668
        # keys are added to it.  Therefore, we can use it here despite
 
669
        # using OpenPGP certificates.
 
670
        
 
671
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
 
672
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
673
        #                "+DHE-DSS"))
 
674
        # Use a fallback default, since this MUST be set.
 
675
        priority = self.server.settings.get("priority", "NORMAL")
 
676
        (gnutls.library.functions
 
677
         .gnutls_priority_set_direct(session._c_object,
 
678
                                     priority, None))
 
679
        
 
680
        try:
 
681
            session.handshake()
 
682
        except gnutls.errors.GNUTLSError, error:
 
683
            logger.warning(u"Handshake failed: %s", error)
 
684
            # Do not run session.bye() here: the session is not
 
685
            # established.  Just abandon the request.
 
686
            return
 
687
        try:
 
688
            fpr = fingerprint(peer_certificate(session))
 
689
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
690
            logger.warning(u"Bad certificate: %s", error)
 
691
            session.bye()
 
692
            return
 
693
        logger.debug(u"Fingerprint: %s", fpr)
 
694
        for c in self.server.clients:
 
695
            if c.fingerprint == fpr:
 
696
                client = c
 
697
                break
 
698
        else:
 
699
            logger.warning(u"Client not found for fingerprint: %s",
 
700
                           fpr)
 
701
            session.bye()
 
702
            return
 
703
        # Have to check if client.still_valid(), since it is possible
 
704
        # that the client timed out while establishing the GnuTLS
 
705
        # session.
 
706
        if not client.still_valid():
 
707
            logger.warning(u"Client %(name)s is invalid",
 
708
                           vars(client))
 
709
            session.bye()
 
710
            return
 
711
        ## This won't work here, since we're in a fork.
 
712
        # client.bump_timeout()
 
713
        sent_size = 0
 
714
        while sent_size < len(client.secret):
 
715
            sent = session.send(client.secret[sent_size:])
 
716
            logger.debug(u"Sent: %d, remaining: %d",
 
717
                         sent, len(client.secret)
 
718
                         - (sent_size + sent))
 
719
            sent_size += sent
 
720
        session.bye()
 
721
 
 
722
 
 
723
class IPv6_TCPServer(SocketServer.ForkingMixIn,
795
724
                     SocketServer.TCPServer, object):
796
 
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
 
725
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
797
726
    Attributes:
798
727
        settings:       Server settings
799
728
        clients:        Set() of Client objects
807
736
        if "clients" in kwargs:
808
737
            self.clients = kwargs["clients"]
809
738
            del kwargs["clients"]
810
 
        if "use_ipv6" in kwargs:
811
 
            if not kwargs["use_ipv6"]:
812
 
                self.address_family = socket.AF_INET
813
 
            del kwargs["use_ipv6"]
814
739
        self.enabled = False
815
740
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
816
741
    def server_bind(self):
830
755
                                 u" bind to interface %s",
831
756
                                 self.settings["interface"])
832
757
                else:
833
 
                    raise
 
758
                    raise error
834
759
        # Only bind(2) the socket if we really need to.
835
760
        if self.server_address[0] or self.server_address[1]:
836
761
            if not self.server_address[0]:
837
 
                if self.address_family == socket.AF_INET6:
838
 
                    any_address = "::" # in6addr_any
839
 
                else:
840
 
                    any_address = socket.INADDR_ANY
841
 
                self.server_address = (any_address,
 
762
                in6addr_any = "::"
 
763
                self.server_address = (in6addr_any,
842
764
                                       self.server_address[1])
843
765
            elif not self.server_address[1]:
844
766
                self.server_address = (self.server_address[0],
856
778
            return super(IPv6_TCPServer, self).server_activate()
857
779
    def enable(self):
858
780
        self.enabled = True
859
 
    def handle_ipc(self, source, condition, file_objects={}):
860
 
        condition_names = {
861
 
            gobject.IO_IN: "IN", # There is data to read.
862
 
            gobject.IO_OUT: "OUT", # Data can be written (without
863
 
                                   # blocking).
864
 
            gobject.IO_PRI: "PRI", # There is urgent data to read.
865
 
            gobject.IO_ERR: "ERR", # Error condition.
866
 
            gobject.IO_HUP: "HUP"  # Hung up (the connection has been
867
 
                                   # broken, usually for pipes and
868
 
                                   # sockets).
869
 
            }
870
 
        conditions_string = ' | '.join(name
871
 
                                       for cond, name in
872
 
                                       condition_names.iteritems()
873
 
                                       if cond & condition)
874
 
        logger.debug("Handling IPC: FD = %d, condition = %s", source,
875
 
                     conditions_string)
876
 
        
877
 
        # Turn the pipe file descriptor into a Python file object
878
 
        if source not in file_objects:
879
 
            file_objects[source] = os.fdopen(source, "r", 1)
880
 
        
881
 
        # Read a line from the file object
882
 
        cmdline = file_objects[source].readline()
883
 
        if not cmdline:             # Empty line means end of file
884
 
            # close the IPC pipe
885
 
            file_objects[source].close()
886
 
            del file_objects[source]
887
 
            
888
 
            # Stop calling this function
889
 
            return False
890
 
        
891
 
        logger.debug("IPC command: %r\n" % cmdline)
892
 
        
893
 
        # Parse and act on command
894
 
        cmd, args = cmdline.split(None, 1)
895
 
        if cmd == "NOTFOUND":
896
 
            if self.settings["use_dbus"]:
897
 
                # Emit D-Bus signal
898
 
                mandos_dbus_service.ClientNotFound(args)
899
 
        elif cmd == "INVALID":
900
 
            if self.settings["use_dbus"]:
901
 
                for client in self.clients:
902
 
                    if client.name == args:
903
 
                        # Emit D-Bus signal
904
 
                        client.Rejected()
905
 
                        break
906
 
        elif cmd == "SENDING":
907
 
            for client in self.clients:
908
 
                if client.name == args:
909
 
                    client.checked_ok()
910
 
                    if self.settings["use_dbus"]:
911
 
                        # Emit D-Bus signal
912
 
                        client.ReceivedSecret()
913
 
                    break
914
 
        else:
915
 
            logger.error("Unknown IPC command: %r", cmdline)
916
 
        
917
 
        # Keep calling this function
918
 
        return True
919
781
 
920
782
 
921
783
def string_to_delta(interval):
922
784
    """Parse a string and return a datetime.timedelta
923
 
    
 
785
 
924
786
    >>> string_to_delta('7d')
925
787
    datetime.timedelta(7)
926
788
    >>> string_to_delta('60s')
1027
889
 
1028
890
 
1029
891
def main():
1030
 
    
1031
 
    ######################################################################
1032
 
    # Parsing of options, both command line and config file
1033
 
    
1034
892
    parser = optparse.OptionParser(version = "%%prog %s" % version)
1035
893
    parser.add_option("-i", "--interface", type="string",
1036
894
                      metavar="IF", help="Bind to interface IF")
1055
913
                      dest="use_dbus",
1056
914
                      help="Do not provide D-Bus system bus"
1057
915
                      " interface")
1058
 
    parser.add_option("--no-ipv6", action="store_false",
1059
 
                      dest="use_ipv6", help="Do not use IPv6")
1060
916
    options = parser.parse_args()[0]
1061
917
    
1062
918
    if options.check:
1073
929
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1074
930
                        "servicename": "Mandos",
1075
931
                        "use_dbus": "True",
1076
 
                        "use_ipv6": "True",
1077
932
                        }
1078
933
    
1079
934
    # Parse config file for server-global settings
1082
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
1083
938
    # Convert the SafeConfigParser object to a dict
1084
939
    server_settings = server_config.defaults()
1085
 
    # Use the appropriate methods on the non-string config options
1086
 
    server_settings["debug"] = server_config.getboolean("DEFAULT",
1087
 
                                                        "debug")
1088
 
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1089
 
                                                           "use_dbus")
1090
 
    server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1091
 
                                                           "use_ipv6")
1092
 
    if server_settings["port"]:
1093
 
        server_settings["port"] = server_config.getint("DEFAULT",
1094
 
                                                       "port")
 
940
    # Use getboolean on the boolean config options
 
941
    server_settings["debug"] = (server_config.getboolean
 
942
                                ("DEFAULT", "debug"))
 
943
    server_settings["use_dbus"] = (server_config.getboolean
 
944
                                   ("DEFAULT", "use_dbus"))
1095
945
    del server_config
1096
946
    
1097
947
    # Override the settings from the config file with command line
1098
948
    # options, if set.
1099
949
    for option in ("interface", "address", "port", "debug",
1100
950
                   "priority", "servicename", "configdir",
1101
 
                   "use_dbus", "use_ipv6"):
 
951
                   "use_dbus"):
1102
952
        value = getattr(options, option)
1103
953
        if value is not None:
1104
954
            server_settings[option] = value
1105
955
    del options
1106
956
    # Now we have our good server settings in "server_settings"
1107
957
    
1108
 
    ##################################################################
1109
 
    
1110
958
    # For convenience
1111
959
    debug = server_settings["debug"]
1112
960
    use_dbus = server_settings["use_dbus"]
1113
 
    use_ipv6 = server_settings["use_ipv6"]
1114
961
    
1115
962
    if not debug:
1116
963
        syslogger.setLevel(logging.WARNING)
1118
965
    
1119
966
    if server_settings["servicename"] != "Mandos":
1120
967
        syslogger.setFormatter(logging.Formatter
1121
 
                               ('Mandos (%s) [%%(process)d]:'
1122
 
                                ' %%(levelname)s: %%(message)s'
 
968
                               ('Mandos (%s): %%(levelname)s:'
 
969
                                ' %%(message)s'
1123
970
                                % server_settings["servicename"]))
1124
971
    
1125
972
    # Parse config file with clients
1131
978
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1132
979
    client_config.read(os.path.join(server_settings["configdir"],
1133
980
                                    "clients.conf"))
1134
 
 
1135
 
    global mandos_dbus_service
1136
 
    mandos_dbus_service = None
1137
981
    
1138
982
    clients = Set()
1139
983
    tcp_server = IPv6_TCPServer((server_settings["address"],
1140
984
                                 server_settings["port"]),
1141
985
                                TCP_handler,
1142
986
                                settings=server_settings,
1143
 
                                clients=clients, use_ipv6=use_ipv6)
 
987
                                clients=clients)
1144
988
    pidfilename = "/var/run/mandos.pid"
1145
989
    try:
1146
990
        pidfile = open(pidfilename, "w")
1147
 
    except IOError:
 
991
    except IOError, error:
1148
992
        logger.error("Could not open file %r", pidfilename)
1149
993
    
1150
994
    try:
1162
1006
                uid = 65534
1163
1007
                gid = 65534
1164
1008
    try:
 
1009
        os.setuid(uid)
1165
1010
        os.setgid(gid)
1166
 
        os.setuid(uid)
1167
1011
    except OSError, error:
1168
1012
        if error[0] != errno.EPERM:
1169
1013
            raise error
1170
1014
    
1171
 
    # Enable all possible GnuTLS debugging
1172
 
    if debug:
1173
 
        # "Use a log level over 10 to enable all debugging options."
1174
 
        # - GnuTLS manual
1175
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
1176
 
        
1177
 
        @gnutls.library.types.gnutls_log_func
1178
 
        def debug_gnutls(level, string):
1179
 
            logger.debug("GnuTLS: %s", string[:-1])
1180
 
        
1181
 
        (gnutls.library.functions
1182
 
         .gnutls_global_set_log_function(debug_gnutls))
1183
 
    
1184
1015
    global service
1185
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1186
1016
    service = AvahiService(name = server_settings["servicename"],
1187
 
                           servicetype = "_mandos._tcp",
1188
 
                           protocol = protocol)
 
1017
                           servicetype = "_mandos._tcp", )
1189
1018
    if server_settings["interface"]:
1190
1019
        service.interface = (if_nametoindex
1191
1020
                             (server_settings["interface"]))
1202
1031
                            avahi.DBUS_INTERFACE_SERVER)
1203
1032
    # End of Avahi example code
1204
1033
    if use_dbus:
1205
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1034
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1035
                                        bus)
1206
1036
    
1207
1037
    clients.update(Set(Client(name = section,
1208
1038
                              config
1225
1055
        daemon()
1226
1056
    
1227
1057
    try:
1228
 
        with closing(pidfile):
1229
 
            pid = os.getpid()
1230
 
            pidfile.write(str(pid) + "\n")
 
1058
        pid = os.getpid()
 
1059
        pidfile.write(str(pid) + "\n")
 
1060
        pidfile.close()
1231
1061
        del pidfile
1232
1062
    except IOError:
1233
1063
        logger.error(u"Could not write to file %r with PID %d",
1259
1089
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1260
1090
    
1261
1091
    if use_dbus:
1262
 
        class MandosDBusService(dbus.service.Object):
 
1092
        class MandosServer(dbus.service.Object):
1263
1093
            """A D-Bus proxy object"""
1264
1094
            def __init__(self):
1265
 
                dbus.service.Object.__init__(self, bus, "/")
1266
 
            _interface = u"se.bsnet.fukt.Mandos"
1267
 
            
 
1095
                dbus.service.Object.__init__(self, bus,
 
1096
                                             "/Mandos")
 
1097
            _interface = u"org.mandos_system.Mandos"
 
1098
 
1268
1099
            @dbus.service.signal(_interface, signature="oa{sv}")
1269
1100
            def ClientAdded(self, objpath, properties):
1270
1101
                "D-Bus signal"
1271
1102
                pass
1272
 
            
1273
 
            @dbus.service.signal(_interface, signature="s")
1274
 
            def ClientNotFound(self, fingerprint):
1275
 
                "D-Bus signal"
1276
 
                pass
1277
 
            
1278
 
            @dbus.service.signal(_interface, signature="os")
1279
 
            def ClientRemoved(self, objpath, name):
1280
 
                "D-Bus signal"
1281
 
                pass
1282
 
            
 
1103
 
 
1104
            @dbus.service.signal(_interface, signature="o")
 
1105
            def ClientRemoved(self, objpath):
 
1106
                "D-Bus signal"
 
1107
                pass
 
1108
 
1283
1109
            @dbus.service.method(_interface, out_signature="ao")
1284
1110
            def GetAllClients(self):
1285
 
                "D-Bus method"
1286
1111
                return dbus.Array(c.dbus_object_path for c in clients)
1287
 
            
 
1112
 
1288
1113
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1289
1114
            def GetAllClientsWithProperties(self):
1290
 
                "D-Bus method"
1291
1115
                return dbus.Dictionary(
1292
1116
                    ((c.dbus_object_path, c.GetAllProperties())
1293
1117
                     for c in clients),
1294
1118
                    signature="oa{sv}")
1295
 
            
 
1119
 
1296
1120
            @dbus.service.method(_interface, in_signature="o")
1297
1121
            def RemoveClient(self, object_path):
1298
 
                "D-Bus method"
1299
1122
                for c in clients:
1300
1123
                    if c.dbus_object_path == object_path:
1301
1124
                        clients.remove(c)
1303
1126
                        c.use_dbus = False
1304
1127
                        c.disable()
1305
1128
                        # Emit D-Bus signal
1306
 
                        self.ClientRemoved(object_path, c.name)
 
1129
                        self.ClientRemoved(object_path)
1307
1130
                        return
1308
1131
                raise KeyError
1309
 
            
 
1132
            @dbus.service.method(_interface)
 
1133
            def Quit(self):
 
1134
                main_loop.quit()
 
1135
 
1310
1136
            del _interface
1311
 
        
1312
 
        mandos_dbus_service = MandosDBusService()
 
1137
    
 
1138
        mandos_server = MandosServer()
1313
1139
    
1314
1140
    for client in clients:
1315
1141
        if use_dbus:
1316
1142
            # Emit D-Bus signal
1317
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1318
 
                                            client.GetAllProperties())
 
1143
            mandos_server.ClientAdded(client.dbus_object_path,
 
1144
                                      client.GetAllProperties())
1319
1145
        client.enable()
1320
1146
    
1321
1147
    tcp_server.enable()
1323
1149
    
1324
1150
    # Find out what port we got
1325
1151
    service.port = tcp_server.socket.getsockname()[1]
1326
 
    if use_ipv6:
1327
 
        logger.info(u"Now listening on address %r, port %d,"
1328
 
                    " flowinfo %d, scope_id %d"
1329
 
                    % tcp_server.socket.getsockname())
1330
 
    else:                       # IPv4
1331
 
        logger.info(u"Now listening on address %r, port %d"
1332
 
                    % tcp_server.socket.getsockname())
 
1152
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
 
1153
                u" scope_id %d" % tcp_server.socket.getsockname())
1333
1154
    
1334
1155
    #service.interface = tcp_server.socket.getsockname()[3]
1335
1156
    
1355
1176
        sys.exit(1)
1356
1177
    except KeyboardInterrupt:
1357
1178
        if debug:
1358
 
            print >> sys.stderr
1359
 
        logger.debug("Server received KeyboardInterrupt")
1360
 
    logger.debug("Server exiting")
 
1179
            print
1361
1180
 
1362
1181
if __name__ == '__main__':
1363
1182
    main()