/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: 2012-05-03 19:06:02 UTC
  • Revision ID: teddy@recompile.se-20120503190602-uqghef5rbpqdvybx
* mandos-monitor: Use new string format method.

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
68
69
 
69
70
import dbus
70
71
import dbus.service
175
176
    
176
177
    def encrypt(self, data, password):
177
178
        self.gnupg.passphrase = self.password_encode(password)
178
 
        with open(os.devnull) as devnull:
 
179
        with open(os.devnull, "w") as devnull:
179
180
            try:
180
181
                proc = self.gnupg.run(['--symmetric'],
181
182
                                      create_fhs=['stdin', 'stdout'],
192
193
    
193
194
    def decrypt(self, data, password):
194
195
        self.gnupg.passphrase = self.password_encode(password)
195
 
        with open(os.devnull) as devnull:
 
196
        with open(os.devnull, "w") as devnull:
196
197
            try:
197
198
                proc = self.gnupg.run(['--decrypt'],
198
199
                                      create_fhs=['stdin', 'stdout'],
199
200
                                      attach_fhs={'stderr': devnull})
200
 
                with contextlib.closing(proc.handles['stdin'] ) as f:
 
201
                with contextlib.closing(proc.handles['stdin']) as f:
201
202
                    f.write(data)
202
203
                with contextlib.closing(proc.handles['stdout']) as f:
203
204
                    decrypted_plaintext = f.read()
276
277
        try:
277
278
            self.add()
278
279
        except dbus.exceptions.DBusException as error:
279
 
            logger.critical("DBusException: %s", error)
 
280
            logger.critical("D-Bus Exception", exc_info=error)
280
281
            self.cleanup()
281
282
            os._exit(1)
282
283
        self.rename_count += 1
321
322
        elif state == avahi.ENTRY_GROUP_FAILURE:
322
323
            logger.critical("Avahi: Error in group state changed %s",
323
324
                            unicode(error))
324
 
            raise AvahiGroupError("State changed: %s"
325
 
                                  % unicode(error))
 
325
            raise AvahiGroupError("State changed: {0!s}"
 
326
                                  .format(error))
326
327
    def cleanup(self):
327
328
        """Derived from the Avahi example code"""
328
329
        if self.group is not None:
374
375
        """Add the new name to the syslog messages"""
375
376
        ret = AvahiService.rename(self)
376
377
        syslogger.setFormatter(logging.Formatter
377
 
                               ('Mandos (%s) [%%(process)d]:'
378
 
                                ' %%(levelname)s: %%(message)s'
379
 
                                % self.name))
 
378
                               ('Mandos ({0}) [%(process)d]:'
 
379
                                ' %(levelname)s: %(message)s'
 
380
                                .format(self.name)))
380
381
        return ret
381
382
 
382
383
def timedelta_to_milliseconds(td):
488
489
                          "rb") as secfile:
489
490
                    client["secret"] = secfile.read()
490
491
            else:
491
 
                raise TypeError("No secret or secfile for section %s"
492
 
                                % section)
 
492
                raise TypeError("No secret or secfile for section {0}"
 
493
                                .format(section))
493
494
            client["timeout"] = string_to_delta(section["timeout"])
494
495
            client["extended_timeout"] = string_to_delta(
495
496
                section["extended_timeout"])
692
693
                try:
693
694
                    command = self.checker_command % escaped_attrs
694
695
                except TypeError as error:
695
 
                    logger.error('Could not format string "%s":'
696
 
                                 ' %s', self.checker_command, error)
 
696
                    logger.error('Could not format string "%s"',
 
697
                                 self.checker_command, exc_info=error)
697
698
                    return True # Try again later
698
699
            self.current_checker_command = command
699
700
            try:
717
718
                    gobject.source_remove(self.checker_callback_tag)
718
719
                    self.checker_callback(pid, status, command)
719
720
            except OSError as error:
720
 
                logger.error("Failed to start subprocess: %s",
721
 
                             error)
 
721
                logger.error("Failed to start subprocess",
 
722
                             exc_info=error)
722
723
        # Re-run this periodically if run by gobject.timeout_add
723
724
        return True
724
725
    
731
732
            return
732
733
        logger.debug("Stopping checker for %(name)s", vars(self))
733
734
        try:
734
 
            os.kill(self.checker.pid, signal.SIGTERM)
 
735
            self.checker.terminate()
735
736
            #time.sleep(0.5)
736
737
            #if self.checker.poll() is None:
737
 
            #    os.kill(self.checker.pid, signal.SIGKILL)
 
738
            #    self.checker.kill()
738
739
        except OSError as error:
739
740
            if error.errno != errno.ESRCH: # No such process
740
741
                raise
757
758
    # "Set" method, so we fail early here:
758
759
    if byte_arrays and signature != "ay":
759
760
        raise ValueError("Byte arrays not supported for non-'ay'"
760
 
                         " signature %r" % signature)
 
761
                         " signature {0!r}".format(signature))
761
762
    def decorator(func):
762
763
        func._dbus_is_property = True
763
764
        func._dbus_interface = dbus_interface
771
772
    return decorator
772
773
 
773
774
 
 
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
 
774
812
class DBusPropertyException(dbus.exceptions.DBusException):
775
813
    """A base class for D-Bus property-related exceptions
776
814
    """
799
837
    """
800
838
    
801
839
    @staticmethod
802
 
    def _is_dbus_property(obj):
803
 
        return getattr(obj, "_dbus_is_property", False)
 
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)
804
848
    
805
 
    def _get_all_dbus_properties(self):
 
849
    def _get_all_dbus_things(self, thing):
806
850
        """Returns a generator of (name, attribute) pairs
807
851
        """
808
 
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
 
852
        return ((getattr(athing.__get__(self), "_dbus_name",
 
853
                         name),
 
854
                 athing.__get__(self))
809
855
                for cls in self.__class__.__mro__
810
 
                for name, prop in
811
 
                inspect.getmembers(cls, self._is_dbus_property))
 
856
                for name, athing in
 
857
                inspect.getmembers(cls,
 
858
                                   self._is_dbus_thing(thing)))
812
859
    
813
860
    def _get_dbus_property(self, interface_name, property_name):
814
861
        """Returns a bound method if one exists which is a D-Bus
816
863
        """
817
864
        for cls in  self.__class__.__mro__:
818
865
            for name, value in (inspect.getmembers
819
 
                                (cls, self._is_dbus_property)):
 
866
                                (cls,
 
867
                                 self._is_dbus_thing("property"))):
820
868
                if (value._dbus_name == property_name
821
869
                    and value._dbus_interface == interface_name):
822
870
                    return value.__get__(self)
864
912
        Note: Will not include properties with access="write".
865
913
        """
866
914
        properties = {}
867
 
        for name, prop in self._get_all_dbus_properties():
 
915
        for name, prop in self._get_all_dbus_things("property"):
868
916
            if (interface_name
869
917
                and interface_name != prop._dbus_interface):
870
918
                # Interface non-empty but did not match
885
933
                         path_keyword='object_path',
886
934
                         connection_keyword='connection')
887
935
    def Introspect(self, object_path, connection):
888
 
        """Standard D-Bus method, overloaded to insert property tags.
 
936
        """Overloading of standard D-Bus method.
 
937
        
 
938
        Inserts property tags and interface annotation tags.
889
939
        """
890
940
        xmlstring = dbus.service.Object.Introspect(self, object_path,
891
941
                                                   connection)
898
948
                e.setAttribute("access", prop._dbus_access)
899
949
                return e
900
950
            for if_tag in document.getElementsByTagName("interface"):
 
951
                # Add property tags
901
952
                for tag in (make_tag(document, name, prop)
902
953
                            for name, prop
903
 
                            in self._get_all_dbus_properties()
 
954
                            in self._get_all_dbus_things("property")
904
955
                            if prop._dbus_interface
905
956
                            == if_tag.getAttribute("name")):
906
957
                    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)
907
989
                # Add the names to the return values for the
908
990
                # "org.freedesktop.DBus.Properties" methods
909
991
                if (if_tag.getAttribute("name")
924
1006
        except (AttributeError, xml.dom.DOMException,
925
1007
                xml.parsers.expat.ExpatError) as error:
926
1008
            logger.error("Failed to override Introspection method",
927
 
                         error)
 
1009
                         exc_info=error)
928
1010
        return xmlstring
929
1011
 
930
1012
 
944
1026
    def __new__(mcs, name, bases, attr):
945
1027
        # Go through all the base classes which could have D-Bus
946
1028
        # methods, signals, or properties in them
 
1029
        old_interface_names = []
947
1030
        for base in (b for b in bases
948
1031
                     if issubclass(b, dbus.service.Object)):
949
1032
            # Go though all attributes of the base class
959
1042
                alt_interface = (attribute._dbus_interface
960
1043
                                 .replace("se.recompile.Mandos",
961
1044
                                          "se.bsnet.fukt.Mandos"))
 
1045
                if alt_interface != attribute._dbus_interface:
 
1046
                    old_interface_names.append(alt_interface)
962
1047
                # Is this a D-Bus signal?
963
1048
                if getattr(attribute, "_dbus_is_signal", False):
964
1049
                    # Extract the original non-method function by
979
1064
                                nonmethod_func.func_name,
980
1065
                                nonmethod_func.func_defaults,
981
1066
                                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
982
1073
                    # Define a creator of a function to call both the
983
1074
                    # old and new functions, so both the old and new
984
1075
                    # signals gets sent when the function is called
1012
1103
                                        attribute.func_name,
1013
1104
                                        attribute.func_defaults,
1014
1105
                                        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
1015
1112
                # Is this a D-Bus property?
1016
1113
                elif getattr(attribute, "_dbus_is_property", False):
1017
1114
                    # Create a new, but exactly alike, function
1031
1128
                                        attribute.func_name,
1032
1129
                                        attribute.func_defaults,
1033
1130
                                        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
1034
1163
        return type.__new__(mcs, name, bases, attr)
1035
1164
 
1036
1165
 
1186
1315
    ## D-Bus methods, signals & properties
1187
1316
    _interface = "se.recompile.Mandos.Client"
1188
1317
    
 
1318
    ## Interfaces
 
1319
    
 
1320
    @dbus_interface_annotations(_interface)
 
1321
    def _foo(self):
 
1322
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
1323
                     "false"}
 
1324
    
1189
1325
    ## Signals
1190
1326
    
1191
1327
    # CheckerCompleted - signal
1943
2079
            elif suffix == "w":
1944
2080
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1945
2081
            else:
1946
 
                raise ValueError("Unknown suffix %r" % suffix)
 
2082
                raise ValueError("Unknown suffix {0!r}"
 
2083
                                 .format(suffix))
1947
2084
        except (ValueError, IndexError) as e:
1948
2085
            raise ValueError(*(e.args))
1949
2086
        timevalue += delta
1963
2100
        sys.exit()
1964
2101
    if not noclose:
1965
2102
        # Close all standard open file descriptors
1966
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
2103
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
1967
2104
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1968
2105
            raise OSError(errno.ENODEV,
1969
 
                          "%s not a character device"
1970
 
                          % os.path.devnull)
 
2106
                          "{0} not a character device"
 
2107
                          .format(os.devnull))
1971
2108
        os.dup2(null, sys.stdin.fileno())
1972
2109
        os.dup2(null, sys.stdout.fileno())
1973
2110
        os.dup2(null, sys.stderr.fileno())
1982
2119
    
1983
2120
    parser = argparse.ArgumentParser()
1984
2121
    parser.add_argument("-v", "--version", action="version",
1985
 
                        version = "%%(prog)s %s" % version,
 
2122
                        version = "%(prog)s {0}".format(version),
1986
2123
                        help="show version number and exit")
1987
2124
    parser.add_argument("-i", "--interface", metavar="IF",
1988
2125
                        help="Bind to interface IF")
2091
2228
    
2092
2229
    if server_settings["servicename"] != "Mandos":
2093
2230
        syslogger.setFormatter(logging.Formatter
2094
 
                               ('Mandos (%s) [%%(process)d]:'
2095
 
                                ' %%(levelname)s: %%(message)s'
2096
 
                                % server_settings["servicename"]))
 
2231
                               ('Mandos ({0}) [%(process)d]:'
 
2232
                                ' %(levelname)s: %(message)s'
 
2233
                                .format(server_settings
 
2234
                                        ["servicename"])))
2097
2235
    
2098
2236
    # Parse config file with clients
2099
2237
    client_config = configparser.SafeConfigParser(Client
2120
2258
        except IOError:
2121
2259
            logger.error("Could not open file %r", pidfilename)
2122
2260
    
2123
 
    try:
2124
 
        uid = pwd.getpwnam("_mandos").pw_uid
2125
 
        gid = pwd.getpwnam("_mandos").pw_gid
2126
 
    except KeyError:
 
2261
    for name in ("_mandos", "mandos", "nobody"):
2127
2262
        try:
2128
 
            uid = pwd.getpwnam("mandos").pw_uid
2129
 
            gid = pwd.getpwnam("mandos").pw_gid
 
2263
            uid = pwd.getpwnam(name).pw_uid
 
2264
            gid = pwd.getpwnam(name).pw_gid
 
2265
            break
2130
2266
        except KeyError:
2131
 
            try:
2132
 
                uid = pwd.getpwnam("nobody").pw_uid
2133
 
                gid = pwd.getpwnam("nobody").pw_gid
2134
 
            except KeyError:
2135
 
                uid = 65534
2136
 
                gid = 65534
 
2267
            continue
 
2268
    else:
 
2269
        uid = 65534
 
2270
        gid = 65534
2137
2271
    try:
2138
2272
        os.setgid(gid)
2139
2273
        os.setuid(uid)
2156
2290
         .gnutls_global_set_log_function(debug_gnutls))
2157
2291
        
2158
2292
        # Redirect stdin so all checkers get /dev/null
2159
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
2293
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
2160
2294
        os.dup2(null, sys.stdin.fileno())
2161
2295
        if null > 2:
2162
2296
            os.close(null)
2170
2304
    
2171
2305
    global main_loop
2172
2306
    # From the Avahi example code
2173
 
    DBusGMainLoop(set_as_default=True )
 
2307
    DBusGMainLoop(set_as_default=True)
2174
2308
    main_loop = gobject.MainLoop()
2175
2309
    bus = dbus.SystemBus()
2176
2310
    # End of Avahi example code
2317
2451
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2318
2452
    
2319
2453
    if use_dbus:
2320
 
        class MandosDBusService(dbus.service.Object):
 
2454
        class MandosDBusService(DBusObjectWithProperties):
2321
2455
            """A D-Bus proxy object"""
2322
2456
            def __init__(self):
2323
2457
                dbus.service.Object.__init__(self, bus, "/")
2324
2458
            _interface = "se.recompile.Mandos"
2325
2459
            
 
2460
            @dbus_interface_annotations(_interface)
 
2461
            def _foo(self):
 
2462
                return { "org.freedesktop.DBus.Property"
 
2463
                         ".EmitsChangedSignal":
 
2464
                             "false"}
 
2465
            
2326
2466
            @dbus.service.signal(_interface, signature="o")
2327
2467
            def ClientAdded(self, objpath):
2328
2468
                "D-Bus signal"
2461
2601
    service.port = tcp_server.socket.getsockname()[1]
2462
2602
    if use_ipv6:
2463
2603
        logger.info("Now listening on address %r, port %d,"
2464
 
                    " flowinfo %d, scope_id %d"
2465
 
                    % tcp_server.socket.getsockname())
 
2604
                    " flowinfo %d, scope_id %d",
 
2605
                    *tcp_server.socket.getsockname())
2466
2606
    else:                       # IPv4
2467
 
        logger.info("Now listening on address %r, port %d"
2468
 
                    % tcp_server.socket.getsockname())
 
2607
        logger.info("Now listening on address %r, port %d",
 
2608
                    *tcp_server.socket.getsockname())
2469
2609
    
2470
2610
    #service.interface = tcp_server.socket.getsockname()[3]
2471
2611
    
2474
2614
        try:
2475
2615
            service.activate()
2476
2616
        except dbus.exceptions.DBusException as error:
2477
 
            logger.critical("DBusException: %s", error)
 
2617
            logger.critical("D-Bus Exception", exc_info=error)
2478
2618
            cleanup()
2479
2619
            sys.exit(1)
2480
2620
        # End of Avahi example code
2487
2627
        logger.debug("Starting main loop")
2488
2628
        main_loop.run()
2489
2629
    except AvahiError as error:
2490
 
        logger.critical("AvahiError: %s", error)
 
2630
        logger.critical("Avahi Error", exc_info=error)
2491
2631
        cleanup()
2492
2632
        sys.exit(1)
2493
2633
    except KeyboardInterrupt: