=== modified file 'mandos' --- mandos 2011-09-24 14:11:08 +0000 +++ mandos 2011-10-02 13:45:45 +0000 @@ -62,6 +62,7 @@ import functools import cPickle as pickle import multiprocessing +import types import dbus import dbus.service @@ -313,11 +314,11 @@ "created", "enabled", "fingerprint", "host", "interval", "last_checked_ok", "last_enabled", "name", "timeout") - + def timeout_milliseconds(self): "Return the 'timeout' attribute in milliseconds" return _timedelta_to_milliseconds(self.timeout) - + def extended_timeout_milliseconds(self): "Return the 'extended_timeout' attribute in milliseconds" return _timedelta_to_milliseconds(self.extended_timeout) @@ -325,7 +326,7 @@ def interval_milliseconds(self): "Return the 'interval' attribute in milliseconds" return _timedelta_to_milliseconds(self.interval) - + def approval_delay_milliseconds(self): return _timedelta_to_milliseconds(self.approval_delay) @@ -509,7 +510,7 @@ 'replace'))) for attr in self.runtime_expansions) - + try: command = self.checker_command % escaped_attrs except TypeError as error: @@ -561,6 +562,7 @@ raise self.checker = None + def dbus_service_property(dbus_interface, signature="v", access="readwrite", byte_arrays=False): """Decorators for marking methods of a DBusObjectWithProperties to @@ -612,7 +614,7 @@ class DBusObjectWithProperties(dbus.service.Object): """A D-Bus object with properties. - + Classes inheriting from this can use the dbus_service_property decorator to expose methods as D-Bus properties. It exposes the standard Get(), Set(), and GetAll() methods on the D-Bus. @@ -629,24 +631,40 @@ for name, prop in inspect.getmembers(self, self._is_dbus_property)) +# def _get_dbus_property(self, interface_name, property_name): +# """Returns a bound method if one exists which is a D-Bus +# property with the specified name and interface. +# """ +# print("get_property({0!r}, {1!r}".format(interface_name, property_name),file=sys.stderr) +# print(dir(self), sys.stderr) +# for name in (property_name, +# property_name + "_dbus_property"): +# prop = getattr(self, name, None) +# if (prop is None +# or not self._is_dbus_property(prop) +# or prop._dbus_name != property_name +# or (interface_name and prop._dbus_interface +# and interface_name != prop._dbus_interface)): +# continue +# return prop +# # No such property +# raise DBusPropertyNotFound(self.dbus_object_path + ":" +# + interface_name + "." +# + property_name) + def _get_dbus_property(self, interface_name, property_name): """Returns a bound method if one exists which is a D-Bus property with the specified name and interface. """ - for name in (property_name, - property_name + "_dbus_property"): - prop = getattr(self, name, None) - if (prop is None - or not self._is_dbus_property(prop) - or prop._dbus_name != property_name - or (interface_name and prop._dbus_interface - and interface_name != prop._dbus_interface)): - continue - return prop + for name, value in inspect.getmembers(self, self._is_dbus_property): + if value._dbus_name == property_name and value._dbus_interface == interface_name: + return value + # No such property raise DBusPropertyNotFound(self.dbus_object_path + ":" + interface_name + "." + property_name) + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss", out_signature="v") @@ -682,7 +700,7 @@ def GetAll(self, interface_name): """Standard D-Bus property GetAll() method, see D-Bus standard. - + Note: Will not include properties with access="write". """ all = {} @@ -757,6 +775,60 @@ return dbus.String(dt.isoformat(), variant_level=variant_level) +class transitional_clientdbus(DBusObjectWithProperties.__metaclass__): + def __new__(mcs, name, bases, attr): + for key, old_dbusobj in attr.items(): + new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.") + if getattr(old_dbusobj, "_dbus_is_signal", False): + unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars, + old_dbusobj.__closure__))["func"].cell_contents + newfunc = types.FunctionType(unwrappedfunc.func_code, + unwrappedfunc.func_globals, + unwrappedfunc.func_name, + unwrappedfunc.func_defaults, + unwrappedfunc.func_closure) + new_dbusfunc = dbus.service.signal( + new_interface, old_dbusobj._dbus_signature)(newfunc) + attr["_transitional_{0}_1".format(key)] = new_dbusfunc + attr["_transitional_{0}_0".format(key)] = old_dbusobj + def fixscope(func1, func2): + def newcall(*args, **kwargs): + func1(*args, **kwargs) + func2(*args, **kwargs) + return newcall + + attr[key] = fixscope( + old_dbusobj, attr["_transitional_{0}_1".format(key)]) + + if getattr(old_dbusobj, "_dbus_is_method", False): + new_dbusfunc = (dbus.service.method + (new_interface, + old_dbusobj._dbus_in_signature, + old_dbusobj._dbus_out_signature) + (types.FunctionType + (old_dbusobj.func_code, + old_dbusobj.func_globals, + old_dbusobj.func_name, + old_dbusobj.func_defaults, + old_dbusobj.func_closure))) + + attr["_transitional_{0}".format(key)] = new_dbusfunc + if getattr(old_dbusobj, "_dbus_is_property", False): + new_dbusfunc = (dbus_service_property + (new_interface, + old_dbusobj._dbus_signature, + old_dbusobj._dbus_access, + old_dbusobj._dbus_get_args_options["byte_arrays"]) + (types.FunctionType + (old_dbusobj.func_code, + old_dbusobj.func_globals, + old_dbusobj.func_name, + old_dbusobj.func_defaults, + old_dbusobj.func_closure))) + + attr["_transitional_{0}".format(key)] = new_dbusfunc + return type.__new__(mcs, name, bases, attr) + class ClientDBus(Client, DBusObjectWithProperties): """A Client class using D-Bus @@ -767,6 +839,8 @@ runtime_expansions = (Client.runtime_expansions + ("dbus_object_path",)) + + __metaclass__ = transitional_clientdbus # dbus.service.Object doesn't use super(), so we can't either. @@ -806,10 +880,10 @@ variant_level) self.PropertyChanged(dbus.String(dbus_name), dbus_value) - + return property(lambda self: real_value[0], setter) - - + + expires = notifychangeproperty(datetime_to_dbus, "Expires") approvals_pending = notifychangeproperty(dbus.Boolean, "ApprovalPending", @@ -867,7 +941,7 @@ return Client.checker_callback(self, pid, condition, command, *args, **kwargs) - + def start_checker(self, *args, **kwargs): old_checker = self.checker if self.checker is not None: @@ -896,7 +970,7 @@ ## D-Bus methods, signals & properties _interface = "se.bsnet.fukt.Mandos.Client" - + ## Signals # CheckerCompleted - signal @@ -1090,7 +1164,7 @@ + datetime.timedelta(milliseconds = time_to_die)) self.disable_initiator_tag = (gobject.timeout_add (time_to_die, self.disable)) - + # ExtendedTimeout - property @dbus_service_property(_interface, signature="t", access="readwrite") @@ -1098,7 +1172,7 @@ if value is None: # get return dbus.UInt64(self.extended_timeout_milliseconds()) self.extended_timeout = datetime.timedelta(0, 0, 0, value) - + # Interval - property @dbus_service_property(_interface, signature="t", access="readwrite") @@ -1113,7 +1187,7 @@ self.checker_initiator_tag = (gobject.timeout_add (value, self.start_checker)) self.start_checker() # Start one now, too - + # Checker - property @dbus_service_property(_interface, signature="s", access="readwrite") @@ -1153,7 +1227,7 @@ self._pipe.send(('init', fpr, address)) if not self._pipe.recv(): raise KeyError() - + def __getattribute__(self, name): if(name == '_pipe'): return super(ProxyClient, self).__getattribute__(name) @@ -1166,7 +1240,7 @@ self._pipe.send(('funcall', name, args, kwargs)) return self._pipe.recv()[1] return func - + def __setattr__(self, name, value): if(name == '_pipe'): return super(ProxyClient, self).__setattr__(name, value) @@ -1185,17 +1259,17 @@ unicode(self.client_address)) logger.debug("Pipe FD: %d", self.server.child_pipe.fileno()) - + session = (gnutls.connection .ClientSession(self.request, gnutls.connection .X509Credentials())) - + # Note: gnutls.connection.X509Credentials is really a # generic GnuTLS certificate credentials object so long as # no X.509 keys are added to it. Therefore, we can use it # here despite using OpenPGP certificates. - + #priority = ':'.join(("NONE", "+VERS-TLS1.1", # "+AES-256-CBC", "+SHA1", # "+COMP-NULL", "+CTYPE-OPENPGP", @@ -1207,7 +1281,7 @@ (gnutls.library.functions .gnutls_priority_set_direct(session._c_object, priority, None)) - + # Start communication using the Mandos protocol # Get protocol number line = self.request.makefile().readline() @@ -1218,7 +1292,7 @@ except (ValueError, IndexError, RuntimeError) as error: logger.error("Unknown protocol version: %s", error) return - + # Start GnuTLS connection try: session.handshake() @@ -1228,7 +1302,7 @@ # established. Just abandon the request. return logger.debug("Handshake succeeded") - + approval_required = False try: try: @@ -1239,7 +1313,7 @@ logger.warning("Bad certificate: %s", error) return logger.debug("Fingerprint: %s", fpr) - + try: client = ProxyClient(child_pipe, fpr, self.client_address) @@ -1311,7 +1385,7 @@ sent, len(client.secret) - (sent_size + sent)) sent_size += sent - + logger.info("Sending secret to %s", client.name) # bump the timeout as if seen client.checked_ok(client.extended_timeout) @@ -1405,6 +1479,7 @@ multiprocessing.Process(target = self.sub_process_main, args = (request, address)).start() + class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object): """ adds a pipe to the MixIn """ def process_request(self, request, client_address): @@ -1413,16 +1488,17 @@ This function creates a new pipe in self.pipe """ parent_pipe, self.child_pipe = multiprocessing.Pipe() - + super(MultiprocessingMixInWithPipe, self).process_request(request, client_address) self.child_pipe.close() self.add_pipe(parent_pipe) - + def add_pipe(self, parent_pipe): """Dummy function; override as necessary""" raise NotImplementedError + class IPv6_TCPServer(MultiprocessingMixInWithPipe, socketserver.TCPServer, object): """IPv6-capable TCP server. Accepts 'None' as address and/or port @@ -1576,7 +1652,7 @@ kwargs = request[3] parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs))) - + if command == 'getattr': attrname = request[1] if callable(client_object.__getattribute__(attrname)): @@ -1588,7 +1664,7 @@ attrname = request[1] value = request[2] setattr(client_object, attrname, value) - + return True @@ -1773,7 +1849,7 @@ debuglevel = server_settings["debuglevel"] use_dbus = server_settings["use_dbus"] use_ipv6 = server_settings["use_ipv6"] - + if server_settings["servicename"] != "Mandos": syslogger.setFormatter(logging.Formatter ('Mandos (%s) [%%(process)d]:' @@ -1840,7 +1916,7 @@ level = getattr(logging, debuglevel.upper()) syslogger.setLevel(level) console.setLevel(level) - + if debug: # Enable all possible GnuTLS debugging @@ -1879,6 +1955,8 @@ try: bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos", bus, do_not_queue=True) + bus_name2 = dbus.service.BusName("se.recompile.Mandos", + bus, do_not_queue=True) except dbus.exceptions.NameExistsException as e: logger.error(unicode(e) + ", disabling D-Bus") use_dbus = False @@ -1933,7 +2011,7 @@ del pidfilename signal.signal(signal.SIGINT, signal.SIG_IGN) - + signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) @@ -2060,5 +2138,6 @@ # Must run before the D-Bus bus name gets deregistered cleanup() + if __name__ == '__main__': main()