/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: 2015-05-31 15:56:58 UTC
  • Revision ID: teddy@recompile.se-20150531155658-l7znu7zlqr2dmuwd
mandos: Generate better messages in exceptions.

mandos (ProxyClient.__init__): Include fingerprint in KeyError().
(rfc3339_duration_to_delta): Include duration in ValueError().

Show diffs side-by-side

added added

removed removed

Lines of Context:
395
395
                    logger.error(bad_states[state] + ": %r", error)
396
396
            self.cleanup()
397
397
        elif state == avahi.SERVER_RUNNING:
398
 
            try:
399
 
                self.add()
400
 
            except dbus.exceptions.DBusException as error:
401
 
                if (error.get_dbus_name()
402
 
                    == "org.freedesktop.Avahi.CollisionError"):
403
 
                    logger.info("Local Zeroconf service name"
404
 
                                " collision.")
405
 
                    return self.rename(remove=False)
406
 
                else:
407
 
                    logger.critical("D-Bus Exception", exc_info=error)
408
 
                    self.cleanup()
409
 
                    os._exit(1)
 
398
            self.add()
410
399
        else:
411
400
            if error is None:
412
401
                logger.debug("Unknown state: %r", state)
435
424
            .format(self.name)))
436
425
        return ret
437
426
 
438
 
def call_pipe(connection,       # : multiprocessing.Connection
439
 
              func, *args, **kwargs):
440
 
    """This function is meant to be called by multiprocessing.Process
441
 
    
442
 
    This function runs func(*args, **kwargs), and writes the resulting
443
 
    return value on the provided multiprocessing.Connection.
444
 
    """
445
 
    connection.send(func(*args, **kwargs))
446
 
    connection.close()
447
427
 
448
428
class Client(object):
449
429
    """A representation of a client host served by this server.
476
456
    last_checker_status: integer between 0 and 255 reflecting exit
477
457
                         status of last checker. -1 reflects crashed
478
458
                         checker, -2 means no checker completed yet.
479
 
    last_checker_signal: The signal which killed the last checker, if
480
 
                         last_checker_status is -1
481
459
    last_enabled: datetime.datetime(); (UTC) or None
482
460
    name:       string; from the config file, used in log messages and
483
461
                        D-Bus identifiers
657
635
        # Also start a new checker *right now*.
658
636
        self.start_checker()
659
637
    
660
 
    def checker_callback(self, source, condition, connection,
661
 
                         command):
 
638
    def checker_callback(self, pid, condition, command):
662
639
        """The checker has completed, so take appropriate actions."""
663
640
        self.checker_callback_tag = None
664
641
        self.checker = None
665
 
        # Read return code from connection (see call_pipe)
666
 
        returncode = connection.recv()
667
 
        connection.close()
668
 
        
669
 
        if returncode >= 0:
670
 
            self.last_checker_status = returncode
671
 
            self.last_checker_signal = None
 
642
        if os.WIFEXITED(condition):
 
643
            self.last_checker_status = os.WEXITSTATUS(condition)
672
644
            if self.last_checker_status == 0:
673
645
                logger.info("Checker for %(name)s succeeded",
674
646
                            vars(self))
677
649
                logger.info("Checker for %(name)s failed", vars(self))
678
650
        else:
679
651
            self.last_checker_status = -1
680
 
            self.last_checker_signal = -returncode
681
652
            logger.warning("Checker for %(name)s crashed?",
682
653
                           vars(self))
683
 
        return False
684
654
    
685
655
    def checked_ok(self):
686
656
        """Assert that the client has been seen, alive and well."""
687
657
        self.last_checked_ok = datetime.datetime.utcnow()
688
658
        self.last_checker_status = 0
689
 
        self.last_checker_signal = None
690
659
        self.bump_timeout()
691
660
    
692
661
    def bump_timeout(self, timeout=None):
718
687
        # than 'timeout' for the client to be disabled, which is as it
719
688
        # should be.
720
689
        
721
 
        if self.checker is not None and not self.checker.is_alive():
722
 
            logger.warning("Checker was not alive; joining")
723
 
            self.checker.join()
724
 
            self.checker = None
 
690
        # If a checker exists, make sure it is not a zombie
 
691
        try:
 
692
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
693
        except AttributeError:
 
694
            pass
 
695
        except OSError as error:
 
696
            if error.errno != errno.ECHILD:
 
697
                raise
 
698
        else:
 
699
            if pid:
 
700
                logger.warning("Checker was a zombie")
 
701
                gobject.source_remove(self.checker_callback_tag)
 
702
                self.checker_callback(pid, status,
 
703
                                      self.current_checker_command)
725
704
        # Start a new checker if needed
726
705
        if self.checker is None:
727
706
            # Escape attributes for the shell
736
715
                             exc_info=error)
737
716
                return True     # Try again later
738
717
            self.current_checker_command = command
739
 
            logger.info("Starting checker %r for %s", command,
740
 
                        self.name)
741
 
            # We don't need to redirect stdout and stderr, since
742
 
            # in normal mode, that is already done by daemon(),
743
 
            # and in debug mode we don't want to.  (Stdin is
744
 
            # always replaced by /dev/null.)
745
 
            # The exception is when not debugging but nevertheless
746
 
            # running in the foreground; use the previously
747
 
            # created wnull.
748
 
            popen_args = { "close_fds": True,
749
 
                           "shell": True,
750
 
                           "cwd": "/" }
751
 
            if (not self.server_settings["debug"]
752
 
                and self.server_settings["foreground"]):
753
 
                popen_args.update({"stdout": wnull,
754
 
                                   "stderr": wnull })
755
 
            pipe = multiprocessing.Pipe(duplex = False)
756
 
            self.checker = multiprocessing.Process(
757
 
                target = call_pipe,
758
 
                args = (pipe[1], subprocess.call, command),
759
 
                kwargs = popen_args)
760
 
            self.checker.start()
761
 
            self.checker_callback_tag = gobject.io_add_watch(
762
 
                pipe[0].fileno(), gobject.IO_IN,
763
 
                self.checker_callback, pipe[0], command)
 
718
            try:
 
719
                logger.info("Starting checker %r for %s", command,
 
720
                            self.name)
 
721
                # We don't need to redirect stdout and stderr, since
 
722
                # in normal mode, that is already done by daemon(),
 
723
                # and in debug mode we don't want to.  (Stdin is
 
724
                # always replaced by /dev/null.)
 
725
                # The exception is when not debugging but nevertheless
 
726
                # running in the foreground; use the previously
 
727
                # created wnull.
 
728
                popen_args = {}
 
729
                if (not self.server_settings["debug"]
 
730
                    and self.server_settings["foreground"]):
 
731
                    popen_args.update({"stdout": wnull,
 
732
                                       "stderr": wnull })
 
733
                self.checker = subprocess.Popen(command,
 
734
                                                close_fds=True,
 
735
                                                shell=True,
 
736
                                                cwd="/",
 
737
                                                **popen_args)
 
738
            except OSError as error:
 
739
                logger.error("Failed to start subprocess",
 
740
                             exc_info=error)
 
741
                return True
 
742
            self.checker_callback_tag = gobject.child_watch_add(
 
743
                self.checker.pid, self.checker_callback, data=command)
 
744
            # The checker may have completed before the gobject
 
745
            # watch was added.  Check for this.
 
746
            try:
 
747
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
748
            except OSError as error:
 
749
                if error.errno == errno.ECHILD:
 
750
                    # This should never happen
 
751
                    logger.error("Child process vanished",
 
752
                                 exc_info=error)
 
753
                    return True
 
754
                raise
 
755
            if pid:
 
756
                gobject.source_remove(self.checker_callback_tag)
 
757
                self.checker_callback(pid, status, command)
764
758
        # Re-run this periodically if run by gobject.timeout_add
765
759
        return True
766
760
    
772
766
        if getattr(self, "checker", None) is None:
773
767
            return
774
768
        logger.debug("Stopping checker for %(name)s", vars(self))
775
 
        self.checker.terminate()
 
769
        try:
 
770
            self.checker.terminate()
 
771
            #time.sleep(0.5)
 
772
            #if self.checker.poll() is None:
 
773
            #    self.checker.kill()
 
774
        except OSError as error:
 
775
            if error.errno != errno.ESRCH: # No such process
 
776
                raise
776
777
        self.checker = None
777
778
 
778
779
 
842
843
                           access="r")
843
844
    def Property_dbus_property(self):
844
845
        return dbus.Boolean(False)
845
 
    
846
 
    See also the DBusObjectWithAnnotations class.
847
846
    """
848
847
    
849
848
    def decorator(func):
871
870
    pass
872
871
 
873
872
 
874
 
class DBusObjectWithAnnotations(dbus.service.Object):
875
 
    """A D-Bus object with annotations.
 
873
class DBusObjectWithProperties(dbus.service.Object):
 
874
    """A D-Bus object with properties.
876
875
    
877
 
    Classes inheriting from this can use the dbus_annotations
878
 
    decorator to add annotations to methods or signals.
 
876
    Classes inheriting from this can use the dbus_service_property
 
877
    decorator to expose methods as D-Bus properties.  It exposes the
 
878
    standard Get(), Set(), and GetAll() methods on the D-Bus.
879
879
    """
880
880
    
881
881
    @staticmethod
897
897
                for name, athing in
898
898
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
899
899
    
900
 
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
901
 
                         out_signature = "s",
902
 
                         path_keyword = 'object_path',
903
 
                         connection_keyword = 'connection')
904
 
    def Introspect(self, object_path, connection):
905
 
        """Overloading of standard D-Bus method.
906
 
        
907
 
        Inserts annotation tags on methods and signals.
908
 
        """
909
 
        xmlstring = dbus.service.Object.Introspect(self, object_path,
910
 
                                                   connection)
911
 
        try:
912
 
            document = xml.dom.minidom.parseString(xmlstring)
913
 
            
914
 
            for if_tag in document.getElementsByTagName("interface"):
915
 
                # Add annotation tags
916
 
                for typ in ("method", "signal"):
917
 
                    for tag in if_tag.getElementsByTagName(typ):
918
 
                        annots = dict()
919
 
                        for name, prop in (self.
920
 
                                           _get_all_dbus_things(typ)):
921
 
                            if (name == tag.getAttribute("name")
922
 
                                and prop._dbus_interface
923
 
                                == if_tag.getAttribute("name")):
924
 
                                annots.update(getattr(
925
 
                                    prop, "_dbus_annotations", {}))
926
 
                        for name, value in annots.items():
927
 
                            ann_tag = document.createElement(
928
 
                                "annotation")
929
 
                            ann_tag.setAttribute("name", name)
930
 
                            ann_tag.setAttribute("value", value)
931
 
                            tag.appendChild(ann_tag)
932
 
                # Add interface annotation tags
933
 
                for annotation, value in dict(
934
 
                    itertools.chain.from_iterable(
935
 
                        annotations().items()
936
 
                        for name, annotations
937
 
                        in self._get_all_dbus_things("interface")
938
 
                        if name == if_tag.getAttribute("name")
939
 
                        )).items():
940
 
                    ann_tag = document.createElement("annotation")
941
 
                    ann_tag.setAttribute("name", annotation)
942
 
                    ann_tag.setAttribute("value", value)
943
 
                    if_tag.appendChild(ann_tag)
944
 
                # Fix argument name for the Introspect method itself
945
 
                if (if_tag.getAttribute("name")
946
 
                                == dbus.INTROSPECTABLE_IFACE):
947
 
                    for cn in if_tag.getElementsByTagName("method"):
948
 
                        if cn.getAttribute("name") == "Introspect":
949
 
                            for arg in cn.getElementsByTagName("arg"):
950
 
                                if (arg.getAttribute("direction")
951
 
                                    == "out"):
952
 
                                    arg.setAttribute("name",
953
 
                                                     "xml_data")
954
 
            xmlstring = document.toxml("utf-8")
955
 
            document.unlink()
956
 
        except (AttributeError, xml.dom.DOMException,
957
 
                xml.parsers.expat.ExpatError) as error:
958
 
            logger.error("Failed to override Introspection method",
959
 
                         exc_info=error)
960
 
        return xmlstring
961
 
 
962
 
 
963
 
class DBusObjectWithProperties(DBusObjectWithAnnotations):
964
 
    """A D-Bus object with properties.
965
 
    
966
 
    Classes inheriting from this can use the dbus_service_property
967
 
    decorator to expose methods as D-Bus properties.  It exposes the
968
 
    standard Get(), Set(), and GetAll() methods on the D-Bus.
969
 
    """
970
 
    
971
900
    def _get_dbus_property(self, interface_name, property_name):
972
901
        """Returns a bound method if one exists which is a D-Bus
973
902
        property with the specified name and interface.
1058
987
        
1059
988
        Inserts property tags and interface annotation tags.
1060
989
        """
1061
 
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
1062
 
                                                         object_path,
1063
 
                                                         connection)
 
990
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
991
                                                   connection)
1064
992
        try:
1065
993
            document = xml.dom.minidom.parseString(xmlstring)
1066
994
            
1079
1007
                            if prop._dbus_interface
1080
1008
                            == if_tag.getAttribute("name")):
1081
1009
                    if_tag.appendChild(tag)
1082
 
                # Add annotation tags for properties
1083
 
                for tag in if_tag.getElementsByTagName("property"):
1084
 
                    annots = dict()
1085
 
                    for name, prop in self._get_all_dbus_things(
1086
 
                            "property"):
1087
 
                        if (name == tag.getAttribute("name")
1088
 
                            and prop._dbus_interface
1089
 
                            == if_tag.getAttribute("name")):
1090
 
                            annots.update(getattr(
1091
 
                                prop, "_dbus_annotations", {}))
1092
 
                    for name, value in annots.items():
1093
 
                        ann_tag = document.createElement(
1094
 
                            "annotation")
1095
 
                        ann_tag.setAttribute("name", name)
1096
 
                        ann_tag.setAttribute("value", value)
1097
 
                        tag.appendChild(ann_tag)
 
1010
                # Add annotation tags
 
1011
                for typ in ("method", "signal", "property"):
 
1012
                    for tag in if_tag.getElementsByTagName(typ):
 
1013
                        annots = dict()
 
1014
                        for name, prop in (self.
 
1015
                                           _get_all_dbus_things(typ)):
 
1016
                            if (name == tag.getAttribute("name")
 
1017
                                and prop._dbus_interface
 
1018
                                == if_tag.getAttribute("name")):
 
1019
                                annots.update(getattr(
 
1020
                                    prop, "_dbus_annotations", {}))
 
1021
                        for name, value in annots.items():
 
1022
                            ann_tag = document.createElement(
 
1023
                                "annotation")
 
1024
                            ann_tag.setAttribute("name", name)
 
1025
                            ann_tag.setAttribute("value", value)
 
1026
                            tag.appendChild(ann_tag)
 
1027
                # Add interface annotation tags
 
1028
                for annotation, value in dict(
 
1029
                    itertools.chain.from_iterable(
 
1030
                        annotations().items()
 
1031
                        for name, annotations
 
1032
                        in self._get_all_dbus_things("interface")
 
1033
                        if name == if_tag.getAttribute("name")
 
1034
                        )).items():
 
1035
                    ann_tag = document.createElement("annotation")
 
1036
                    ann_tag.setAttribute("name", annotation)
 
1037
                    ann_tag.setAttribute("value", value)
 
1038
                    if_tag.appendChild(ann_tag)
1098
1039
                # Add the names to the return values for the
1099
1040
                # "org.freedesktop.DBus.Properties" methods
1100
1041
                if (if_tag.getAttribute("name")
1170
1111
                interface_names.add(alt_interface)
1171
1112
                # Is this a D-Bus signal?
1172
1113
                if getattr(attribute, "_dbus_is_signal", False):
1173
 
                    if sys.version_info.major == 2:
1174
 
                        # Extract the original non-method undecorated
1175
 
                        # function by black magic
1176
 
                        nonmethod_func = (dict(
1177
 
                            zip(attribute.func_code.co_freevars,
1178
 
                                attribute.__closure__))
1179
 
                                          ["func"].cell_contents)
1180
 
                    else:
1181
 
                        nonmethod_func = attribute
 
1114
                    # Extract the original non-method undecorated
 
1115
                    # function by black magic
 
1116
                    nonmethod_func = (dict(
 
1117
                        zip(attribute.func_code.co_freevars,
 
1118
                            attribute.__closure__))
 
1119
                                      ["func"].cell_contents)
1182
1120
                    # Create a new, but exactly alike, function
1183
1121
                    # object, and decorate it to be a new D-Bus signal
1184
1122
                    # with the alternate D-Bus interface name
1185
 
                    if sys.version_info.major == 2:
1186
 
                        new_function = types.FunctionType(
1187
 
                            nonmethod_func.func_code,
1188
 
                            nonmethod_func.func_globals,
1189
 
                            nonmethod_func.func_name,
1190
 
                            nonmethod_func.func_defaults,
1191
 
                            nonmethod_func.func_closure)
1192
 
                    else:
1193
 
                        new_function = types.FunctionType(
1194
 
                            nonmethod_func.__code__,
1195
 
                            nonmethod_func.__globals__,
1196
 
                            nonmethod_func.__name__,
1197
 
                            nonmethod_func.__defaults__,
1198
 
                            nonmethod_func.__closure__)
1199
1123
                    new_function = (dbus.service.signal(
1200
 
                        alt_interface,
1201
 
                        attribute._dbus_signature)(new_function))
 
1124
                        alt_interface, attribute._dbus_signature)
 
1125
                                    (types.FunctionType(
 
1126
                                        nonmethod_func.func_code,
 
1127
                                        nonmethod_func.func_globals,
 
1128
                                        nonmethod_func.func_name,
 
1129
                                        nonmethod_func.func_defaults,
 
1130
                                        nonmethod_func.func_closure)))
1202
1131
                    # Copy annotations, if any
1203
1132
                    try:
1204
1133
                        new_function._dbus_annotations = dict(
1427
1356
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
1428
1357
        Client.__del__(self, *args, **kwargs)
1429
1358
    
1430
 
    def checker_callback(self, source, condition,
1431
 
                         connection, command, *args, **kwargs):
1432
 
        ret = Client.checker_callback(self, source, condition,
1433
 
                                      connection, command, *args,
1434
 
                                      **kwargs)
1435
 
        exitstatus = self.last_checker_status
1436
 
        if exitstatus >= 0:
 
1359
    def checker_callback(self, pid, condition, command,
 
1360
                         *args, **kwargs):
 
1361
        self.checker_callback_tag = None
 
1362
        self.checker = None
 
1363
        if os.WIFEXITED(condition):
 
1364
            exitstatus = os.WEXITSTATUS(condition)
1437
1365
            # Emit D-Bus signal
1438
1366
            self.CheckerCompleted(dbus.Int16(exitstatus),
1439
 
                                  # This is specific to GNU libC
1440
 
                                  dbus.Int64(exitstatus << 8),
 
1367
                                  dbus.Int64(condition),
1441
1368
                                  dbus.String(command))
1442
1369
        else:
1443
1370
            # Emit D-Bus signal
1444
1371
            self.CheckerCompleted(dbus.Int16(-1),
1445
 
                                  dbus.Int64(
1446
 
                                      # This is specific to GNU libC
1447
 
                                      (exitstatus << 8)
1448
 
                                      | self.last_checker_signal),
 
1372
                                  dbus.Int64(condition),
1449
1373
                                  dbus.String(command))
1450
 
        return ret
 
1374
        
 
1375
        return Client.checker_callback(self, pid, condition, command,
 
1376
                                       *args, **kwargs)
1451
1377
    
1452
1378
    def start_checker(self, *args, **kwargs):
1453
1379
        old_checker_pid = getattr(self.checker, "pid", None)
1528
1454
        self.checked_ok()
1529
1455
    
1530
1456
    # Enable - method
1531
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1532
1457
    @dbus.service.method(_interface)
1533
1458
    def Enable(self):
1534
1459
        "D-Bus method"
1535
1460
        self.enable()
1536
1461
    
1537
1462
    # StartChecker - method
1538
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1539
1463
    @dbus.service.method(_interface)
1540
1464
    def StartChecker(self):
1541
1465
        "D-Bus method"
1542
1466
        self.start_checker()
1543
1467
    
1544
1468
    # Disable - method
1545
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1546
1469
    @dbus.service.method(_interface)
1547
1470
    def Disable(self):
1548
1471
        "D-Bus method"
1549
1472
        self.disable()
1550
1473
    
1551
1474
    # StopChecker - method
1552
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1553
1475
    @dbus.service.method(_interface)
1554
1476
    def StopChecker(self):
1555
1477
        self.stop_checker()
1591
1513
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1592
1514
    
1593
1515
    # Name - property
1594
 
    @dbus_annotations(
1595
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1596
1516
    @dbus_service_property(_interface, signature="s", access="read")
1597
1517
    def Name_dbus_property(self):
1598
1518
        return dbus.String(self.name)
1599
1519
    
1600
1520
    # Fingerprint - property
1601
 
    @dbus_annotations(
1602
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1603
1521
    @dbus_service_property(_interface, signature="s", access="read")
1604
1522
    def Fingerprint_dbus_property(self):
1605
1523
        return dbus.String(self.fingerprint)
1614
1532
        self.host = str(value)
1615
1533
    
1616
1534
    # Created - property
1617
 
    @dbus_annotations(
1618
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1619
1535
    @dbus_service_property(_interface, signature="s", access="read")
1620
1536
    def Created_dbus_property(self):
1621
1537
        return datetime_to_dbus(self.created)
1736
1652
            self.stop_checker()
1737
1653
    
1738
1654
    # ObjectPath - property
1739
 
    @dbus_annotations(
1740
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const",
1741
 
         "org.freedesktop.DBus.Deprecated": "true"})
1742
1655
    @dbus_service_property(_interface, signature="o", access="read")
1743
1656
    def ObjectPath_dbus_property(self):
1744
1657
        return self.dbus_object_path # is already a dbus.ObjectPath
1745
1658
    
1746
1659
    # Secret = property
1747
 
    @dbus_annotations(
1748
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal":
1749
 
         "invalidates"})
1750
1660
    @dbus_service_property(_interface,
1751
1661
                           signature="ay",
1752
1662
                           access="write",
2231
2141
        
2232
2142
        if command == 'getattr':
2233
2143
            attrname = request[1]
2234
 
            if isinstance(client_object.__getattribute__(attrname),
2235
 
                          collections.Callable):
 
2144
            if callable(client_object.__getattribute__(attrname)):
2236
2145
                parent_pipe.send(('function', ))
2237
2146
            else:
2238
2147
                parent_pipe.send((
2273
2182
    # avoid excessive use of external libraries.
2274
2183
    
2275
2184
    # New type for defining tokens, syntax, and semantics all-in-one
 
2185
    Token = collections.namedtuple("Token",
 
2186
                                   ("regexp", # To match token; if
 
2187
                                              # "value" is not None,
 
2188
                                              # must have a "group"
 
2189
                                              # containing digits
 
2190
                                    "value",  # datetime.timedelta or
 
2191
                                              # None
 
2192
                                    "followers")) # Tokens valid after
 
2193
                                                  # this token
2276
2194
    Token = collections.namedtuple("Token", (
2277
2195
        "regexp",  # To match token; if "value" is not None, must have
2278
2196
                   # a "group" containing digits
2313
2231
    # Define starting values
2314
2232
    value = datetime.timedelta() # Value so far
2315
2233
    found_token = None
2316
 
    followers = frozenset((token_duration, )) # Following valid tokens
 
2234
    followers = frozenset((token_duration,)) # Following valid tokens
2317
2235
    s = duration                # String left to parse
2318
2236
    # Loop until end token is found
2319
2237
    while found_token is not token_end:
2476
2394
                        "debug": "False",
2477
2395
                        "priority":
2478
2396
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
2479
 
                        ":+SIGN-DSA-SHA256",
 
2397
                        ":+SIGN-RSA-SHA224:+SIGN-RSA-RMD160",
2480
2398
                        "servicename": "Mandos",
2481
2399
                        "use_dbus": "True",
2482
2400
                        "use_ipv6": "True",
2738
2656
                    pass
2739
2657
            
2740
2658
            # Clients who has passed its expire date can still be
2741
 
            # enabled if its last checker was successful.  A Client
 
2659
            # enabled if its last checker was successful.  Clients
2742
2660
            # whose checker succeeded before we stored its state is
2743
2661
            # assumed to have successfully run all checkers during
2744
2662
            # downtime.
2813
2731
        
2814
2732
        @alternate_dbus_interfaces(
2815
2733
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
2816
 
        class MandosDBusService(DBusObjectWithAnnotations):
 
2734
        class MandosDBusService(DBusObjectWithProperties):
2817
2735
            """A D-Bus proxy object"""
2818
2736
            
2819
2737
            def __init__(self):
2821
2739
            
2822
2740
            _interface = "se.recompile.Mandos"
2823
2741
            
 
2742
            @dbus_interface_annotations(_interface)
 
2743
            def _foo(self):
 
2744
                return {
 
2745
                    "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
2746
                    "false" }
 
2747
            
2824
2748
            @dbus.service.signal(_interface, signature="o")
2825
2749
            def ClientAdded(self, objpath):
2826
2750
                "D-Bus signal"