/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: Björn Påhlsson
  • Date: 2011-06-19 20:25:38 UTC
  • mto: This revision was merged to the branch mainline in revision 485.
  • Revision ID: belorn@fukt.bsnet.se-20110619202538-0js072v8fso12u07
prepended mandos plugin to error messages in each plugin. Added a better way in TODO.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
# along with this program.  If not, see
29
29
# <http://www.gnu.org/licenses/>.
30
30
31
 
# Contact the authors at <mandos@recompile.se>.
 
31
# Contact the authors at <mandos@fukt.bsnet.se>.
32
32
33
33
 
34
34
from __future__ import (division, absolute_import, print_function,
62
62
import functools
63
63
import cPickle as pickle
64
64
import multiprocessing
65
 
import types
66
65
 
67
66
import dbus
68
67
import dbus.service
83
82
        SO_BINDTODEVICE = None
84
83
 
85
84
 
86
 
version = "1.4.1"
 
85
version = "1.3.0"
87
86
 
88
 
logger = logging.getLogger()
 
87
#logger = logging.getLogger('mandos')
 
88
logger = logging.Logger('mandos')
89
89
syslogger = (logging.handlers.SysLogHandler
90
90
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
91
91
              address = str("/dev/log")))
95
95
logger.addHandler(syslogger)
96
96
 
97
97
console = logging.StreamHandler()
98
 
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
99
 
                                       ' [%(process)d]:'
 
98
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
100
99
                                       ' %(levelname)s:'
101
100
                                       ' %(message)s'))
102
101
logger.addHandler(console)
103
102
 
104
 
 
105
103
class AvahiError(Exception):
106
104
    def __init__(self, value, *args, **kwargs):
107
105
        self.value = value
161
159
                            " after %i retries, exiting.",
162
160
                            self.rename_count)
163
161
            raise AvahiServiceError("Too many renames")
164
 
        self.name = unicode(self.server
165
 
                            .GetAlternativeServiceName(self.name))
 
162
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
166
163
        logger.info("Changing Zeroconf service name to %r ...",
167
164
                    self.name)
 
165
        syslogger.setFormatter(logging.Formatter
 
166
                               ('Mandos (%s) [%%(process)d]:'
 
167
                                ' %%(levelname)s: %%(message)s'
 
168
                                % self.name))
168
169
        self.remove()
169
170
        try:
170
171
            self.add()
190
191
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
191
192
        self.entry_group_state_changed_match = (
192
193
            self.group.connect_to_signal(
193
 
                'StateChanged', self.entry_group_state_changed))
 
194
                'StateChanged', self .entry_group_state_changed))
194
195
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
195
196
                     self.name, self.type)
196
197
        self.group.AddService(
262
263
                                 self.server_state_changed)
263
264
        self.server_state_changed(self.server.GetState())
264
265
 
265
 
class AvahiServiceToSyslog(AvahiService):
266
 
    def rename(self):
267
 
        """Add the new name to the syslog messages"""
268
 
        ret = AvahiService.rename(self)
269
 
        syslogger.setFormatter(logging.Formatter
270
 
                               ('Mandos (%s) [%%(process)d]:'
271
 
                                ' %%(levelname)s: %%(message)s'
272
 
                                % self.name))
273
 
        return ret
274
266
 
275
 
def _timedelta_to_milliseconds(td):
276
 
    "Convert a datetime.timedelta() to milliseconds"
277
 
    return ((td.days * 24 * 60 * 60 * 1000)
278
 
            + (td.seconds * 1000)
279
 
            + (td.microseconds // 1000))
280
 
        
281
267
class Client(object):
282
268
    """A representation of a client host served by this server.
283
269
    
311
297
    secret:     bytestring; sent verbatim (over TLS) to client
312
298
    timeout:    datetime.timedelta(); How long from last_checked_ok
313
299
                                      until this client is disabled
314
 
    extended_timeout:   extra long timeout when password has been sent
315
300
    runtime_expansions: Allowed attributes for runtime expansion.
316
 
    expires:    datetime.datetime(); time (UTC) when a client will be
317
 
                disabled, or None
318
301
    """
319
302
    
320
303
    runtime_expansions = ("approval_delay", "approval_duration",
322
305
                          "host", "interval", "last_checked_ok",
323
306
                          "last_enabled", "name", "timeout")
324
307
    
 
308
    @staticmethod
 
309
    def _timedelta_to_milliseconds(td):
 
310
        "Convert a datetime.timedelta() to milliseconds"
 
311
        return ((td.days * 24 * 60 * 60 * 1000)
 
312
                + (td.seconds * 1000)
 
313
                + (td.microseconds // 1000))
 
314
    
325
315
    def timeout_milliseconds(self):
326
316
        "Return the 'timeout' attribute in milliseconds"
327
 
        return _timedelta_to_milliseconds(self.timeout)
328
 
    
329
 
    def extended_timeout_milliseconds(self):
330
 
        "Return the 'extended_timeout' attribute in milliseconds"
331
 
        return _timedelta_to_milliseconds(self.extended_timeout)
 
317
        return self._timedelta_to_milliseconds(self.timeout)
332
318
    
333
319
    def interval_milliseconds(self):
334
320
        "Return the 'interval' attribute in milliseconds"
335
 
        return _timedelta_to_milliseconds(self.interval)
336
 
    
 
321
        return self._timedelta_to_milliseconds(self.interval)
 
322
 
337
323
    def approval_delay_milliseconds(self):
338
 
        return _timedelta_to_milliseconds(self.approval_delay)
 
324
        return self._timedelta_to_milliseconds(self.approval_delay)
339
325
    
340
326
    def __init__(self, name = None, disable_hook=None, config=None):
341
327
        """Note: the 'checker' key in 'config' sets the
368
354
        self.last_enabled = None
369
355
        self.last_checked_ok = None
370
356
        self.timeout = string_to_delta(config["timeout"])
371
 
        self.extended_timeout = string_to_delta(config
372
 
                                                ["extended_timeout"])
373
357
        self.interval = string_to_delta(config["interval"])
374
358
        self.disable_hook = disable_hook
375
359
        self.checker = None
376
360
        self.checker_initiator_tag = None
377
361
        self.disable_initiator_tag = None
378
 
        self.expires = None
379
362
        self.checker_callback_tag = None
380
363
        self.checker_command = config["checker"]
381
364
        self.current_checker_command = None
388
371
            config["approval_delay"])
389
372
        self.approval_duration = string_to_delta(
390
373
            config["approval_duration"])
391
 
        self.changedstate = (multiprocessing_manager
392
 
                             .Condition(multiprocessing_manager
393
 
                                        .Lock()))
 
374
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
394
375
    
395
376
    def send_changedstate(self):
396
377
        self.changedstate.acquire()
397
378
        self.changedstate.notify_all()
398
379
        self.changedstate.release()
399
 
    
 
380
        
400
381
    def enable(self):
401
382
        """Start this client's checker and timeout hooks"""
402
383
        if getattr(self, "enabled", False):
403
384
            # Already enabled
404
385
            return
405
386
        self.send_changedstate()
 
387
        self.last_enabled = datetime.datetime.utcnow()
406
388
        # Schedule a new checker to be started an 'interval' from now,
407
389
        # and every interval from then on.
408
390
        self.checker_initiator_tag = (gobject.timeout_add
409
391
                                      (self.interval_milliseconds(),
410
392
                                       self.start_checker))
411
393
        # Schedule a disable() when 'timeout' has passed
412
 
        self.expires = datetime.datetime.utcnow() + self.timeout
413
394
        self.disable_initiator_tag = (gobject.timeout_add
414
395
                                   (self.timeout_milliseconds(),
415
396
                                    self.disable))
416
397
        self.enabled = True
417
 
        self.last_enabled = datetime.datetime.utcnow()
418
398
        # Also start a new checker *right now*.
419
399
        self.start_checker()
420
400
    
429
409
        if getattr(self, "disable_initiator_tag", False):
430
410
            gobject.source_remove(self.disable_initiator_tag)
431
411
            self.disable_initiator_tag = None
432
 
        self.expires = None
433
412
        if getattr(self, "checker_initiator_tag", False):
434
413
            gobject.source_remove(self.checker_initiator_tag)
435
414
            self.checker_initiator_tag = None
461
440
            logger.warning("Checker for %(name)s crashed?",
462
441
                           vars(self))
463
442
    
464
 
    def checked_ok(self, timeout=None):
 
443
    def checked_ok(self):
465
444
        """Bump up the timeout for this client.
466
445
        
467
446
        This should only be called when the client has been seen,
468
447
        alive and well.
469
448
        """
470
 
        if timeout is None:
471
 
            timeout = self.timeout
472
449
        self.last_checked_ok = datetime.datetime.utcnow()
473
 
        if self.disable_initiator_tag is not None:
474
 
            gobject.source_remove(self.disable_initiator_tag)
475
 
        if getattr(self, "enabled", False):
476
 
            self.disable_initiator_tag = (gobject.timeout_add
477
 
                                          (_timedelta_to_milliseconds
478
 
                                           (timeout), self.disable))
479
 
            self.expires = datetime.datetime.utcnow() + timeout
 
450
        gobject.source_remove(self.disable_initiator_tag)
 
451
        self.disable_initiator_tag = (gobject.timeout_add
 
452
                                      (self.timeout_milliseconds(),
 
453
                                       self.disable))
480
454
    
481
455
    def need_approval(self):
482
456
        self.last_approval_request = datetime.datetime.utcnow()
522
496
                                       'replace')))
523
497
                    for attr in
524
498
                    self.runtime_expansions)
525
 
                
 
499
 
526
500
                try:
527
501
                    command = self.checker_command % escaped_attrs
528
502
                except TypeError as error:
574
548
                raise
575
549
        self.checker = None
576
550
 
577
 
 
578
551
def dbus_service_property(dbus_interface, signature="v",
579
552
                          access="readwrite", byte_arrays=False):
580
553
    """Decorators for marking methods of a DBusObjectWithProperties to
626
599
 
627
600
class DBusObjectWithProperties(dbus.service.Object):
628
601
    """A D-Bus object with properties.
629
 
    
 
602
 
630
603
    Classes inheriting from this can use the dbus_service_property
631
604
    decorator to expose methods as D-Bus properties.  It exposes the
632
605
    standard Get(), Set(), and GetAll() methods on the D-Bus.
639
612
    def _get_all_dbus_properties(self):
640
613
        """Returns a generator of (name, attribute) pairs
641
614
        """
642
 
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
643
 
                for cls in self.__class__.__mro__
 
615
        return ((prop._dbus_name, prop)
644
616
                for name, prop in
645
 
                inspect.getmembers(cls, self._is_dbus_property))
 
617
                inspect.getmembers(self, self._is_dbus_property))
646
618
    
647
619
    def _get_dbus_property(self, interface_name, property_name):
648
620
        """Returns a bound method if one exists which is a D-Bus
649
621
        property with the specified name and interface.
650
622
        """
651
 
        for cls in  self.__class__.__mro__:
652
 
            for name, value in (inspect.getmembers
653
 
                                (cls, self._is_dbus_property)):
654
 
                if (value._dbus_name == property_name
655
 
                    and value._dbus_interface == interface_name):
656
 
                    return value.__get__(self)
657
 
        
 
623
        for name in (property_name,
 
624
                     property_name + "_dbus_property"):
 
625
            prop = getattr(self, name, None)
 
626
            if (prop is None
 
627
                or not self._is_dbus_property(prop)
 
628
                or prop._dbus_name != property_name
 
629
                or (interface_name and prop._dbus_interface
 
630
                    and interface_name != prop._dbus_interface)):
 
631
                continue
 
632
            return prop
658
633
        # No such property
659
634
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
660
635
                                   + interface_name + "."
694
669
    def GetAll(self, interface_name):
695
670
        """Standard D-Bus property GetAll() method, see D-Bus
696
671
        standard.
697
 
        
 
672
 
698
673
        Note: Will not include properties with access="write".
699
674
        """
700
675
        all = {}
762
737
        return xmlstring
763
738
 
764
739
 
765
 
def datetime_to_dbus (dt, variant_level=0):
766
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
767
 
    if dt is None:
768
 
        return dbus.String("", variant_level = variant_level)
769
 
    return dbus.String(dt.isoformat(),
770
 
                       variant_level=variant_level)
771
 
 
772
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
773
 
                                  .__metaclass__):
774
 
    """Applied to an empty subclass of a D-Bus object, this metaclass
775
 
    will add additional D-Bus attributes matching a certain pattern.
776
 
    """
777
 
    def __new__(mcs, name, bases, attr):
778
 
        # Go through all the base classes which could have D-Bus
779
 
        # methods, signals, or properties in them
780
 
        for base in (b for b in bases
781
 
                     if issubclass(b, dbus.service.Object)):
782
 
            # Go though all attributes of the base class
783
 
            for attrname, attribute in inspect.getmembers(base):
784
 
                # Ignore non-D-Bus attributes, and D-Bus attributes
785
 
                # with the wrong interface name
786
 
                if (not hasattr(attribute, "_dbus_interface")
787
 
                    or not attribute._dbus_interface
788
 
                    .startswith("se.recompile.Mandos")):
789
 
                    continue
790
 
                # Create an alternate D-Bus interface name based on
791
 
                # the current name
792
 
                alt_interface = (attribute._dbus_interface
793
 
                                 .replace("se.recompile.Mandos",
794
 
                                          "se.bsnet.fukt.Mandos"))
795
 
                # Is this a D-Bus signal?
796
 
                if getattr(attribute, "_dbus_is_signal", False):
797
 
                    # Extract the original non-method function by
798
 
                    # black magic
799
 
                    nonmethod_func = (dict(
800
 
                            zip(attribute.func_code.co_freevars,
801
 
                                attribute.__closure__))["func"]
802
 
                                      .cell_contents)
803
 
                    # Create a new, but exactly alike, function
804
 
                    # object, and decorate it to be a new D-Bus signal
805
 
                    # with the alternate D-Bus interface name
806
 
                    new_function = (dbus.service.signal
807
 
                                    (alt_interface,
808
 
                                     attribute._dbus_signature)
809
 
                                    (types.FunctionType(
810
 
                                nonmethod_func.func_code,
811
 
                                nonmethod_func.func_globals,
812
 
                                nonmethod_func.func_name,
813
 
                                nonmethod_func.func_defaults,
814
 
                                nonmethod_func.func_closure)))
815
 
                    # Define a creator of a function to call both the
816
 
                    # old and new functions, so both the old and new
817
 
                    # signals gets sent when the function is called
818
 
                    def fixscope(func1, func2):
819
 
                        """This function is a scope container to pass
820
 
                        func1 and func2 to the "call_both" function
821
 
                        outside of its arguments"""
822
 
                        def call_both(*args, **kwargs):
823
 
                            """This function will emit two D-Bus
824
 
                            signals by calling func1 and func2"""
825
 
                            func1(*args, **kwargs)
826
 
                            func2(*args, **kwargs)
827
 
                        return call_both
828
 
                    # Create the "call_both" function and add it to
829
 
                    # the class
830
 
                    attr[attrname] = fixscope(attribute,
831
 
                                              new_function)
832
 
                # Is this a D-Bus method?
833
 
                elif getattr(attribute, "_dbus_is_method", False):
834
 
                    # Create a new, but exactly alike, function
835
 
                    # object.  Decorate it to be a new D-Bus method
836
 
                    # with the alternate D-Bus interface name.  Add it
837
 
                    # to the class.
838
 
                    attr[attrname] = (dbus.service.method
839
 
                                      (alt_interface,
840
 
                                       attribute._dbus_in_signature,
841
 
                                       attribute._dbus_out_signature)
842
 
                                      (types.FunctionType
843
 
                                       (attribute.func_code,
844
 
                                        attribute.func_globals,
845
 
                                        attribute.func_name,
846
 
                                        attribute.func_defaults,
847
 
                                        attribute.func_closure)))
848
 
                # Is this a D-Bus property?
849
 
                elif getattr(attribute, "_dbus_is_property", False):
850
 
                    # Create a new, but exactly alike, function
851
 
                    # object, and decorate it to be a new D-Bus
852
 
                    # property with the alternate D-Bus interface
853
 
                    # name.  Add it to the class.
854
 
                    attr[attrname] = (dbus_service_property
855
 
                                      (alt_interface,
856
 
                                       attribute._dbus_signature,
857
 
                                       attribute._dbus_access,
858
 
                                       attribute
859
 
                                       ._dbus_get_args_options
860
 
                                       ["byte_arrays"])
861
 
                                      (types.FunctionType
862
 
                                       (attribute.func_code,
863
 
                                        attribute.func_globals,
864
 
                                        attribute.func_name,
865
 
                                        attribute.func_defaults,
866
 
                                        attribute.func_closure)))
867
 
        return type.__new__(mcs, name, bases, attr)
868
 
 
869
740
class ClientDBus(Client, DBusObjectWithProperties):
870
741
    """A Client class using D-Bus
871
742
    
893
764
        DBusObjectWithProperties.__init__(self, self.bus,
894
765
                                          self.dbus_object_path)
895
766
        
896
 
    def notifychangeproperty(transform_func,
897
 
                             dbus_name, type_func=lambda x: x,
898
 
                             variant_level=1):
899
 
        """ Modify a variable so that it's a property which announces
900
 
        its changes to DBus.
 
767
    def _get_approvals_pending(self):
 
768
        return self._approvals_pending
 
769
    def _set_approvals_pending(self, value):
 
770
        old_value = self._approvals_pending
 
771
        self._approvals_pending = value
 
772
        bval = bool(value)
 
773
        if (hasattr(self, "dbus_object_path")
 
774
            and bval is not bool(old_value)):
 
775
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
776
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
777
                                 dbus_bool)
901
778
 
902
 
        transform_fun: Function that takes a value and a variant_level
903
 
                       and transforms it to a D-Bus type.
904
 
        dbus_name: D-Bus name of the variable
905
 
        type_func: Function that transform the value before sending it
906
 
                   to the D-Bus.  Default: no transform
907
 
        variant_level: D-Bus variant level.  Default: 1
908
 
        """
909
 
        attrname = "_{0}".format(dbus_name)
910
 
        def setter(self, value):
911
 
            if hasattr(self, "dbus_object_path"):
912
 
                if (not hasattr(self, attrname) or
913
 
                    type_func(getattr(self, attrname, None))
914
 
                    != type_func(value)):
915
 
                    dbus_value = transform_func(type_func(value),
916
 
                                                variant_level
917
 
                                                =variant_level)
918
 
                    self.PropertyChanged(dbus.String(dbus_name),
919
 
                                         dbus_value)
920
 
            setattr(self, attrname, value)
921
 
        
922
 
        return property(lambda self: getattr(self, attrname), setter)
923
 
    
924
 
    
925
 
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
926
 
    approvals_pending = notifychangeproperty(dbus.Boolean,
927
 
                                             "ApprovalPending",
928
 
                                             type_func = bool)
929
 
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
930
 
    last_enabled = notifychangeproperty(datetime_to_dbus,
931
 
                                        "LastEnabled")
932
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
933
 
                                   type_func = lambda checker:
934
 
                                       checker is not None)
935
 
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
936
 
                                           "LastCheckedOK")
937
 
    last_approval_request = notifychangeproperty(
938
 
        datetime_to_dbus, "LastApprovalRequest")
939
 
    approved_by_default = notifychangeproperty(dbus.Boolean,
940
 
                                               "ApprovedByDefault")
941
 
    approval_delay = notifychangeproperty(dbus.UInt16,
942
 
                                          "ApprovalDelay",
943
 
                                          type_func =
944
 
                                          _timedelta_to_milliseconds)
945
 
    approval_duration = notifychangeproperty(
946
 
        dbus.UInt16, "ApprovalDuration",
947
 
        type_func = _timedelta_to_milliseconds)
948
 
    host = notifychangeproperty(dbus.String, "Host")
949
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
950
 
                                   type_func =
951
 
                                   _timedelta_to_milliseconds)
952
 
    extended_timeout = notifychangeproperty(
953
 
        dbus.UInt16, "ExtendedTimeout",
954
 
        type_func = _timedelta_to_milliseconds)
955
 
    interval = notifychangeproperty(dbus.UInt16,
956
 
                                    "Interval",
957
 
                                    type_func =
958
 
                                    _timedelta_to_milliseconds)
959
 
    checker_command = notifychangeproperty(dbus.String, "Checker")
960
 
    
961
 
    del notifychangeproperty
 
779
    approvals_pending = property(_get_approvals_pending,
 
780
                                 _set_approvals_pending)
 
781
    del _get_approvals_pending, _set_approvals_pending
 
782
    
 
783
    @staticmethod
 
784
    def _datetime_to_dbus(dt, variant_level=0):
 
785
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
786
        return dbus.String(dt.isoformat(),
 
787
                           variant_level=variant_level)
 
788
    
 
789
    def enable(self):
 
790
        oldstate = getattr(self, "enabled", False)
 
791
        r = Client.enable(self)
 
792
        if oldstate != self.enabled:
 
793
            # Emit D-Bus signals
 
794
            self.PropertyChanged(dbus.String("Enabled"),
 
795
                                 dbus.Boolean(True, variant_level=1))
 
796
            self.PropertyChanged(
 
797
                dbus.String("LastEnabled"),
 
798
                self._datetime_to_dbus(self.last_enabled,
 
799
                                       variant_level=1))
 
800
        return r
 
801
    
 
802
    def disable(self, quiet = False):
 
803
        oldstate = getattr(self, "enabled", False)
 
804
        r = Client.disable(self, quiet=quiet)
 
805
        if not quiet and oldstate != self.enabled:
 
806
            # Emit D-Bus signal
 
807
            self.PropertyChanged(dbus.String("Enabled"),
 
808
                                 dbus.Boolean(False, variant_level=1))
 
809
        return r
962
810
    
963
811
    def __del__(self, *args, **kwargs):
964
812
        try:
973
821
                         *args, **kwargs):
974
822
        self.checker_callback_tag = None
975
823
        self.checker = None
 
824
        # Emit D-Bus signal
 
825
        self.PropertyChanged(dbus.String("CheckerRunning"),
 
826
                             dbus.Boolean(False, variant_level=1))
976
827
        if os.WIFEXITED(condition):
977
828
            exitstatus = os.WEXITSTATUS(condition)
978
829
            # Emit D-Bus signal
988
839
        return Client.checker_callback(self, pid, condition, command,
989
840
                                       *args, **kwargs)
990
841
    
 
842
    def checked_ok(self, *args, **kwargs):
 
843
        Client.checked_ok(self, *args, **kwargs)
 
844
        # Emit D-Bus signal
 
845
        self.PropertyChanged(
 
846
            dbus.String("LastCheckedOK"),
 
847
            (self._datetime_to_dbus(self.last_checked_ok,
 
848
                                    variant_level=1)))
 
849
    
 
850
    def need_approval(self, *args, **kwargs):
 
851
        r = Client.need_approval(self, *args, **kwargs)
 
852
        # Emit D-Bus signal
 
853
        self.PropertyChanged(
 
854
            dbus.String("LastApprovalRequest"),
 
855
            (self._datetime_to_dbus(self.last_approval_request,
 
856
                                    variant_level=1)))
 
857
        return r
 
858
    
991
859
    def start_checker(self, *args, **kwargs):
992
860
        old_checker = self.checker
993
861
        if self.checker is not None:
1000
868
            and old_checker_pid != self.checker.pid):
1001
869
            # Emit D-Bus signal
1002
870
            self.CheckerStarted(self.current_checker_command)
 
871
            self.PropertyChanged(
 
872
                dbus.String("CheckerRunning"),
 
873
                dbus.Boolean(True, variant_level=1))
1003
874
        return r
1004
875
    
 
876
    def stop_checker(self, *args, **kwargs):
 
877
        old_checker = getattr(self, "checker", None)
 
878
        r = Client.stop_checker(self, *args, **kwargs)
 
879
        if (old_checker is not None
 
880
            and getattr(self, "checker", None) is None):
 
881
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
882
                                 dbus.Boolean(False, variant_level=1))
 
883
        return r
 
884
 
1005
885
    def _reset_approved(self):
1006
886
        self._approved = None
1007
887
        return False
1009
889
    def approve(self, value=True):
1010
890
        self.send_changedstate()
1011
891
        self._approved = value
1012
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
892
        gobject.timeout_add(self._timedelta_to_milliseconds
1013
893
                            (self.approval_duration),
1014
894
                            self._reset_approved)
1015
895
    
1016
896
    
1017
897
    ## D-Bus methods, signals & properties
1018
 
    _interface = "se.recompile.Mandos.Client"
 
898
    _interface = "se.bsnet.fukt.Mandos.Client"
1019
899
    
1020
900
    ## Signals
1021
901
    
1107
987
        if value is None:       # get
1108
988
            return dbus.Boolean(self.approved_by_default)
1109
989
        self.approved_by_default = bool(value)
 
990
        # Emit D-Bus signal
 
991
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
 
992
                             dbus.Boolean(value, variant_level=1))
1110
993
    
1111
994
    # ApprovalDelay - property
1112
995
    @dbus_service_property(_interface, signature="t",
1115
998
        if value is None:       # get
1116
999
            return dbus.UInt64(self.approval_delay_milliseconds())
1117
1000
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
1001
        # Emit D-Bus signal
 
1002
        self.PropertyChanged(dbus.String("ApprovalDelay"),
 
1003
                             dbus.UInt64(value, variant_level=1))
1118
1004
    
1119
1005
    # ApprovalDuration - property
1120
1006
    @dbus_service_property(_interface, signature="t",
1121
1007
                           access="readwrite")
1122
1008
    def ApprovalDuration_dbus_property(self, value=None):
1123
1009
        if value is None:       # get
1124
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
1010
            return dbus.UInt64(self._timedelta_to_milliseconds(
1125
1011
                    self.approval_duration))
1126
1012
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
1013
        # Emit D-Bus signal
 
1014
        self.PropertyChanged(dbus.String("ApprovalDuration"),
 
1015
                             dbus.UInt64(value, variant_level=1))
1127
1016
    
1128
1017
    # Name - property
1129
1018
    @dbus_service_property(_interface, signature="s", access="read")
1142
1031
        if value is None:       # get
1143
1032
            return dbus.String(self.host)
1144
1033
        self.host = value
 
1034
        # Emit D-Bus signal
 
1035
        self.PropertyChanged(dbus.String("Host"),
 
1036
                             dbus.String(value, variant_level=1))
1145
1037
    
1146
1038
    # Created - property
1147
1039
    @dbus_service_property(_interface, signature="s", access="read")
1148
1040
    def Created_dbus_property(self):
1149
 
        return dbus.String(datetime_to_dbus(self.created))
 
1041
        return dbus.String(self._datetime_to_dbus(self.created))
1150
1042
    
1151
1043
    # LastEnabled - property
1152
1044
    @dbus_service_property(_interface, signature="s", access="read")
1153
1045
    def LastEnabled_dbus_property(self):
1154
 
        return datetime_to_dbus(self.last_enabled)
 
1046
        if self.last_enabled is None:
 
1047
            return dbus.String("")
 
1048
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
1155
1049
    
1156
1050
    # Enabled - property
1157
1051
    @dbus_service_property(_interface, signature="b",
1171
1065
        if value is not None:
1172
1066
            self.checked_ok()
1173
1067
            return
1174
 
        return datetime_to_dbus(self.last_checked_ok)
1175
 
    
1176
 
    # Expires - property
1177
 
    @dbus_service_property(_interface, signature="s", access="read")
1178
 
    def Expires_dbus_property(self):
1179
 
        return datetime_to_dbus(self.expires)
 
1068
        if self.last_checked_ok is None:
 
1069
            return dbus.String("")
 
1070
        return dbus.String(self._datetime_to_dbus(self
 
1071
                                                  .last_checked_ok))
1180
1072
    
1181
1073
    # LastApprovalRequest - property
1182
1074
    @dbus_service_property(_interface, signature="s", access="read")
1183
1075
    def LastApprovalRequest_dbus_property(self):
1184
 
        return datetime_to_dbus(self.last_approval_request)
 
1076
        if self.last_approval_request is None:
 
1077
            return dbus.String("")
 
1078
        return dbus.String(self.
 
1079
                           _datetime_to_dbus(self
 
1080
                                             .last_approval_request))
1185
1081
    
1186
1082
    # Timeout - property
1187
1083
    @dbus_service_property(_interface, signature="t",
1190
1086
        if value is None:       # get
1191
1087
            return dbus.UInt64(self.timeout_milliseconds())
1192
1088
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1089
        # Emit D-Bus signal
 
1090
        self.PropertyChanged(dbus.String("Timeout"),
 
1091
                             dbus.UInt64(value, variant_level=1))
1193
1092
        if getattr(self, "disable_initiator_tag", None) is None:
1194
1093
            return
1195
1094
        # Reschedule timeout
1196
1095
        gobject.source_remove(self.disable_initiator_tag)
1197
1096
        self.disable_initiator_tag = None
1198
 
        self.expires = None
1199
 
        time_to_die = _timedelta_to_milliseconds((self
1200
 
                                                  .last_checked_ok
1201
 
                                                  + self.timeout)
1202
 
                                                 - datetime.datetime
1203
 
                                                 .utcnow())
 
1097
        time_to_die = (self.
 
1098
                       _timedelta_to_milliseconds((self
 
1099
                                                   .last_checked_ok
 
1100
                                                   + self.timeout)
 
1101
                                                  - datetime.datetime
 
1102
                                                  .utcnow()))
1204
1103
        if time_to_die <= 0:
1205
1104
            # The timeout has passed
1206
1105
            self.disable()
1207
1106
        else:
1208
 
            self.expires = (datetime.datetime.utcnow()
1209
 
                            + datetime.timedelta(milliseconds =
1210
 
                                                 time_to_die))
1211
1107
            self.disable_initiator_tag = (gobject.timeout_add
1212
1108
                                          (time_to_die, self.disable))
1213
1109
    
1214
 
    # ExtendedTimeout - property
1215
 
    @dbus_service_property(_interface, signature="t",
1216
 
                           access="readwrite")
1217
 
    def ExtendedTimeout_dbus_property(self, value=None):
1218
 
        if value is None:       # get
1219
 
            return dbus.UInt64(self.extended_timeout_milliseconds())
1220
 
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1221
 
    
1222
1110
    # Interval - property
1223
1111
    @dbus_service_property(_interface, signature="t",
1224
1112
                           access="readwrite")
1226
1114
        if value is None:       # get
1227
1115
            return dbus.UInt64(self.interval_milliseconds())
1228
1116
        self.interval = datetime.timedelta(0, 0, 0, value)
 
1117
        # Emit D-Bus signal
 
1118
        self.PropertyChanged(dbus.String("Interval"),
 
1119
                             dbus.UInt64(value, variant_level=1))
1229
1120
        if getattr(self, "checker_initiator_tag", None) is None:
1230
1121
            return
1231
1122
        # Reschedule checker run
1233
1124
        self.checker_initiator_tag = (gobject.timeout_add
1234
1125
                                      (value, self.start_checker))
1235
1126
        self.start_checker()    # Start one now, too
1236
 
    
 
1127
 
1237
1128
    # Checker - property
1238
1129
    @dbus_service_property(_interface, signature="s",
1239
1130
                           access="readwrite")
1241
1132
        if value is None:       # get
1242
1133
            return dbus.String(self.checker_command)
1243
1134
        self.checker_command = value
 
1135
        # Emit D-Bus signal
 
1136
        self.PropertyChanged(dbus.String("Checker"),
 
1137
                             dbus.String(self.checker_command,
 
1138
                                         variant_level=1))
1244
1139
    
1245
1140
    # CheckerRunning - property
1246
1141
    @dbus_service_property(_interface, signature="b",
1273
1168
        self._pipe.send(('init', fpr, address))
1274
1169
        if not self._pipe.recv():
1275
1170
            raise KeyError()
1276
 
    
 
1171
 
1277
1172
    def __getattribute__(self, name):
1278
1173
        if(name == '_pipe'):
1279
1174
            return super(ProxyClient, self).__getattribute__(name)
1286
1181
                self._pipe.send(('funcall', name, args, kwargs))
1287
1182
                return self._pipe.recv()[1]
1288
1183
            return func
1289
 
    
 
1184
 
1290
1185
    def __setattr__(self, name, value):
1291
1186
        if(name == '_pipe'):
1292
1187
            return super(ProxyClient, self).__setattr__(name, value)
1293
1188
        self._pipe.send(('setattr', name, value))
1294
1189
 
1295
 
class ClientDBusTransitional(ClientDBus):
1296
 
    __metaclass__ = AlternateDBusNamesMetaclass
1297
1190
 
1298
1191
class ClientHandler(socketserver.BaseRequestHandler, object):
1299
1192
    """A class to handle client connections.
1307
1200
                        unicode(self.client_address))
1308
1201
            logger.debug("Pipe FD: %d",
1309
1202
                         self.server.child_pipe.fileno())
1310
 
            
 
1203
 
1311
1204
            session = (gnutls.connection
1312
1205
                       .ClientSession(self.request,
1313
1206
                                      gnutls.connection
1314
1207
                                      .X509Credentials()))
1315
 
            
 
1208
 
1316
1209
            # Note: gnutls.connection.X509Credentials is really a
1317
1210
            # generic GnuTLS certificate credentials object so long as
1318
1211
            # no X.509 keys are added to it.  Therefore, we can use it
1319
1212
            # here despite using OpenPGP certificates.
1320
 
            
 
1213
 
1321
1214
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1322
1215
            #                      "+AES-256-CBC", "+SHA1",
1323
1216
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1329
1222
            (gnutls.library.functions
1330
1223
             .gnutls_priority_set_direct(session._c_object,
1331
1224
                                         priority, None))
1332
 
            
 
1225
 
1333
1226
            # Start communication using the Mandos protocol
1334
1227
            # Get protocol number
1335
1228
            line = self.request.makefile().readline()
1340
1233
            except (ValueError, IndexError, RuntimeError) as error:
1341
1234
                logger.error("Unknown protocol version: %s", error)
1342
1235
                return
1343
 
            
 
1236
 
1344
1237
            # Start GnuTLS connection
1345
1238
            try:
1346
1239
                session.handshake()
1350
1243
                # established.  Just abandon the request.
1351
1244
                return
1352
1245
            logger.debug("Handshake succeeded")
1353
 
            
 
1246
 
1354
1247
            approval_required = False
1355
1248
            try:
1356
1249
                try:
1361
1254
                    logger.warning("Bad certificate: %s", error)
1362
1255
                    return
1363
1256
                logger.debug("Fingerprint: %s", fpr)
1364
 
                
 
1257
 
1365
1258
                try:
1366
1259
                    client = ProxyClient(child_pipe, fpr,
1367
1260
                                         self.client_address)
1375
1268
                
1376
1269
                while True:
1377
1270
                    if not client.enabled:
1378
 
                        logger.info("Client %s is disabled",
 
1271
                        logger.warning("Client %s is disabled",
1379
1272
                                       client.name)
1380
1273
                        if self.server.use_dbus:
1381
1274
                            # Emit D-Bus signal
1382
 
                            client.Rejected("Disabled")
 
1275
                            client.Rejected("Disabled")                    
1383
1276
                        return
1384
1277
                    
1385
1278
                    if client._approved or not client.approval_delay:
1402
1295
                        return
1403
1296
                    
1404
1297
                    #wait until timeout or approved
 
1298
                    #x = float(client._timedelta_to_milliseconds(delay))
1405
1299
                    time = datetime.datetime.now()
1406
1300
                    client.changedstate.acquire()
1407
 
                    (client.changedstate.wait
1408
 
                     (float(client._timedelta_to_milliseconds(delay)
1409
 
                            / 1000)))
 
1301
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1410
1302
                    client.changedstate.release()
1411
1303
                    time2 = datetime.datetime.now()
1412
1304
                    if (time2 - time) >= delay:
1434
1326
                                 sent, len(client.secret)
1435
1327
                                 - (sent_size + sent))
1436
1328
                    sent_size += sent
1437
 
                
 
1329
 
1438
1330
                logger.info("Sending secret to %s", client.name)
1439
 
                # bump the timeout using extended_timeout
1440
 
                client.checked_ok(client.extended_timeout)
 
1331
                # bump the timeout as if seen
 
1332
                client.checked_ok()
1441
1333
                if self.server.use_dbus:
1442
1334
                    # Emit D-Bus signal
1443
1335
                    client.GotSecret()
1522
1414
        except:
1523
1415
            self.handle_error(request, address)
1524
1416
        self.close_request(request)
1525
 
    
 
1417
            
1526
1418
    def process_request(self, request, address):
1527
1419
        """Start a new process to process the request."""
1528
 
        proc = multiprocessing.Process(target = self.sub_process_main,
1529
 
                                       args = (request,
1530
 
                                               address))
1531
 
        proc.start()
1532
 
        return proc
1533
 
 
 
1420
        multiprocessing.Process(target = self.sub_process_main,
 
1421
                                args = (request, address)).start()
1534
1422
 
1535
1423
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1536
1424
    """ adds a pipe to the MixIn """
1540
1428
        This function creates a new pipe in self.pipe
1541
1429
        """
1542
1430
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1543
 
        
1544
 
        proc = MultiprocessingMixIn.process_request(self, request,
1545
 
                                                    client_address)
 
1431
 
 
1432
        super(MultiprocessingMixInWithPipe,
 
1433
              self).process_request(request, client_address)
1546
1434
        self.child_pipe.close()
1547
 
        self.add_pipe(parent_pipe, proc)
1548
 
    
1549
 
    def add_pipe(self, parent_pipe, proc):
 
1435
        self.add_pipe(parent_pipe)
 
1436
 
 
1437
    def add_pipe(self, parent_pipe):
1550
1438
        """Dummy function; override as necessary"""
1551
1439
        raise NotImplementedError
1552
1440
 
1553
 
 
1554
1441
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1555
1442
                     socketserver.TCPServer, object):
1556
1443
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1640
1527
    def server_activate(self):
1641
1528
        if self.enabled:
1642
1529
            return socketserver.TCPServer.server_activate(self)
1643
 
    
1644
1530
    def enable(self):
1645
1531
        self.enabled = True
1646
 
    
1647
 
    def add_pipe(self, parent_pipe, proc):
 
1532
    def add_pipe(self, parent_pipe):
1648
1533
        # Call "handle_ipc" for both data and EOF events
1649
1534
        gobject.io_add_watch(parent_pipe.fileno(),
1650
1535
                             gobject.IO_IN | gobject.IO_HUP,
1651
1536
                             functools.partial(self.handle_ipc,
1652
 
                                               parent_pipe =
1653
 
                                               parent_pipe,
1654
 
                                               proc = proc))
1655
 
    
 
1537
                                               parent_pipe = parent_pipe))
 
1538
        
1656
1539
    def handle_ipc(self, source, condition, parent_pipe=None,
1657
 
                   proc = None, client_object=None):
 
1540
                   client_object=None):
1658
1541
        condition_names = {
1659
1542
            gobject.IO_IN: "IN",   # There is data to read.
1660
1543
            gobject.IO_OUT: "OUT", # Data can be written (without
1669
1552
                                       for cond, name in
1670
1553
                                       condition_names.iteritems()
1671
1554
                                       if cond & condition)
1672
 
        # error, or the other end of multiprocessing.Pipe has closed
 
1555
        # error or the other end of multiprocessing.Pipe has closed
1673
1556
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1674
 
            # Wait for other process to exit
1675
 
            proc.join()
1676
1557
            return False
1677
1558
        
1678
1559
        # Read a request from the child
1688
1569
                    client = c
1689
1570
                    break
1690
1571
            else:
1691
 
                logger.info("Client not found for fingerprint: %s, ad"
1692
 
                            "dress: %s", fpr, address)
 
1572
                logger.warning("Client not found for fingerprint: %s, ad"
 
1573
                               "dress: %s", fpr, address)
1693
1574
                if self.use_dbus:
1694
1575
                    # Emit D-Bus signal
1695
 
                    mandos_dbus_service.ClientNotFound(fpr,
1696
 
                                                       address[0])
 
1576
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
1697
1577
                parent_pipe.send(False)
1698
1578
                return False
1699
1579
            
1700
1580
            gobject.io_add_watch(parent_pipe.fileno(),
1701
1581
                                 gobject.IO_IN | gobject.IO_HUP,
1702
1582
                                 functools.partial(self.handle_ipc,
1703
 
                                                   parent_pipe =
1704
 
                                                   parent_pipe,
1705
 
                                                   proc = proc,
1706
 
                                                   client_object =
1707
 
                                                   client))
 
1583
                                                   parent_pipe = parent_pipe,
 
1584
                                                   client_object = client))
1708
1585
            parent_pipe.send(True)
1709
 
            # remove the old hook in favor of the new above hook on
1710
 
            # same fileno
 
1586
            # remove the old hook in favor of the new above hook on same fileno
1711
1587
            return False
1712
1588
        if command == 'funcall':
1713
1589
            funcname = request[1]
1714
1590
            args = request[2]
1715
1591
            kwargs = request[3]
1716
1592
            
1717
 
            parent_pipe.send(('data', getattr(client_object,
1718
 
                                              funcname)(*args,
1719
 
                                                         **kwargs)))
1720
 
        
 
1593
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1594
 
1721
1595
        if command == 'getattr':
1722
1596
            attrname = request[1]
1723
1597
            if callable(client_object.__getattribute__(attrname)):
1724
1598
                parent_pipe.send(('function',))
1725
1599
            else:
1726
 
                parent_pipe.send(('data', client_object
1727
 
                                  .__getattribute__(attrname)))
 
1600
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1728
1601
        
1729
1602
        if command == 'setattr':
1730
1603
            attrname = request[1]
1731
1604
            value = request[2]
1732
1605
            setattr(client_object, attrname, value)
1733
 
        
 
1606
 
1734
1607
        return True
1735
1608
 
1736
1609
 
1915
1788
    debuglevel = server_settings["debuglevel"]
1916
1789
    use_dbus = server_settings["use_dbus"]
1917
1790
    use_ipv6 = server_settings["use_ipv6"]
1918
 
    
 
1791
 
1919
1792
    if server_settings["servicename"] != "Mandos":
1920
1793
        syslogger.setFormatter(logging.Formatter
1921
1794
                               ('Mandos (%s) [%%(process)d]:'
1923
1796
                                % server_settings["servicename"]))
1924
1797
    
1925
1798
    # Parse config file with clients
1926
 
    client_defaults = { "timeout": "5m",
1927
 
                        "extended_timeout": "15m",
1928
 
                        "interval": "2m",
 
1799
    client_defaults = { "timeout": "1h",
 
1800
                        "interval": "5m",
1929
1801
                        "checker": "fping -q -- %%(host)s",
1930
1802
                        "host": "",
1931
1803
                        "approval_delay": "0s",
1976
1848
            raise error
1977
1849
    
1978
1850
    if not debug and not debuglevel:
1979
 
        logger.setLevel(logging.WARNING)
 
1851
        syslogger.setLevel(logging.WARNING)
 
1852
        console.setLevel(logging.WARNING)
1980
1853
    if debuglevel:
1981
1854
        level = getattr(logging, debuglevel.upper())
1982
 
        logger.setLevel(level)
1983
 
    
 
1855
        syslogger.setLevel(level)
 
1856
        console.setLevel(level)
 
1857
 
1984
1858
    if debug:
1985
 
        logger.setLevel(logging.DEBUG)
1986
1859
        # Enable all possible GnuTLS debugging
1987
1860
        
1988
1861
        # "Use a log level over 10 to enable all debugging options."
2018
1891
    # End of Avahi example code
2019
1892
    if use_dbus:
2020
1893
        try:
2021
 
            bus_name = dbus.service.BusName("se.recompile.Mandos",
 
1894
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
2022
1895
                                            bus, do_not_queue=True)
2023
 
            old_bus_name = (dbus.service.BusName
2024
 
                            ("se.bsnet.fukt.Mandos", bus,
2025
 
                             do_not_queue=True))
2026
1896
        except dbus.exceptions.NameExistsException as e:
2027
1897
            logger.error(unicode(e) + ", disabling D-Bus")
2028
1898
            use_dbus = False
2029
1899
            server_settings["use_dbus"] = False
2030
1900
            tcp_server.use_dbus = False
2031
1901
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2032
 
    service = AvahiServiceToSyslog(name =
2033
 
                                   server_settings["servicename"],
2034
 
                                   servicetype = "_mandos._tcp",
2035
 
                                   protocol = protocol, bus = bus)
 
1902
    service = AvahiService(name = server_settings["servicename"],
 
1903
                           servicetype = "_mandos._tcp",
 
1904
                           protocol = protocol, bus = bus)
2036
1905
    if server_settings["interface"]:
2037
1906
        service.interface = (if_nametoindex
2038
1907
                             (str(server_settings["interface"])))
2042
1911
    
2043
1912
    client_class = Client
2044
1913
    if use_dbus:
2045
 
        client_class = functools.partial(ClientDBusTransitional,
2046
 
                                         bus = bus)
 
1914
        client_class = functools.partial(ClientDBus, bus = bus)
2047
1915
    def client_config_items(config, section):
2048
1916
        special_settings = {
2049
1917
            "approved_by_default":
2079
1947
        del pidfilename
2080
1948
        
2081
1949
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2082
 
    
 
1950
 
2083
1951
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2084
1952
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2085
1953
    
2088
1956
            """A D-Bus proxy object"""
2089
1957
            def __init__(self):
2090
1958
                dbus.service.Object.__init__(self, bus, "/")
2091
 
            _interface = "se.recompile.Mandos"
 
1959
            _interface = "se.bsnet.fukt.Mandos"
2092
1960
            
2093
1961
            @dbus.service.signal(_interface, signature="o")
2094
1962
            def ClientAdded(self, objpath):
2136
2004
            
2137
2005
            del _interface
2138
2006
        
2139
 
        class MandosDBusServiceTransitional(MandosDBusService):
2140
 
            __metaclass__ = AlternateDBusNamesMetaclass
2141
 
        mandos_dbus_service = MandosDBusServiceTransitional()
 
2007
        mandos_dbus_service = MandosDBusService()
2142
2008
    
2143
2009
    def cleanup():
2144
2010
        "Cleanup function; run on exit"
2145
2011
        service.cleanup()
2146
2012
        
2147
 
        multiprocessing.active_children()
2148
2013
        while tcp_server.clients:
2149
2014
            client = tcp_server.clients.pop()
2150
2015
            if use_dbus:
2154
2019
            client.disable(quiet=True)
2155
2020
            if use_dbus:
2156
2021
                # Emit D-Bus signal
2157
 
                mandos_dbus_service.ClientRemoved(client
2158
 
                                                  .dbus_object_path,
 
2022
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2159
2023
                                                  client.name)
2160
2024
    
2161
2025
    atexit.register(cleanup)
2210
2074
    # Must run before the D-Bus bus name gets deregistered
2211
2075
    cleanup()
2212
2076
 
2213
 
 
2214
2077
if __name__ == '__main__':
2215
2078
    main()