/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
137
137
        logger.info(u"Changing Zeroconf service name to %r ...",
138
138
                    str(self.name))
139
139
        syslogger.setFormatter(logging.Formatter
140
 
                               ('Mandos (%s): %%(levelname)s:'
141
 
                                ' %%(message)s' % self.name))
 
140
                               ('Mandos (%s) [%%(process)d]:'
 
141
                                ' %%(levelname)s: %%(message)s'
 
142
                                % self.name))
142
143
        self.remove()
143
144
        self.add()
144
145
        self.rename_count += 1
178
179
    return dbus.String(dt.isoformat(), variant_level=variant_level)
179
180
 
180
181
 
181
 
class Client(dbus.service.Object):
 
182
class Client(object):
182
183
    """A representation of a client host served by this server.
183
184
    Attributes:
184
185
    name:       string; from the config file, used in log messages and
206
207
                     runtime with vars(self) as dict, so that for
207
208
                     instance %(name)s can be used in the command.
208
209
    current_checker_command: string; current running checker_command
209
 
    use_dbus: bool(); Whether to provide D-Bus interface and signals
210
 
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
211
210
    """
212
211
    def timeout_milliseconds(self):
213
212
        "Return the 'timeout' attribute in milliseconds"
221
220
                + (self.interval.seconds * 1000)
222
221
                + (self.interval.microseconds // 1000))
223
222
    
224
 
    def __init__(self, name = None, disable_hook=None, config=None,
225
 
                 use_dbus=True):
 
223
    def __init__(self, name = None, disable_hook=None, config=None):
226
224
        """Note: the 'checker' key in 'config' sets the
227
225
        'checker_command' attribute and *not* the 'checker'
228
226
        attribute."""
230
228
        if config is None:
231
229
            config = {}
232
230
        logger.debug(u"Creating client %r", self.name)
233
 
        self.use_dbus = False   # During __init__
234
231
        # Uppercase and remove spaces from fingerprint for later
235
232
        # comparison purposes with return value from the fingerprint()
236
233
        # function
262
259
        self.checker_command = config["checker"]
263
260
        self.current_checker_command = None
264
261
        self.last_connect = None
265
 
        # Only now, when this client is initialized, can it show up on
266
 
        # the D-Bus
267
 
        self.use_dbus = use_dbus
268
 
        if self.use_dbus:
269
 
            self.dbus_object_path = (dbus.ObjectPath
270
 
                                     ("/clients/"
271
 
                                      + self.name.replace(".", "_")))
272
 
            dbus.service.Object.__init__(self, bus,
273
 
                                         self.dbus_object_path)
274
262
    
275
263
    def enable(self):
276
264
        """Start this client's checker and timeout hooks"""
287
275
                                   (self.timeout_milliseconds(),
288
276
                                    self.disable))
289
277
        self.enabled = True
290
 
        if self.use_dbus:
291
 
            # Emit D-Bus signals
292
 
            self.PropertyChanged(dbus.String(u"enabled"),
293
 
                                 dbus.Boolean(True, variant_level=1))
294
 
            self.PropertyChanged(dbus.String(u"last_enabled"),
295
 
                                 (_datetime_to_dbus(self.last_enabled,
296
 
                                                    variant_level=1)))
297
278
    
298
279
    def disable(self):
299
280
        """Disable this client."""
310
291
        if self.disable_hook:
311
292
            self.disable_hook(self)
312
293
        self.enabled = False
313
 
        if self.use_dbus:
314
 
            # Emit D-Bus signal
315
 
            self.PropertyChanged(dbus.String(u"enabled"),
316
 
                                 dbus.Boolean(False, variant_level=1))
317
294
        # Do not run this again if called by a gobject.timeout_add
318
295
        return False
319
296
    
325
302
        """The checker has completed, so take appropriate actions."""
326
303
        self.checker_callback_tag = None
327
304
        self.checker = None
328
 
        if self.use_dbus:
329
 
            # Emit D-Bus signal
330
 
            self.PropertyChanged(dbus.String(u"checker_running"),
331
 
                                 dbus.Boolean(False, variant_level=1))
332
305
        if os.WIFEXITED(condition):
333
306
            exitstatus = os.WEXITSTATUS(condition)
334
307
            if exitstatus == 0:
338
311
            else:
339
312
                logger.info(u"Checker for %(name)s failed",
340
313
                            vars(self))
341
 
            if self.use_dbus:
342
 
                # Emit D-Bus signal
343
 
                self.CheckerCompleted(dbus.Int16(exitstatus),
344
 
                                      dbus.Int64(condition),
345
 
                                      dbus.String(command))
346
314
        else:
347
315
            logger.warning(u"Checker for %(name)s crashed?",
348
316
                           vars(self))
349
 
            if self.use_dbus:
350
 
                # Emit D-Bus signal
351
 
                self.CheckerCompleted(dbus.Int16(-1),
352
 
                                      dbus.Int64(condition),
353
 
                                      dbus.String(command))
354
317
    
355
318
    def checked_ok(self):
356
319
        """Bump up the timeout for this client.
362
325
        self.disable_initiator_tag = (gobject.timeout_add
363
326
                                      (self.timeout_milliseconds(),
364
327
                                       self.disable))
365
 
        if self.use_dbus:
366
 
            # Emit D-Bus signal
367
 
            self.PropertyChanged(
368
 
                dbus.String(u"last_checked_ok"),
369
 
                (_datetime_to_dbus(self.last_checked_ok,
370
 
                                   variant_level=1)))
371
328
    
372
329
    def start_checker(self):
373
330
        """Start a new checker subprocess if one is not running.
406
363
                    logger.error(u'Could not format string "%s":'
407
364
                                 u' %s', self.checker_command, error)
408
365
                    return True # Try again later
409
 
                self.current_checker_command = command
 
366
            self.current_checker_command = command
410
367
            try:
411
368
                logger.info(u"Starting checker %r for %s",
412
369
                            command, self.name)
417
374
                self.checker = subprocess.Popen(command,
418
375
                                                close_fds=True,
419
376
                                                shell=True, cwd="/")
420
 
                if self.use_dbus:
421
 
                    # Emit D-Bus signal
422
 
                    self.CheckerStarted(command)
423
 
                    self.PropertyChanged(
424
 
                        dbus.String("checker_running"),
425
 
                        dbus.Boolean(True, variant_level=1))
426
377
                self.checker_callback_tag = (gobject.child_watch_add
427
378
                                             (self.checker.pid,
428
379
                                              self.checker_callback,
456
407
            if error.errno != errno.ESRCH: # No such process
457
408
                raise
458
409
        self.checker = None
459
 
        if self.use_dbus:
460
 
            self.PropertyChanged(dbus.String(u"checker_running"),
461
 
                                 dbus.Boolean(False, variant_level=1))
462
410
    
463
411
    def still_valid(self):
464
412
        """Has the timeout not yet passed for this client?"""
469
417
            return now < (self.created + self.timeout)
470
418
        else:
471
419
            return now < (self.last_checked_ok + self.timeout)
 
420
 
 
421
 
 
422
class ClientDBus(Client, dbus.service.Object):
 
423
    """A Client class using D-Bus
 
424
    Attributes:
 
425
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
 
426
    """
 
427
    # dbus.service.Object doesn't use super(), so we can't either.
 
428
    
 
429
    def __init__(self, *args, **kwargs):
 
430
        Client.__init__(self, *args, **kwargs)
 
431
        # Only now, when this client is initialized, can it show up on
 
432
        # the D-Bus
 
433
        self.dbus_object_path = (dbus.ObjectPath
 
434
                                 ("/clients/"
 
435
                                  + self.name.replace(".", "_")))
 
436
        dbus.service.Object.__init__(self, bus,
 
437
                                     self.dbus_object_path)
 
438
    def enable(self):
 
439
        oldstate = getattr(self, "enabled", False)
 
440
        r = Client.enable(self)
 
441
        if oldstate != self.enabled:
 
442
            # Emit D-Bus signals
 
443
            self.PropertyChanged(dbus.String(u"enabled"),
 
444
                                 dbus.Boolean(True, variant_level=1))
 
445
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
446
                                 (_datetime_to_dbus(self.last_enabled,
 
447
                                                    variant_level=1)))
 
448
        return r
 
449
    
 
450
    def disable(self, signal = True):
 
451
        oldstate = getattr(self, "enabled", False)
 
452
        r = Client.disable(self)
 
453
        if signal and oldstate != self.enabled:
 
454
            # Emit D-Bus signal
 
455
            self.PropertyChanged(dbus.String(u"enabled"),
 
456
                                 dbus.Boolean(False, variant_level=1))
 
457
        return r
 
458
    
 
459
    def __del__(self, *args, **kwargs):
 
460
        try:
 
461
            self.remove_from_connection()
 
462
        except org.freedesktop.DBus.Python.LookupError:
 
463
            pass
 
464
        dbus.service.Object.__del__(self, *args, **kwargs)
 
465
        Client.__del__(self, *args, **kwargs)
 
466
    
 
467
    def checker_callback(self, pid, condition, command,
 
468
                         *args, **kwargs):
 
469
        self.checker_callback_tag = None
 
470
        self.checker = None
 
471
        # Emit D-Bus signal
 
472
        self.PropertyChanged(dbus.String(u"checker_running"),
 
473
                             dbus.Boolean(False, variant_level=1))
 
474
        if os.WIFEXITED(condition):
 
475
            exitstatus = os.WEXITSTATUS(condition)
 
476
            # Emit D-Bus signal
 
477
            self.CheckerCompleted(dbus.Int16(exitstatus),
 
478
                                  dbus.Int64(condition),
 
479
                                  dbus.String(command))
 
480
        else:
 
481
            # Emit D-Bus signal
 
482
            self.CheckerCompleted(dbus.Int16(-1),
 
483
                                  dbus.Int64(condition),
 
484
                                  dbus.String(command))
 
485
        
 
486
        return Client.checker_callback(self, pid, condition, command,
 
487
                                       *args, **kwargs)
 
488
    
 
489
    def checked_ok(self, *args, **kwargs):
 
490
        r = Client.checked_ok(self, *args, **kwargs)
 
491
        # Emit D-Bus signal
 
492
        self.PropertyChanged(
 
493
            dbus.String(u"last_checked_ok"),
 
494
            (_datetime_to_dbus(self.last_checked_ok,
 
495
                               variant_level=1)))
 
496
        return r
 
497
    
 
498
    def start_checker(self, *args, **kwargs):
 
499
        old_checker = self.checker
 
500
        if self.checker is not None:
 
501
            old_checker_pid = self.checker.pid
 
502
        else:
 
503
            old_checker_pid = None
 
504
        r = Client.start_checker(self, *args, **kwargs)
 
505
        # Only emit D-Bus signal if new checker process was started
 
506
        if ((self.checker is not None)
 
507
            and not (old_checker is not None
 
508
                     and old_checker_pid == self.checker.pid)):
 
509
            self.CheckerStarted(self.current_checker_command)
 
510
            self.PropertyChanged(
 
511
                dbus.String("checker_running"),
 
512
                dbus.Boolean(True, variant_level=1))
 
513
        return r
 
514
    
 
515
    def stop_checker(self, *args, **kwargs):
 
516
        old_checker = getattr(self, "checker", None)
 
517
        r = Client.stop_checker(self, *args, **kwargs)
 
518
        if (old_checker is not None
 
519
            and getattr(self, "checker", None) is None):
 
520
            self.PropertyChanged(dbus.String(u"checker_running"),
 
521
                                 dbus.Boolean(False, variant_level=1))
 
522
        return r
472
523
    
473
524
    ## D-Bus methods & signals
474
525
    _interface = u"se.bsnet.fukt.Mandos.Client"
532
583
                }, signature="sv")
533
584
    
534
585
    # IsStillValid - method
535
 
    IsStillValid = (dbus.service.method(_interface, out_signature="b")
536
 
                    (still_valid))
537
 
    IsStillValid.__name__ = "IsStillValid"
 
586
    @dbus.service.method(_interface, out_signature="b")
 
587
    def IsStillValid(self):
 
588
        return self.still_valid()
538
589
    
539
590
    # PropertyChanged - signal
540
591
    @dbus.service.signal(_interface, signature="sv")
542
593
        "D-Bus signal"
543
594
        pass
544
595
    
 
596
    # ReceivedSecret - signal
 
597
    @dbus.service.signal(_interface)
 
598
    def ReceivedSecret(self):
 
599
        "D-Bus signal"
 
600
        pass
 
601
    
 
602
    # Rejected - signal
 
603
    @dbus.service.signal(_interface)
 
604
    def Rejected(self):
 
605
        "D-Bus signal"
 
606
        pass
 
607
    
545
608
    # SetChecker - method
546
609
    @dbus.service.method(_interface, in_signature="s")
547
610
    def SetChecker(self, checker):
678
741
    def handle(self):
679
742
        logger.info(u"TCP connection from: %s",
680
743
                    unicode(self.client_address))
681
 
        session = (gnutls.connection
682
 
                   .ClientSession(self.request,
683
 
                                  gnutls.connection
684
 
                                  .X509Credentials()))
685
 
        
686
 
        line = self.request.makefile().readline()
687
 
        logger.debug(u"Protocol version: %r", line)
688
 
        try:
689
 
            if int(line.strip().split()[0]) > 1:
690
 
                raise RuntimeError
691
 
        except (ValueError, IndexError, RuntimeError), error:
692
 
            logger.error(u"Unknown protocol version: %s", error)
693
 
            return
694
 
        
695
 
        # Note: gnutls.connection.X509Credentials is really a generic
696
 
        # GnuTLS certificate credentials object so long as no X.509
697
 
        # keys are added to it.  Therefore, we can use it here despite
698
 
        # using OpenPGP certificates.
699
 
        
700
 
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
701
 
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
702
 
        #                     "+DHE-DSS"))
703
 
        # Use a fallback default, since this MUST be set.
704
 
        priority = self.server.settings.get("priority", "NORMAL")
705
 
        (gnutls.library.functions
706
 
         .gnutls_priority_set_direct(session._c_object,
707
 
                                     priority, None))
708
 
        
709
 
        try:
710
 
            session.handshake()
711
 
        except gnutls.errors.GNUTLSError, error:
712
 
            logger.warning(u"Handshake failed: %s", error)
713
 
            # Do not run session.bye() here: the session is not
714
 
            # established.  Just abandon the request.
715
 
            return
716
 
        logger.debug(u"Handshake succeeded")
717
 
        try:
718
 
            fpr = fingerprint(peer_certificate(session))
719
 
        except (TypeError, gnutls.errors.GNUTLSError), error:
720
 
            logger.warning(u"Bad certificate: %s", error)
721
 
            session.bye()
722
 
            return
723
 
        logger.debug(u"Fingerprint: %s", fpr)
724
 
        
725
 
        for c in self.server.clients:
726
 
            if c.fingerprint == fpr:
727
 
                client = c
728
 
                break
729
 
        else:
730
 
            logger.warning(u"Client not found for fingerprint: %s",
731
 
                           fpr)
732
 
            session.bye()
733
 
            return
734
 
        # Have to check if client.still_valid(), since it is possible
735
 
        # that the client timed out while establishing the GnuTLS
736
 
        # session.
737
 
        if not client.still_valid():
738
 
            logger.warning(u"Client %(name)s is invalid",
739
 
                           vars(client))
740
 
            session.bye()
741
 
            return
742
 
        ## This won't work here, since we're in a fork.
743
 
        # client.checked_ok()
744
 
        sent_size = 0
745
 
        while sent_size < len(client.secret):
746
 
            sent = session.send(client.secret[sent_size:])
747
 
            logger.debug(u"Sent: %d, remaining: %d",
748
 
                         sent, len(client.secret)
749
 
                         - (sent_size + sent))
750
 
            sent_size += sent
751
 
        session.bye()
752
 
 
753
 
 
754
 
class IPv6_TCPServer(SocketServer.ForkingMixIn,
 
744
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
 
745
        # Open IPC pipe to parent process
 
746
        with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
 
747
            session = (gnutls.connection
 
748
                       .ClientSession(self.request,
 
749
                                      gnutls.connection
 
750
                                      .X509Credentials()))
 
751
            
 
752
            line = self.request.makefile().readline()
 
753
            logger.debug(u"Protocol version: %r", line)
 
754
            try:
 
755
                if int(line.strip().split()[0]) > 1:
 
756
                    raise RuntimeError
 
757
            except (ValueError, IndexError, RuntimeError), error:
 
758
                logger.error(u"Unknown protocol version: %s", error)
 
759
                return
 
760
            
 
761
            # Note: gnutls.connection.X509Credentials is really a
 
762
            # generic GnuTLS certificate credentials object so long as
 
763
            # no X.509 keys are added to it.  Therefore, we can use it
 
764
            # here despite using OpenPGP certificates.
 
765
            
 
766
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
767
            #                     "+AES-256-CBC", "+SHA1",
 
768
            #                     "+COMP-NULL", "+CTYPE-OPENPGP",
 
769
            #                     "+DHE-DSS"))
 
770
            # Use a fallback default, since this MUST be set.
 
771
            priority = self.server.settings.get("priority", "NORMAL")
 
772
            (gnutls.library.functions
 
773
             .gnutls_priority_set_direct(session._c_object,
 
774
                                         priority, None))
 
775
            
 
776
            try:
 
777
                session.handshake()
 
778
            except gnutls.errors.GNUTLSError, error:
 
779
                logger.warning(u"Handshake failed: %s", error)
 
780
                # Do not run session.bye() here: the session is not
 
781
                # established.  Just abandon the request.
 
782
                return
 
783
            logger.debug(u"Handshake succeeded")
 
784
            try:
 
785
                fpr = fingerprint(peer_certificate(session))
 
786
            except (TypeError, gnutls.errors.GNUTLSError), error:
 
787
                logger.warning(u"Bad certificate: %s", error)
 
788
                session.bye()
 
789
                return
 
790
            logger.debug(u"Fingerprint: %s", fpr)
 
791
            
 
792
            for c in self.server.clients:
 
793
                if c.fingerprint == fpr:
 
794
                    client = c
 
795
                    break
 
796
            else:
 
797
                logger.warning(u"Client not found for fingerprint: %s",
 
798
                               fpr)
 
799
                ipc.write("NOTFOUND %s\n" % fpr)
 
800
                session.bye()
 
801
                return
 
802
            # Have to check if client.still_valid(), since it is
 
803
            # possible that the client timed out while establishing
 
804
            # the GnuTLS session.
 
805
            if not client.still_valid():
 
806
                logger.warning(u"Client %(name)s is invalid",
 
807
                               vars(client))
 
808
                ipc.write("INVALID %s\n" % client.name)
 
809
                session.bye()
 
810
                return
 
811
            ipc.write("SENDING %s\n" % client.name)
 
812
            sent_size = 0
 
813
            while sent_size < len(client.secret):
 
814
                sent = session.send(client.secret[sent_size:])
 
815
                logger.debug(u"Sent: %d, remaining: %d",
 
816
                             sent, len(client.secret)
 
817
                             - (sent_size + sent))
 
818
                sent_size += sent
 
819
            session.bye()
 
820
 
 
821
 
 
822
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
 
823
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
 
824
    Assumes a gobject.MainLoop event loop.
 
825
    """
 
826
    def process_request(self, request, client_address):
 
827
        """This overrides and wraps the original process_request().
 
828
        This function creates a new pipe in self.pipe 
 
829
        """
 
830
        self.pipe = os.pipe()
 
831
        super(ForkingMixInWithPipe,
 
832
              self).process_request(request, client_address)
 
833
        os.close(self.pipe[1])  # close write end
 
834
        # Call "handle_ipc" for both data and EOF events
 
835
        gobject.io_add_watch(self.pipe[0],
 
836
                             gobject.IO_IN | gobject.IO_HUP,
 
837
                             self.handle_ipc)
 
838
    def handle_ipc(source, condition):
 
839
        """Dummy function; override as necessary"""
 
840
        os.close(source)
 
841
        return False
 
842
 
 
843
 
 
844
class IPv6_TCPServer(ForkingMixInWithPipe,
755
845
                     SocketServer.TCPServer, object):
756
846
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
757
847
    Attributes:
816
906
            return super(IPv6_TCPServer, self).server_activate()
817
907
    def enable(self):
818
908
        self.enabled = True
 
909
    def handle_ipc(self, source, condition, file_objects={}):
 
910
        condition_names = {
 
911
            gobject.IO_IN: "IN", # There is data to read.
 
912
            gobject.IO_OUT: "OUT", # Data can be written (without
 
913
                                   # blocking).
 
914
            gobject.IO_PRI: "PRI", # There is urgent data to read.
 
915
            gobject.IO_ERR: "ERR", # Error condition.
 
916
            gobject.IO_HUP: "HUP"  # Hung up (the connection has been
 
917
                                   # broken, usually for pipes and
 
918
                                   # sockets).
 
919
            }
 
920
        conditions_string = ' | '.join(name
 
921
                                       for cond, name in
 
922
                                       condition_names.iteritems()
 
923
                                       if cond & condition)
 
924
        logger.debug("Handling IPC: FD = %d, condition = %s", source,
 
925
                     conditions_string)
 
926
        
 
927
        # Turn the pipe file descriptor into a Python file object
 
928
        if source not in file_objects:
 
929
            file_objects[source] = os.fdopen(source, "r", 1)
 
930
        
 
931
        # Read a line from the file object
 
932
        cmdline = file_objects[source].readline()
 
933
        if not cmdline:             # Empty line means end of file
 
934
            # close the IPC pipe
 
935
            file_objects[source].close()
 
936
            del file_objects[source]
 
937
            
 
938
            # Stop calling this function
 
939
            return False
 
940
        
 
941
        logger.debug("IPC command: %r\n" % cmdline)
 
942
        
 
943
        # Parse and act on command
 
944
        cmd, args = cmdline.split(None, 1)
 
945
        if cmd == "NOTFOUND":
 
946
            if self.settings["use_dbus"]:
 
947
                # Emit D-Bus signal
 
948
                mandos_dbus_service.ClientNotFound(args)
 
949
        elif cmd == "INVALID":
 
950
            if self.settings["use_dbus"]:
 
951
                for client in self.clients:
 
952
                    if client.name == args:
 
953
                        # Emit D-Bus signal
 
954
                        client.Rejected()
 
955
                        break
 
956
        elif cmd == "SENDING":
 
957
            for client in self.clients:
 
958
                if client.name == args:
 
959
                    client.checked_ok()
 
960
                    if self.settings["use_dbus"]:
 
961
                        # Emit D-Bus signal
 
962
                        client.ReceivedSecret()
 
963
                    break
 
964
        else:
 
965
            logger.error("Unknown IPC command: %r", cmdline)
 
966
        
 
967
        # Keep calling this function
 
968
        return True
819
969
 
820
970
 
821
971
def string_to_delta(interval):
927
1077
 
928
1078
 
929
1079
def main():
 
1080
    
 
1081
    ######################################################################
 
1082
    # Parsing of options, both command line and config file
 
1083
    
930
1084
    parser = optparse.OptionParser(version = "%%prog %s" % version)
931
1085
    parser.add_option("-i", "--interface", type="string",
932
1086
                      metavar="IF", help="Bind to interface IF")
949
1103
                      " files")
950
1104
    parser.add_option("--no-dbus", action="store_false",
951
1105
                      dest="use_dbus",
952
 
                      help=optparse.SUPPRESS_HELP) # XXX: Not done yet
 
1106
                      help="Do not provide D-Bus system bus"
 
1107
                      " interface")
953
1108
    parser.add_option("--no-ipv6", action="store_false",
954
1109
                      dest="use_ipv6", help="Do not use IPv6")
955
1110
    options = parser.parse_args()[0]
1000
1155
    del options
1001
1156
    # Now we have our good server settings in "server_settings"
1002
1157
    
 
1158
    ##################################################################
 
1159
    
1003
1160
    # For convenience
1004
1161
    debug = server_settings["debug"]
1005
1162
    use_dbus = server_settings["use_dbus"]
1006
 
    use_dbus = False            # XXX: Not done yet
1007
1163
    use_ipv6 = server_settings["use_ipv6"]
1008
1164
    
1009
1165
    if not debug:
1012
1168
    
1013
1169
    if server_settings["servicename"] != "Mandos":
1014
1170
        syslogger.setFormatter(logging.Formatter
1015
 
                               ('Mandos (%s): %%(levelname)s:'
1016
 
                                ' %%(message)s'
 
1171
                               ('Mandos (%s) [%%(process)d]:'
 
1172
                                ' %%(levelname)s: %%(message)s'
1017
1173
                                % server_settings["servicename"]))
1018
1174
    
1019
1175
    # Parse config file with clients
1025
1181
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1026
1182
    client_config.read(os.path.join(server_settings["configdir"],
1027
1183
                                    "clients.conf"))
 
1184
 
 
1185
    global mandos_dbus_service
 
1186
    mandos_dbus_service = None
1028
1187
    
1029
1188
    clients = Set()
1030
1189
    tcp_server = IPv6_TCPServer((server_settings["address"],
1095
1254
    if use_dbus:
1096
1255
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1097
1256
    
1098
 
    clients.update(Set(Client(name = section,
1099
 
                              config
1100
 
                              = dict(client_config.items(section)),
1101
 
                              use_dbus = use_dbus)
1102
 
                       for section in client_config.sections()))
 
1257
    client_class = Client
 
1258
    if use_dbus:
 
1259
        client_class = ClientDBus
 
1260
    clients.update(Set(
 
1261
            client_class(name = section,
 
1262
                         config= dict(client_config.items(section)))
 
1263
            for section in client_config.sections()))
1103
1264
    if not clients:
1104
1265
        logger.warning(u"No clients defined")
1105
1266
    
1116
1277
        daemon()
1117
1278
    
1118
1279
    try:
1119
 
        pid = os.getpid()
1120
 
        pidfile.write(str(pid) + "\n")
1121
 
        pidfile.close()
 
1280
        with closing(pidfile):
 
1281
            pid = os.getpid()
 
1282
            pidfile.write(str(pid) + "\n")
1122
1283
        del pidfile
1123
1284
    except IOError:
1124
1285
        logger.error(u"Could not write to file %r with PID %d",
1150
1311
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1151
1312
    
1152
1313
    if use_dbus:
1153
 
        class MandosServer(dbus.service.Object):
 
1314
        class MandosDBusService(dbus.service.Object):
1154
1315
            """A D-Bus proxy object"""
1155
1316
            def __init__(self):
1156
1317
                dbus.service.Object.__init__(self, bus, "/")
1161
1322
                "D-Bus signal"
1162
1323
                pass
1163
1324
            
 
1325
            @dbus.service.signal(_interface, signature="s")
 
1326
            def ClientNotFound(self, fingerprint):
 
1327
                "D-Bus signal"
 
1328
                pass
 
1329
            
1164
1330
            @dbus.service.signal(_interface, signature="os")
1165
1331
            def ClientRemoved(self, objpath, name):
1166
1332
                "D-Bus signal"
1185
1351
                for c in clients:
1186
1352
                    if c.dbus_object_path == object_path:
1187
1353
                        clients.remove(c)
 
1354
                        c.remove_from_connection()
1188
1355
                        # Don't signal anything except ClientRemoved
1189
 
                        c.use_dbus = False
1190
 
                        c.disable()
 
1356
                        c.disable(signal=False)
1191
1357
                        # Emit D-Bus signal
1192
1358
                        self.ClientRemoved(object_path, c.name)
1193
1359
                        return
1195
1361
            
1196
1362
            del _interface
1197
1363
        
1198
 
        mandos_server = MandosServer()
 
1364
        mandos_dbus_service = MandosDBusService()
1199
1365
    
1200
1366
    for client in clients:
1201
1367
        if use_dbus:
1202
1368
            # Emit D-Bus signal
1203
 
            mandos_server.ClientAdded(client.dbus_object_path,
1204
 
                                      client.GetAllProperties())
 
1369
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
 
1370
                                            client.GetAllProperties())
1205
1371
        client.enable()
1206
1372
    
1207
1373
    tcp_server.enable()