/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2012-01-08 10:19:33 UTC
  • Revision ID: teddy@recompile.se-20120108101933-xbtodqgepxax8hn1
Tags: version-1.5.2-1
* Makefile (version): Changed to "1.5.2".
* NEWS (Version 1.5.2): New entry.
* debian/changelog (1.5.2-1): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
import types
66
66
import binascii
67
67
import tempfile
68
 
import itertools
69
68
 
70
69
import dbus
71
70
import dbus.service
86
85
    except ImportError:
87
86
        SO_BINDTODEVICE = None
88
87
 
89
 
version = "1.5.3"
 
88
version = "1.5.2"
90
89
stored_state_file = "clients.pickle"
91
90
 
92
91
logger = logging.getLogger()
176
175
    
177
176
    def encrypt(self, data, password):
178
177
        self.gnupg.passphrase = self.password_encode(password)
179
 
        with open(os.devnull, "w") as devnull:
 
178
        with open(os.devnull) as devnull:
180
179
            try:
181
180
                proc = self.gnupg.run(['--symmetric'],
182
181
                                      create_fhs=['stdin', 'stdout'],
193
192
    
194
193
    def decrypt(self, data, password):
195
194
        self.gnupg.passphrase = self.password_encode(password)
196
 
        with open(os.devnull, "w") as devnull:
 
195
        with open(os.devnull) as devnull:
197
196
            try:
198
197
                proc = self.gnupg.run(['--decrypt'],
199
198
                                      create_fhs=['stdin', 'stdout'],
200
199
                                      attach_fhs={'stderr': devnull})
201
 
                with contextlib.closing(proc.handles['stdin']) as f:
 
200
                with contextlib.closing(proc.handles['stdin'] ) as f:
202
201
                    f.write(data)
203
202
                with contextlib.closing(proc.handles['stdout']) as f:
204
203
                    decrypted_plaintext = f.read()
277
276
        try:
278
277
            self.add()
279
278
        except dbus.exceptions.DBusException as error:
280
 
            logger.critical("D-Bus Exception", exc_info=error)
 
279
            logger.critical("DBusException: %s", error)
281
280
            self.cleanup()
282
281
            os._exit(1)
283
282
        self.rename_count += 1
416
415
    last_checked_ok: datetime.datetime(); (UTC) or None
417
416
    last_checker_status: integer between 0 and 255 reflecting exit
418
417
                         status of last checker. -1 reflects crashed
419
 
                         checker, -2 means no checker completed yet.
 
418
                         checker, or None.
420
419
    last_enabled: datetime.datetime(); (UTC) or None
421
420
    name:       string; from the config file, used in log messages and
422
421
                        D-Bus identifiers
502
501
            client["checker_command"] = section["checker"]
503
502
            client["last_approval_request"] = None
504
503
            client["last_checked_ok"] = None
505
 
            client["last_checker_status"] = -2
 
504
            client["last_checker_status"] = None
506
505
        
507
506
        return settings
508
507
        
627
626
            logger.warning("Checker for %(name)s crashed?",
628
627
                           vars(self))
629
628
    
630
 
    def checked_ok(self):
631
 
        """Assert that the client has been seen, alive and well."""
632
 
        self.last_checked_ok = datetime.datetime.utcnow()
633
 
        self.last_checker_status = 0
634
 
        self.bump_timeout()
635
 
    
636
 
    def bump_timeout(self, timeout=None):
637
 
        """Bump up the timeout for this client."""
 
629
    def checked_ok(self, timeout=None):
 
630
        """Bump up the timeout for this client.
 
631
        
 
632
        This should only be called when the client has been seen,
 
633
        alive and well.
 
634
        """
638
635
        if timeout is None:
639
636
            timeout = self.timeout
 
637
        self.last_checked_ok = datetime.datetime.utcnow()
640
638
        if self.disable_initiator_tag is not None:
641
639
            gobject.source_remove(self.disable_initiator_tag)
642
640
        if getattr(self, "enabled", False):
693
691
                try:
694
692
                    command = self.checker_command % escaped_attrs
695
693
                except TypeError as error:
696
 
                    logger.error('Could not format string "%s"',
697
 
                                 self.checker_command, exc_info=error)
 
694
                    logger.error('Could not format string "%s":'
 
695
                                 ' %s', self.checker_command, error)
698
696
                    return True # Try again later
699
697
            self.current_checker_command = command
700
698
            try:
718
716
                    gobject.source_remove(self.checker_callback_tag)
719
717
                    self.checker_callback(pid, status, command)
720
718
            except OSError as error:
721
 
                logger.error("Failed to start subprocess",
722
 
                             exc_info=error)
 
719
                logger.error("Failed to start subprocess: %s",
 
720
                             error)
723
721
        # Re-run this periodically if run by gobject.timeout_add
724
722
        return True
725
723
    
732
730
            return
733
731
        logger.debug("Stopping checker for %(name)s", vars(self))
734
732
        try:
735
 
            self.checker.terminate()
 
733
            os.kill(self.checker.pid, signal.SIGTERM)
736
734
            #time.sleep(0.5)
737
735
            #if self.checker.poll() is None:
738
 
            #    self.checker.kill()
 
736
            #    os.kill(self.checker.pid, signal.SIGKILL)
739
737
        except OSError as error:
740
738
            if error.errno != errno.ESRCH: # No such process
741
739
                raise
772
770
    return decorator
773
771
 
774
772
 
775
 
def dbus_interface_annotations(dbus_interface):
776
 
    """Decorator for marking functions returning interface annotations.
777
 
    
778
 
    Usage:
779
 
    
780
 
    @dbus_interface_annotations("org.example.Interface")
781
 
    def _foo(self):  # Function name does not matter
782
 
        return {"org.freedesktop.DBus.Deprecated": "true",
783
 
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
784
 
                    "false"}
785
 
    """
786
 
    def decorator(func):
787
 
        func._dbus_is_interface = True
788
 
        func._dbus_interface = dbus_interface
789
 
        func._dbus_name = dbus_interface
790
 
        return func
791
 
    return decorator
792
 
 
793
 
 
794
 
def dbus_annotations(annotations):
795
 
    """Decorator to annotate D-Bus methods, signals or properties
796
 
    Usage:
797
 
    
798
 
    @dbus_service_property("org.example.Interface", signature="b",
799
 
                           access="r")
800
 
    @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
801
 
                        "org.freedesktop.DBus.Property."
802
 
                        "EmitsChangedSignal": "false"})
803
 
    def Property_dbus_property(self):
804
 
        return dbus.Boolean(False)
805
 
    """
806
 
    def decorator(func):
807
 
        func._dbus_annotations = annotations
808
 
        return func
809
 
    return decorator
810
 
 
811
 
 
812
773
class DBusPropertyException(dbus.exceptions.DBusException):
813
774
    """A base class for D-Bus property-related exceptions
814
775
    """
837
798
    """
838
799
    
839
800
    @staticmethod
840
 
    def _is_dbus_thing(thing):
841
 
        """Returns a function testing if an attribute is a D-Bus thing
842
 
        
843
 
        If called like _is_dbus_thing("method") it returns a function
844
 
        suitable for use as predicate to inspect.getmembers().
845
 
        """
846
 
        return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
847
 
                                   False)
 
801
    def _is_dbus_property(obj):
 
802
        return getattr(obj, "_dbus_is_property", False)
848
803
    
849
 
    def _get_all_dbus_things(self, thing):
 
804
    def _get_all_dbus_properties(self):
850
805
        """Returns a generator of (name, attribute) pairs
851
806
        """
852
 
        return ((getattr(athing.__get__(self), "_dbus_name",
853
 
                         name),
854
 
                 athing.__get__(self))
 
807
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
855
808
                for cls in self.__class__.__mro__
856
 
                for name, athing in
857
 
                inspect.getmembers(cls,
858
 
                                   self._is_dbus_thing(thing)))
 
809
                for name, prop in
 
810
                inspect.getmembers(cls, self._is_dbus_property))
859
811
    
860
812
    def _get_dbus_property(self, interface_name, property_name):
861
813
        """Returns a bound method if one exists which is a D-Bus
863
815
        """
864
816
        for cls in  self.__class__.__mro__:
865
817
            for name, value in (inspect.getmembers
866
 
                                (cls,
867
 
                                 self._is_dbus_thing("property"))):
 
818
                                (cls, self._is_dbus_property)):
868
819
                if (value._dbus_name == property_name
869
820
                    and value._dbus_interface == interface_name):
870
821
                    return value.__get__(self)
912
863
        Note: Will not include properties with access="write".
913
864
        """
914
865
        properties = {}
915
 
        for name, prop in self._get_all_dbus_things("property"):
 
866
        for name, prop in self._get_all_dbus_properties():
916
867
            if (interface_name
917
868
                and interface_name != prop._dbus_interface):
918
869
                # Interface non-empty but did not match
933
884
                         path_keyword='object_path',
934
885
                         connection_keyword='connection')
935
886
    def Introspect(self, object_path, connection):
936
 
        """Overloading of standard D-Bus method.
937
 
        
938
 
        Inserts property tags and interface annotation tags.
 
887
        """Standard D-Bus method, overloaded to insert property tags.
939
888
        """
940
889
        xmlstring = dbus.service.Object.Introspect(self, object_path,
941
890
                                                   connection)
948
897
                e.setAttribute("access", prop._dbus_access)
949
898
                return e
950
899
            for if_tag in document.getElementsByTagName("interface"):
951
 
                # Add property tags
952
900
                for tag in (make_tag(document, name, prop)
953
901
                            for name, prop
954
 
                            in self._get_all_dbus_things("property")
 
902
                            in self._get_all_dbus_properties()
955
903
                            if prop._dbus_interface
956
904
                            == if_tag.getAttribute("name")):
957
905
                    if_tag.appendChild(tag)
958
 
                # Add annotation tags
959
 
                for typ in ("method", "signal", "property"):
960
 
                    for tag in if_tag.getElementsByTagName(typ):
961
 
                        annots = dict()
962
 
                        for name, prop in (self.
963
 
                                           _get_all_dbus_things(typ)):
964
 
                            if (name == tag.getAttribute("name")
965
 
                                and prop._dbus_interface
966
 
                                == if_tag.getAttribute("name")):
967
 
                                annots.update(getattr
968
 
                                              (prop,
969
 
                                               "_dbus_annotations",
970
 
                                               {}))
971
 
                        for name, value in annots.iteritems():
972
 
                            ann_tag = document.createElement(
973
 
                                "annotation")
974
 
                            ann_tag.setAttribute("name", name)
975
 
                            ann_tag.setAttribute("value", value)
976
 
                            tag.appendChild(ann_tag)
977
 
                # Add interface annotation tags
978
 
                for annotation, value in dict(
979
 
                    itertools.chain(
980
 
                        *(annotations().iteritems()
981
 
                          for name, annotations in
982
 
                          self._get_all_dbus_things("interface")
983
 
                          if name == if_tag.getAttribute("name")
984
 
                          ))).iteritems():
985
 
                    ann_tag = document.createElement("annotation")
986
 
                    ann_tag.setAttribute("name", annotation)
987
 
                    ann_tag.setAttribute("value", value)
988
 
                    if_tag.appendChild(ann_tag)
989
906
                # Add the names to the return values for the
990
907
                # "org.freedesktop.DBus.Properties" methods
991
908
                if (if_tag.getAttribute("name")
1006
923
        except (AttributeError, xml.dom.DOMException,
1007
924
                xml.parsers.expat.ExpatError) as error:
1008
925
            logger.error("Failed to override Introspection method",
1009
 
                         exc_info=error)
 
926
                         error)
1010
927
        return xmlstring
1011
928
 
1012
929
 
1026
943
    def __new__(mcs, name, bases, attr):
1027
944
        # Go through all the base classes which could have D-Bus
1028
945
        # methods, signals, or properties in them
1029
 
        old_interface_names = []
1030
946
        for base in (b for b in bases
1031
947
                     if issubclass(b, dbus.service.Object)):
1032
948
            # Go though all attributes of the base class
1042
958
                alt_interface = (attribute._dbus_interface
1043
959
                                 .replace("se.recompile.Mandos",
1044
960
                                          "se.bsnet.fukt.Mandos"))
1045
 
                if alt_interface != attribute._dbus_interface:
1046
 
                    old_interface_names.append(alt_interface)
1047
961
                # Is this a D-Bus signal?
1048
962
                if getattr(attribute, "_dbus_is_signal", False):
1049
963
                    # Extract the original non-method function by
1064
978
                                nonmethod_func.func_name,
1065
979
                                nonmethod_func.func_defaults,
1066
980
                                nonmethod_func.func_closure)))
1067
 
                    # Copy annotations, if any
1068
 
                    try:
1069
 
                        new_function._dbus_annotations = (
1070
 
                            dict(attribute._dbus_annotations))
1071
 
                    except AttributeError:
1072
 
                        pass
1073
981
                    # Define a creator of a function to call both the
1074
982
                    # old and new functions, so both the old and new
1075
983
                    # signals gets sent when the function is called
1103
1011
                                        attribute.func_name,
1104
1012
                                        attribute.func_defaults,
1105
1013
                                        attribute.func_closure)))
1106
 
                    # Copy annotations, if any
1107
 
                    try:
1108
 
                        attr[attrname]._dbus_annotations = (
1109
 
                            dict(attribute._dbus_annotations))
1110
 
                    except AttributeError:
1111
 
                        pass
1112
1014
                # Is this a D-Bus property?
1113
1015
                elif getattr(attribute, "_dbus_is_property", False):
1114
1016
                    # Create a new, but exactly alike, function
1128
1030
                                        attribute.func_name,
1129
1031
                                        attribute.func_defaults,
1130
1032
                                        attribute.func_closure)))
1131
 
                    # Copy annotations, if any
1132
 
                    try:
1133
 
                        attr[attrname]._dbus_annotations = (
1134
 
                            dict(attribute._dbus_annotations))
1135
 
                    except AttributeError:
1136
 
                        pass
1137
 
                # Is this a D-Bus interface?
1138
 
                elif getattr(attribute, "_dbus_is_interface", False):
1139
 
                    # Create a new, but exactly alike, function
1140
 
                    # object.  Decorate it to be a new D-Bus interface
1141
 
                    # with the alternate D-Bus interface name.  Add it
1142
 
                    # to the class.
1143
 
                    attr[attrname] = (dbus_interface_annotations
1144
 
                                      (alt_interface)
1145
 
                                      (types.FunctionType
1146
 
                                       (attribute.func_code,
1147
 
                                        attribute.func_globals,
1148
 
                                        attribute.func_name,
1149
 
                                        attribute.func_defaults,
1150
 
                                        attribute.func_closure)))
1151
 
        # Deprecate all old interfaces
1152
 
        basename="_AlternateDBusNamesMetaclass_interface_annotation{0}"
1153
 
        for old_interface_name in old_interface_names:
1154
 
            @dbus_interface_annotations(old_interface_name)
1155
 
            def func(self):
1156
 
                return { "org.freedesktop.DBus.Deprecated": "true" }
1157
 
            # Find an unused name
1158
 
            for aname in (basename.format(i) for i in
1159
 
                          itertools.count()):
1160
 
                if aname not in attr:
1161
 
                    attr[aname] = func
1162
 
                    break
1163
1033
        return type.__new__(mcs, name, bases, attr)
1164
1034
 
1165
1035
 
1230
1100
                                       checker is not None)
1231
1101
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1232
1102
                                           "LastCheckedOK")
1233
 
    last_checker_status = notifychangeproperty(dbus.Int16,
1234
 
                                               "LastCheckerStatus")
1235
1103
    last_approval_request = notifychangeproperty(
1236
1104
        datetime_to_dbus, "LastApprovalRequest")
1237
1105
    approved_by_default = notifychangeproperty(dbus.Boolean,
1315
1183
    ## D-Bus methods, signals & properties
1316
1184
    _interface = "se.recompile.Mandos.Client"
1317
1185
    
1318
 
    ## Interfaces
1319
 
    
1320
 
    @dbus_interface_annotations(_interface)
1321
 
    def _foo(self):
1322
 
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
1323
 
                     "false"}
1324
 
    
1325
1186
    ## Signals
1326
1187
    
1327
1188
    # CheckerCompleted - signal
1478
1339
            return
1479
1340
        return datetime_to_dbus(self.last_checked_ok)
1480
1341
    
1481
 
    # LastCheckerStatus - property
1482
 
    @dbus_service_property(_interface, signature="n",
1483
 
                           access="read")
1484
 
    def LastCheckerStatus_dbus_property(self):
1485
 
        return dbus.Int16(self.last_checker_status)
1486
 
    
1487
1342
    # Expires - property
1488
1343
    @dbus_service_property(_interface, signature="s", access="read")
1489
1344
    def Expires_dbus_property(self):
1750
1605
                
1751
1606
                logger.info("Sending secret to %s", client.name)
1752
1607
                # bump the timeout using extended_timeout
1753
 
                client.bump_timeout(client.extended_timeout)
 
1608
                client.checked_ok(client.extended_timeout)
1754
1609
                if self.server.use_dbus:
1755
1610
                    # Emit D-Bus signal
1756
1611
                    client.GotSecret()
2099
1954
        sys.exit()
2100
1955
    if not noclose:
2101
1956
        # Close all standard open file descriptors
2102
 
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
 
1957
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2103
1958
        if not stat.S_ISCHR(os.fstat(null).st_mode):
2104
1959
            raise OSError(errno.ENODEV,
2105
1960
                          "%s not a character device"
2106
 
                          % os.devnull)
 
1961
                          % os.path.devnull)
2107
1962
        os.dup2(null, sys.stdin.fileno())
2108
1963
        os.dup2(null, sys.stdout.fileno())
2109
1964
        os.dup2(null, sys.stderr.fileno())
2292
2147
         .gnutls_global_set_log_function(debug_gnutls))
2293
2148
        
2294
2149
        # Redirect stdin so all checkers get /dev/null
2295
 
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
 
2150
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2296
2151
        os.dup2(null, sys.stdin.fileno())
2297
2152
        if null > 2:
2298
2153
            os.close(null)
2306
2161
    
2307
2162
    global main_loop
2308
2163
    # From the Avahi example code
2309
 
    DBusGMainLoop(set_as_default=True)
 
2164
    DBusGMainLoop(set_as_default=True )
2310
2165
    main_loop = gobject.MainLoop()
2311
2166
    bus = dbus.SystemBus()
2312
2167
    # End of Avahi example code
2453
2308
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2454
2309
    
2455
2310
    if use_dbus:
2456
 
        class MandosDBusService(DBusObjectWithProperties):
 
2311
        class MandosDBusService(dbus.service.Object):
2457
2312
            """A D-Bus proxy object"""
2458
2313
            def __init__(self):
2459
2314
                dbus.service.Object.__init__(self, bus, "/")
2460
2315
            _interface = "se.recompile.Mandos"
2461
2316
            
2462
 
            @dbus_interface_annotations(_interface)
2463
 
            def _foo(self):
2464
 
                return { "org.freedesktop.DBus.Property"
2465
 
                         ".EmitsChangedSignal":
2466
 
                             "false"}
2467
 
            
2468
2317
            @dbus.service.signal(_interface, signature="o")
2469
2318
            def ClientAdded(self, objpath):
2470
2319
                "D-Bus signal"
2616
2465
        try:
2617
2466
            service.activate()
2618
2467
        except dbus.exceptions.DBusException as error:
2619
 
            logger.critical("D-Bus Exception", exc_info=error)
 
2468
            logger.critical("DBusException: %s", error)
2620
2469
            cleanup()
2621
2470
            sys.exit(1)
2622
2471
        # End of Avahi example code
2629
2478
        logger.debug("Starting main loop")
2630
2479
        main_loop.run()
2631
2480
    except AvahiError as error:
2632
 
        logger.critical("Avahi Error", exc_info=error)
 
2481
        logger.critical("AvahiError: %s", error)
2633
2482
        cleanup()
2634
2483
        sys.exit(1)
2635
2484
    except KeyboardInterrupt: