/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-08-02 09:36:40 UTC
  • Revision ID: teddy@recompile.se-20150802093640-nc0n17rbmqlbaxuf
Add D-Bus annotations on a few properties on the Client object.

The D-Bus property "Secret" on the interface
"se.recompile.Mandos.Client" should have the annotation
"org.freedesktop.DBus.Property.EmitsChangedSignal" set to
"invalidates".  Also, the properties "Created", "Fingerprint", "Name",
and "ObjectPath" should have the same annotation set to "const".

* mandos (ClientDBus.Name_dbus_property): Set annotation
                    "org.freedesktop.DBus.Property.EmitsChangedSignal"
                    to "const".
  (ClientDBus.Fingerprint_dbus_property): - '' -
  (ClientDBus.Created_dbus_property): - '' -
  (ClientDBus.ObjectPath_dbus_property): - '' -
  (ClientDBus.Secret_dbus_property): Set annotation
                    "org.freedesktop.DBus.Property.EmitsChangedSignal"
                    to "invalidates".

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
import argparse
45
45
import datetime
46
46
import errno
 
47
import gnutls.crypto
47
48
import gnutls.connection
48
49
import gnutls.errors
49
50
import gnutls.library.functions
103
104
if sys.version_info.major == 2:
104
105
    str = unicode
105
106
 
106
 
version = "1.7.1"
 
107
version = "1.6.9"
107
108
stored_state_file = "clients.pickle"
108
109
 
109
110
logger = logging.getLogger()
841
842
                           access="r")
842
843
    def Property_dbus_property(self):
843
844
        return dbus.Boolean(False)
844
 
    
845
 
    See also the DBusObjectWithAnnotations class.
846
845
    """
847
846
    
848
847
    def decorator(func):
870
869
    pass
871
870
 
872
871
 
873
 
class DBusObjectWithAnnotations(dbus.service.Object):
874
 
    """A D-Bus object with annotations.
 
872
class DBusObjectWithProperties(dbus.service.Object):
 
873
    """A D-Bus object with properties.
875
874
    
876
 
    Classes inheriting from this can use the dbus_annotations
877
 
    decorator to add annotations to methods or signals.
 
875
    Classes inheriting from this can use the dbus_service_property
 
876
    decorator to expose methods as D-Bus properties.  It exposes the
 
877
    standard Get(), Set(), and GetAll() methods on the D-Bus.
878
878
    """
879
879
    
880
880
    @staticmethod
896
896
                for name, athing in
897
897
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
898
898
    
899
 
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
900
 
                         out_signature = "s",
901
 
                         path_keyword = 'object_path',
902
 
                         connection_keyword = 'connection')
903
 
    def Introspect(self, object_path, connection):
904
 
        """Overloading of standard D-Bus method.
905
 
        
906
 
        Inserts annotation tags on methods and signals.
907
 
        """
908
 
        xmlstring = dbus.service.Object.Introspect(self, object_path,
909
 
                                                   connection)
910
 
        try:
911
 
            document = xml.dom.minidom.parseString(xmlstring)
912
 
            
913
 
            for if_tag in document.getElementsByTagName("interface"):
914
 
                # Add annotation tags
915
 
                for typ in ("method", "signal"):
916
 
                    for tag in if_tag.getElementsByTagName(typ):
917
 
                        annots = dict()
918
 
                        for name, prop in (self.
919
 
                                           _get_all_dbus_things(typ)):
920
 
                            if (name == tag.getAttribute("name")
921
 
                                and prop._dbus_interface
922
 
                                == if_tag.getAttribute("name")):
923
 
                                annots.update(getattr(
924
 
                                    prop, "_dbus_annotations", {}))
925
 
                        for name, value in annots.items():
926
 
                            ann_tag = document.createElement(
927
 
                                "annotation")
928
 
                            ann_tag.setAttribute("name", name)
929
 
                            ann_tag.setAttribute("value", value)
930
 
                            tag.appendChild(ann_tag)
931
 
                # Add interface annotation tags
932
 
                for annotation, value in dict(
933
 
                    itertools.chain.from_iterable(
934
 
                        annotations().items()
935
 
                        for name, annotations
936
 
                        in self._get_all_dbus_things("interface")
937
 
                        if name == if_tag.getAttribute("name")
938
 
                        )).items():
939
 
                    ann_tag = document.createElement("annotation")
940
 
                    ann_tag.setAttribute("name", annotation)
941
 
                    ann_tag.setAttribute("value", value)
942
 
                    if_tag.appendChild(ann_tag)
943
 
                # Fix argument name for the Introspect method itself
944
 
                if (if_tag.getAttribute("name")
945
 
                                == dbus.INTROSPECTABLE_IFACE):
946
 
                    for cn in if_tag.getElementsByTagName("method"):
947
 
                        if cn.getAttribute("name") == "Introspect":
948
 
                            for arg in cn.getElementsByTagName("arg"):
949
 
                                if (arg.getAttribute("direction")
950
 
                                    == "out"):
951
 
                                    arg.setAttribute("name",
952
 
                                                     "xml_data")
953
 
            xmlstring = document.toxml("utf-8")
954
 
            document.unlink()
955
 
        except (AttributeError, xml.dom.DOMException,
956
 
                xml.parsers.expat.ExpatError) as error:
957
 
            logger.error("Failed to override Introspection method",
958
 
                         exc_info=error)
959
 
        return xmlstring
960
 
 
961
 
 
962
 
class DBusObjectWithProperties(DBusObjectWithAnnotations):
963
 
    """A D-Bus object with properties.
964
 
    
965
 
    Classes inheriting from this can use the dbus_service_property
966
 
    decorator to expose methods as D-Bus properties.  It exposes the
967
 
    standard Get(), Set(), and GetAll() methods on the D-Bus.
968
 
    """
969
 
    
970
899
    def _get_dbus_property(self, interface_name, property_name):
971
900
        """Returns a bound method if one exists which is a D-Bus
972
901
        property with the specified name and interface.
982
911
        raise DBusPropertyNotFound("{}:{}.{}".format(
983
912
            self.dbus_object_path, interface_name, property_name))
984
913
    
985
 
    @classmethod
986
 
    def _get_all_interface_names(cls):
987
 
        """Get a sequence of all interfaces supported by an object"""
988
 
        return (name for name in set(getattr(getattr(x, attr),
989
 
                                             "_dbus_interface", None)
990
 
                                     for x in (inspect.getmro(cls))
991
 
                                     for attr in dir(x))
992
 
                if name is not None)
993
 
    
994
914
    @dbus.service.method(dbus.PROPERTIES_IFACE,
995
915
                         in_signature="ss",
996
916
                         out_signature="v")
1066
986
        
1067
987
        Inserts property tags and interface annotation tags.
1068
988
        """
1069
 
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
1070
 
                                                         object_path,
1071
 
                                                         connection)
 
989
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
990
                                                   connection)
1072
991
        try:
1073
992
            document = xml.dom.minidom.parseString(xmlstring)
1074
993
            
1087
1006
                            if prop._dbus_interface
1088
1007
                            == if_tag.getAttribute("name")):
1089
1008
                    if_tag.appendChild(tag)
1090
 
                # Add annotation tags for properties
1091
 
                for tag in if_tag.getElementsByTagName("property"):
1092
 
                    annots = dict()
1093
 
                    for name, prop in self._get_all_dbus_things(
1094
 
                            "property"):
1095
 
                        if (name == tag.getAttribute("name")
1096
 
                            and prop._dbus_interface
1097
 
                            == if_tag.getAttribute("name")):
1098
 
                            annots.update(getattr(
1099
 
                                prop, "_dbus_annotations", {}))
1100
 
                    for name, value in annots.items():
1101
 
                        ann_tag = document.createElement(
1102
 
                            "annotation")
1103
 
                        ann_tag.setAttribute("name", name)
1104
 
                        ann_tag.setAttribute("value", value)
1105
 
                        tag.appendChild(ann_tag)
 
1009
                # Add annotation tags
 
1010
                for typ in ("method", "signal", "property"):
 
1011
                    for tag in if_tag.getElementsByTagName(typ):
 
1012
                        annots = dict()
 
1013
                        for name, prop in (self.
 
1014
                                           _get_all_dbus_things(typ)):
 
1015
                            if (name == tag.getAttribute("name")
 
1016
                                and prop._dbus_interface
 
1017
                                == if_tag.getAttribute("name")):
 
1018
                                annots.update(getattr(
 
1019
                                    prop, "_dbus_annotations", {}))
 
1020
                        for name, value in annots.items():
 
1021
                            ann_tag = document.createElement(
 
1022
                                "annotation")
 
1023
                            ann_tag.setAttribute("name", name)
 
1024
                            ann_tag.setAttribute("value", value)
 
1025
                            tag.appendChild(ann_tag)
 
1026
                # Add interface annotation tags
 
1027
                for annotation, value in dict(
 
1028
                    itertools.chain.from_iterable(
 
1029
                        annotations().items()
 
1030
                        for name, annotations
 
1031
                        in self._get_all_dbus_things("interface")
 
1032
                        if name == if_tag.getAttribute("name")
 
1033
                        )).items():
 
1034
                    ann_tag = document.createElement("annotation")
 
1035
                    ann_tag.setAttribute("name", annotation)
 
1036
                    ann_tag.setAttribute("value", value)
 
1037
                    if_tag.appendChild(ann_tag)
1106
1038
                # Add the names to the return values for the
1107
1039
                # "org.freedesktop.DBus.Properties" methods
1108
1040
                if (if_tag.getAttribute("name")
1126
1058
                         exc_info=error)
1127
1059
        return xmlstring
1128
1060
 
1129
 
try:
1130
 
    dbus.OBJECT_MANAGER_IFACE
1131
 
except AttributeError:
1132
 
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1133
 
 
1134
 
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1135
 
    """A D-Bus object with an ObjectManager.
1136
 
    
1137
 
    Classes inheriting from this exposes the standard
1138
 
    GetManagedObjects call and the InterfacesAdded and
1139
 
    InterfacesRemoved signals on the standard
1140
 
    "org.freedesktop.DBus.ObjectManager" interface.
1141
 
    
1142
 
    Note: No signals are sent automatically; they must be sent
1143
 
    manually.
1144
 
    """
1145
 
    @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1146
 
                         out_signature = "a{oa{sa{sv}}}")
1147
 
    def GetManagedObjects(self):
1148
 
        """This function must be overridden"""
1149
 
        raise NotImplementedError()
1150
 
    
1151
 
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1152
 
                         signature = "oa{sa{sv}}")
1153
 
    def InterfacesAdded(self, object_path, interfaces_and_properties):
1154
 
        pass
1155
 
    
1156
 
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
1157
 
    def InterfacesRemoved(self, object_path, interfaces):
1158
 
        pass
1159
 
    
1160
 
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1161
 
                         out_signature = "s",
1162
 
                         path_keyword = 'object_path',
1163
 
                         connection_keyword = 'connection')
1164
 
    def Introspect(self, object_path, connection):
1165
 
        """Overloading of standard D-Bus method.
1166
 
        
1167
 
        Override return argument name of GetManagedObjects to be
1168
 
        "objpath_interfaces_and_properties"
1169
 
        """
1170
 
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
1171
 
                                                         object_path,
1172
 
                                                         connection)
1173
 
        try:
1174
 
            document = xml.dom.minidom.parseString(xmlstring)
1175
 
            
1176
 
            for if_tag in document.getElementsByTagName("interface"):
1177
 
                # Fix argument name for the GetManagedObjects method
1178
 
                if (if_tag.getAttribute("name")
1179
 
                                == dbus.OBJECT_MANAGER_IFACE):
1180
 
                    for cn in if_tag.getElementsByTagName("method"):
1181
 
                        if (cn.getAttribute("name")
1182
 
                            == "GetManagedObjects"):
1183
 
                            for arg in cn.getElementsByTagName("arg"):
1184
 
                                if (arg.getAttribute("direction")
1185
 
                                    == "out"):
1186
 
                                    arg.setAttribute(
1187
 
                                        "name",
1188
 
                                        "objpath_interfaces"
1189
 
                                        "_and_properties")
1190
 
            xmlstring = document.toxml("utf-8")
1191
 
            document.unlink()
1192
 
        except (AttributeError, xml.dom.DOMException,
1193
 
                xml.parsers.expat.ExpatError) as error:
1194
 
            logger.error("Failed to override Introspection method",
1195
 
                         exc_info = error)
1196
 
        return xmlstring
1197
1061
 
1198
1062
def datetime_to_dbus(dt, variant_level=0):
1199
1063
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1290
1154
                        func1 and func2 to the "call_both" function
1291
1155
                        outside of its arguments"""
1292
1156
                        
1293
 
                        @functools.wraps(func2)
1294
1157
                        def call_both(*args, **kwargs):
1295
1158
                            """This function will emit two D-Bus
1296
1159
                            signals by calling func1 and func2"""
1297
1160
                            func1(*args, **kwargs)
1298
1161
                            func2(*args, **kwargs)
1299
 
                        # Make wrapper function look like a D-Bus signal
1300
 
                        for name, attr in inspect.getmembers(func2):
1301
 
                            if name.startswith("_dbus_"):
1302
 
                                setattr(call_both, name, attr)
1303
1162
                        
1304
1163
                        return call_both
1305
1164
                    # Create the "call_both" function and add it to
1517
1376
        if exitstatus >= 0:
1518
1377
            # Emit D-Bus signal
1519
1378
            self.CheckerCompleted(dbus.Int16(exitstatus),
1520
 
                                  # This is specific to GNU libC
1521
 
                                  dbus.Int64(exitstatus << 8),
 
1379
                                  dbus.Int64(0),
1522
1380
                                  dbus.String(command))
1523
1381
        else:
1524
1382
            # Emit D-Bus signal
1525
1383
            self.CheckerCompleted(dbus.Int16(-1),
1526
1384
                                  dbus.Int64(
1527
 
                                      # This is specific to GNU libC
1528
 
                                      (exitstatus << 8)
1529
 
                                      | self.last_checker_signal),
 
1385
                                      self.last_checker_signal),
1530
1386
                                  dbus.String(command))
1531
1387
        return ret
1532
1388
    
1609
1465
        self.checked_ok()
1610
1466
    
1611
1467
    # Enable - method
1612
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1613
1468
    @dbus.service.method(_interface)
1614
1469
    def Enable(self):
1615
1470
        "D-Bus method"
1616
1471
        self.enable()
1617
1472
    
1618
1473
    # StartChecker - method
1619
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1620
1474
    @dbus.service.method(_interface)
1621
1475
    def StartChecker(self):
1622
1476
        "D-Bus method"
1623
1477
        self.start_checker()
1624
1478
    
1625
1479
    # Disable - method
1626
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1627
1480
    @dbus.service.method(_interface)
1628
1481
    def Disable(self):
1629
1482
        "D-Bus method"
1630
1483
        self.disable()
1631
1484
    
1632
1485
    # StopChecker - method
1633
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1634
1486
    @dbus.service.method(_interface)
1635
1487
    def StopChecker(self):
1636
1488
        self.stop_checker()
1818
1670
    
1819
1671
    # ObjectPath - property
1820
1672
    @dbus_annotations(
1821
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const",
1822
 
         "org.freedesktop.DBus.Deprecated": "true"})
 
1673
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1823
1674
    @dbus_service_property(_interface, signature="o", access="read")
1824
1675
    def ObjectPath_dbus_property(self):
1825
1676
        return self.dbus_object_path # is already a dbus.ObjectPath
1880
1731
                         self.server.child_pipe.fileno())
1881
1732
            
1882
1733
            session = gnutls.connection.ClientSession(
1883
 
                self.request, gnutls.connection.X509Credentials())
 
1734
                self.request, gnutls.connection .X509Credentials())
1884
1735
            
1885
1736
            # Note: gnutls.connection.X509Credentials is really a
1886
1737
            # generic GnuTLS certificate credentials object so long as
2894
2745
        
2895
2746
        @alternate_dbus_interfaces(
2896
2747
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
2897
 
        class MandosDBusService(DBusObjectWithObjectManager):
 
2748
        class MandosDBusService(DBusObjectWithProperties):
2898
2749
            """A D-Bus proxy object"""
2899
2750
            
2900
2751
            def __init__(self):
2902
2753
            
2903
2754
            _interface = "se.recompile.Mandos"
2904
2755
            
 
2756
            @dbus_interface_annotations(_interface)
 
2757
            def _foo(self):
 
2758
                return {
 
2759
                    "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
2760
                    "false" }
 
2761
            
2905
2762
            @dbus.service.signal(_interface, signature="o")
2906
2763
            def ClientAdded(self, objpath):
2907
2764
                "D-Bus signal"
2912
2769
                "D-Bus signal"
2913
2770
                pass
2914
2771
            
2915
 
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
2916
 
                               "true"})
2917
2772
            @dbus.service.signal(_interface, signature="os")
2918
2773
            def ClientRemoved(self, objpath, name):
2919
2774
                "D-Bus signal"
2920
2775
                pass
2921
2776
            
2922
 
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
2923
 
                               "true"})
2924
2777
            @dbus.service.method(_interface, out_signature="ao")
2925
2778
            def GetAllClients(self):
2926
2779
                "D-Bus method"
2927
2780
                return dbus.Array(c.dbus_object_path for c in
2928
2781
                                  tcp_server.clients.itervalues())
2929
2782
            
2930
 
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
2931
 
                               "true"})
2932
2783
            @dbus.service.method(_interface,
2933
2784
                                 out_signature="a{oa{sv}}")
2934
2785
            def GetAllClientsWithProperties(self):
2935
2786
                "D-Bus method"
2936
2787
                return dbus.Dictionary(
2937
 
                    { c.dbus_object_path: c.GetAll(
2938
 
                        "se.recompile.Mandos.Client")
 
2788
                    { c.dbus_object_path: c.GetAll("")
2939
2789
                      for c in tcp_server.clients.itervalues() },
2940
2790
                    signature="oa{sv}")
2941
2791
            
2946
2796
                    if c.dbus_object_path == object_path:
2947
2797
                        del tcp_server.clients[c.name]
2948
2798
                        c.remove_from_connection()
2949
 
                        # Don't signal the disabling
 
2799
                        # Don't signal anything except ClientRemoved
2950
2800
                        c.disable(quiet=True)
2951
 
                        # Emit D-Bus signal for removal
2952
 
                        self.client_removed_signal(c)
 
2801
                        # Emit D-Bus signal
 
2802
                        self.ClientRemoved(object_path, c.name)
2953
2803
                        return
2954
2804
                raise KeyError(object_path)
2955
2805
            
2956
2806
            del _interface
2957
 
            
2958
 
            @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
2959
 
                                 out_signature = "a{oa{sa{sv}}}")
2960
 
            def GetManagedObjects(self):
2961
 
                """D-Bus method"""
2962
 
                return dbus.Dictionary(
2963
 
                    { client.dbus_object_path:
2964
 
                      dbus.Dictionary(
2965
 
                          { interface: client.GetAll(interface)
2966
 
                            for interface in
2967
 
                                 client._get_all_interface_names()})
2968
 
                      for client in tcp_server.clients.values()})
2969
 
            
2970
 
            def client_added_signal(self, client):
2971
 
                """Send the new standard signal and the old signal"""
2972
 
                if use_dbus:
2973
 
                    # New standard signal
2974
 
                    self.InterfacesAdded(
2975
 
                        client.dbus_object_path,
2976
 
                        dbus.Dictionary(
2977
 
                            { interface: client.GetAll(interface)
2978
 
                              for interface in
2979
 
                              client._get_all_interface_names()}))
2980
 
                    # Old signal
2981
 
                    self.ClientAdded(client.dbus_object_path)
2982
 
            
2983
 
            def client_removed_signal(self, client):
2984
 
                """Send the new standard signal and the old signal"""
2985
 
                if use_dbus:
2986
 
                    # New standard signal
2987
 
                    self.InterfacesRemoved(
2988
 
                        client.dbus_object_path,
2989
 
                        client._get_all_interface_names())
2990
 
                    # Old signal
2991
 
                    self.ClientRemoved(client.dbus_object_path,
2992
 
                                       client.name)
2993
2807
        
2994
2808
        mandos_dbus_service = MandosDBusService()
2995
2809
    
3060
2874
            name, client = tcp_server.clients.popitem()
3061
2875
            if use_dbus:
3062
2876
                client.remove_from_connection()
3063
 
            # Don't signal the disabling
 
2877
            # Don't signal anything except ClientRemoved
3064
2878
            client.disable(quiet=True)
3065
 
            # Emit D-Bus signal for removal
3066
2879
            if use_dbus:
3067
 
                mandos_dbus_service.client_removed_signal(client)
 
2880
                # Emit D-Bus signal
 
2881
                mandos_dbus_service.ClientRemoved(
 
2882
                    client.dbus_object_path, client.name)
3068
2883
        client_settings.clear()
3069
2884
    
3070
2885
    atexit.register(cleanup)
3071
2886
    
3072
2887
    for client in tcp_server.clients.itervalues():
3073
2888
        if use_dbus:
3074
 
            # Emit D-Bus signal for adding
3075
 
            mandos_dbus_service.client_added_signal(client)
 
2889
            # Emit D-Bus signal
 
2890
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
3076
2891
        # Need to initiate checking of clients
3077
2892
        if client.enabled:
3078
2893
            client.init_checker()