=== modified file 'DBUS-API' --- DBUS-API 2012-01-15 20:27:28 +0000 +++ DBUS-API 2014-08-10 14:13:02 +0000 @@ -150,9 +150,6 @@ milliseconds, depending on ApprovedByDefault. Approve() can now usefully be called on this client object. -*** PropertyChanged(s: Property, v: Value) - The Property on this client has changed to Value. - *** Rejected(s: Reason) This client was not given its secret for a specified Reason. === modified file 'TODO' --- TODO 2014-07-14 21:41:08 +0000 +++ TODO 2014-10-05 19:39:25 +0000 @@ -23,6 +23,7 @@ ** TODO [#B] Use capabilities instead of seteuid(). ** TODO [#B] Use getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton() ** TODO [#C] Make start_mandos_communication() take "struct server". +** TODO [#C] --interfaces=regex,eth*,noregex (bridge-utils-interfaces(5)) * splashy ** TODO [#B] use scandir(3) instead of readdir(3) @@ -32,7 +33,6 @@ ** TODO [#B] use scandir(3) instead of readdir(3) * askpass-fifo -** TODO [#B] Drop privileges after opening FIFO. * password-prompt ** TODO [#B] lock stdin (with flock()?) @@ -48,6 +48,9 @@ ** kernel command line option for debug info * mandos (server) +** TODO [#B] Work around Avahi issue + Avahi does not announce link-local addresses if any global + addresses exist: http://lists.freedesktop.org/archives/avahi/2010-March/001863.html ** TODO [#B] Log level :BUGS: *** TODO /etc/mandos/clients.d/*.conf Watch this directory and add/remove/update clients? @@ -69,9 +72,6 @@ ** TODO Use python-tlslite? ** TODO D-Bus AddClient() method on server object ** TODO Use org.freedesktop.DBus.Method.NoReply annotation on async methods. :2: -** TODO Emit [[http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties][org.freedesktop.DBus.Properties.PropertiesChanged]] signal :2: - TODO Deprecate se.recompile.Mandos.Client.PropertyChanged - annotate! - TODO Can use "invalidates" annotation to also emit on changed secret. ** TODO Support [[http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager][org.freedesktop.DBus.ObjectManager]] interface on server object :2: Deprecate methods GetAllClients(), GetAllClientsWithProperties() and signals ClientAdded and ClientRemoved. @@ -80,12 +80,13 @@ ** TODO Secret Service API? http://standards.freedesktop.org/secret-service/ ** TODO Remove D-Bus interfaces with old domain name :2: -** TODO Remove old string_to_delta format :2: +** TODO Remove old string_to_delta format :2: ** TODO http://0pointer.de/blog/projects/stateless.html *** tmpfiles snippet to create /var/lib/mandos with right user+perms *** File in /usr/lib/sysusers.d to create user+group "_mandos" ** TODO Error handling on error parsing config files ** TODO init.d script error handling +** TODO D-Bus server properties; address, port, interface, etc. :2: * mandos.xml ** Add mandos contact info in manual pages @@ -93,7 +94,7 @@ * mandos-ctl *** Handle "no D-Bus server" and/or "no Mandos server found" better *** [#B] --dump option -** TODO Remove old string_to_delta format :2: +** TODO Remove old string_to_delta format :2: * TODO mandos-dispatch Listens for specified D-Bus signals and spawns shell commands with === modified file 'debian/control' --- debian/control 2014-07-25 23:54:03 +0000 +++ debian/control 2014-10-05 19:46:11 +0000 @@ -5,8 +5,8 @@ Uploaders: Teddy Hogeborn , Björn Påhlsson Build-Depends: debhelper (>= 9), docbook-xml, docbook-xsl, - libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc, - pkg-config + libavahi-core-dev, libgpgme11-dev, libgnutls28-dev + | gnutls-dev, xsltproc, pkg-config Build-Depends-Indep: systemd, python2.7, python2.7-gnutls, python2.7-dbus, python2.7-avahi, python2.7-gobject Standards-Version: 3.9.5 === modified file 'mandos' --- mandos 2014-08-06 20:56:55 +0000 +++ mandos 2014-08-10 14:13:02 +0000 @@ -88,6 +88,9 @@ except ImportError: SO_BINDTODEVICE = None +if sys.version_info.major == 2: + str = unicode + version = "1.6.8" stored_state_file = "clients.pickle" @@ -104,10 +107,8 @@ SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h with contextlib.closing(socket.socket()) as s: ifreq = fcntl.ioctl(s, SIOCGIFINDEX, - struct.pack(str("16s16x"), - interface)) - interface_index = struct.unpack(str("I"), - ifreq[16:20])[0] + struct.pack(b"16s16x", interface)) + interface_index = struct.unpack("I", ifreq[16:20])[0] return interface_index @@ -118,7 +119,7 @@ syslogger = (logging.handlers.SysLogHandler (facility = logging.handlers.SysLogHandler.LOG_DAEMON, - address = str("/dev/log"))) + address = "/dev/log")) syslogger.setFormatter(logging.Formatter ('Mandos [%(process)d]: %(levelname)s:' ' %(message)s')) @@ -224,9 +225,8 @@ class AvahiError(Exception): def __init__(self, value, *args, **kwargs): self.value = value - super(AvahiError, self).__init__(value, *args, **kwargs) - def __unicode__(self): - return unicode(repr(self.value)) + return super(AvahiError, self).__init__(value, *args, + **kwargs) class AvahiServiceError(AvahiError): pass @@ -282,8 +282,8 @@ " after %i retries, exiting.", self.rename_count) raise AvahiServiceError("Too many renames") - self.name = unicode(self.server - .GetAlternativeServiceName(self.name)) + self.name = str(self.server + .GetAlternativeServiceName(self.name)) logger.info("Changing Zeroconf service name to %r ...", self.name) self.remove() @@ -337,7 +337,7 @@ self.rename() elif state == avahi.ENTRY_GROUP_FAILURE: logger.critical("Avahi: Error in group state changed %s", - unicode(error)) + str(error)) raise AvahiGroupError("State changed: {!s}" .format(error)) @@ -688,8 +688,7 @@ if self.checker is None: # Escape attributes for the shell escaped_attrs = { attr: - re.escape(unicode(getattr(self, - attr))) + re.escape(str(getattr(self, attr))) for attr in self.runtime_expansions } try: command = self.checker_command % escaped_attrs @@ -814,11 +813,11 @@ """Decorator to annotate D-Bus methods, signals or properties Usage: + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true", + "org.freedesktop.DBus.Property." + "EmitsChangedSignal": "false"}) @dbus_service_property("org.example.Interface", signature="b", access="r") - @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true", - "org.freedesktop.DBus.Property." - "EmitsChangedSignal": "false"}) def Property_dbus_property(self): return dbus.Boolean(False) """ @@ -831,9 +830,7 @@ class DBusPropertyException(dbus.exceptions.DBusException): """A base class for D-Bus property-related exceptions """ - def __unicode__(self): - return unicode(str(self)) - + pass class DBusPropertyAccessException(DBusPropertyException): """A property's access permissions disallows an operation. @@ -949,6 +946,14 @@ value.variant_level+1) return dbus.Dictionary(properties, signature="sv") + @dbus.service.signal(dbus.PROPERTIES_IFACE, signature="sa{sv}as") + def PropertiesChanged(self, interface_name, changed_properties, + invalidated_properties): + """Standard D-Bus PropertiesChanged() signal, see D-Bus + standard. + """ + pass + @dbus.service.method(dbus.INTROSPECTABLE_IFACE, out_signature="s", path_keyword='object_path', @@ -1221,6 +1226,8 @@ runtime_expansions = (Client.runtime_expansions + ("dbus_object_path",)) + _interface = "se.recompile.Mandos.Client" + # dbus.service.Object doesn't use super(), so we can't either. def __init__(self, bus = None, *args, **kwargs): @@ -1228,7 +1235,7 @@ Client.__init__(self, *args, **kwargs) # Only now, when this client is initialized, can it show up on # the D-Bus - client_object_name = unicode(self.name).translate( + client_object_name = str(self.name).translate( {ord("."): ord("_"), ord("-"): ord("_")}) self.dbus_object_path = (dbus.ObjectPath @@ -1238,7 +1245,8 @@ def notifychangeproperty(transform_func, dbus_name, type_func=lambda x: x, - variant_level=1): + variant_level=1, invalidate_only=False, + _interface=_interface): """ Modify a variable so that it's a property which announces its changes to DBus. @@ -1255,11 +1263,21 @@ if (not hasattr(self, attrname) or type_func(getattr(self, attrname, None)) != type_func(value)): - dbus_value = transform_func(type_func(value), - variant_level - =variant_level) - self.PropertyChanged(dbus.String(dbus_name), - dbus_value) + if invalidate_only: + self.PropertiesChanged(_interface, + dbus.Dictionary(), + dbus.Array + ((dbus_name,))) + else: + dbus_value = transform_func(type_func(value), + variant_level + =variant_level) + self.PropertyChanged(dbus.String(dbus_name), + dbus_value) + self.PropertiesChanged(_interface, + dbus.Dictionary({ + dbus.String(dbus_name): + dbus_value }), dbus.Array()) setattr(self, attrname, value) return property(lambda self: getattr(self, attrname), setter) @@ -1303,6 +1321,8 @@ lambda td: td.total_seconds() * 1000) checker_command = notifychangeproperty(dbus.String, "Checker") + secret = notifychangeproperty(dbus.ByteArray, "Secret", + invalidate_only=True) del notifychangeproperty @@ -1355,15 +1375,9 @@ self.send_changedstate() ## D-Bus methods, signals & properties - _interface = "se.recompile.Mandos.Client" ## Interfaces - @dbus_interface_annotations(_interface) - def _foo(self): - return { "org.freedesktop.DBus.Property.EmitsChangedSignal": - "false"} - ## Signals # CheckerCompleted - signal @@ -1379,6 +1393,7 @@ pass # PropertyChanged - signal + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.signal(_interface, signature="sv") def PropertyChanged(self, property, value): "D-Bus signal" @@ -1489,7 +1504,7 @@ def Host_dbus_property(self, value=None): if value is None: # get return dbus.String(self.host) - self.host = unicode(value) + self.host = str(value) # Created - property @dbus_service_property(_interface, signature="s", access="read") @@ -1593,7 +1608,7 @@ def Checker_dbus_property(self, value=None): if value is None: # get return dbus.String(self.checker_command) - self.checker_command = unicode(value) + self.checker_command = str(value) # CheckerRunning - property @dbus_service_property(_interface, signature="b", @@ -1615,7 +1630,7 @@ @dbus_service_property(_interface, signature="ay", access="write", byte_arrays=True) def Secret_dbus_property(self, value): - self.secret = str(value) + self.secret = bytes(value) del _interface @@ -1655,7 +1670,7 @@ def handle(self): with contextlib.closing(self.server.child_pipe) as child_pipe: logger.info("TCP connection from: %s", - unicode(self.client_address)) + str(self.client_address)) logger.debug("Pipe FD: %d", self.server.child_pipe.fileno()) @@ -1960,7 +1975,8 @@ try: self.socket.setsockopt(socket.SOL_SOCKET, SO_BINDTODEVICE, - str(self.interface + '\0')) + (self.interface + "\0") + .encode("utf-8")) except socket.error as error: if error.errno == errno.EPERM: logger.error("No permission to bind to" @@ -2226,7 +2242,7 @@ timevalue = datetime.timedelta(0) for s in interval.split(): try: - suffix = unicode(s[-1]) + suffix = s[-1] value = int(s[:-1]) if suffix == "d": delta = datetime.timedelta(value) @@ -2383,8 +2399,9 @@ del options # Force all strings to be unicode for option in server_settings.keys(): - if type(server_settings[option]) is str: - server_settings[option] = unicode(server_settings[option]) + if isinstance(server_settings[option], bytes): + server_settings[option] = (server_settings[option] + .decode("utf-8")) # Force all boolean options to be boolean for option in ("debug", "use_dbus", "use_ipv6", "restore", "foreground", "zeroconf"): @@ -2533,7 +2550,8 @@ protocol = protocol, bus = bus) if server_settings["interface"]: service.interface = (if_nametoindex - (str(server_settings["interface"]))) + (server_settings["interface"] + .encode("utf-8"))) global multiprocessing_manager multiprocessing_manager = multiprocessing.Manager() @@ -2657,7 +2675,7 @@ try: with pidfile: pid = os.getpid() - pidfile.write(str(pid) + "\n".encode("utf-8")) + pidfile.write("{}\n".format(pid).encode("utf-8")) except IOError: logger.error("Could not write to file %r with PID %d", pidfilename, pid) @@ -2709,8 +2727,8 @@ def GetAllClientsWithProperties(self): "D-Bus method" return dbus.Dictionary( - ((c.dbus_object_path, c.GetAll("")) - for c in tcp_server.clients.itervalues()), + { c.dbus_object_path: c.GetAll("") + for c in tcp_server.clients.itervalues() }, signature="oa{sv}") @dbus.service.method(_interface, in_signature="o") === modified file 'mandos-monitor' --- mandos-monitor 2014-08-06 20:56:55 +0000 +++ mandos-monitor 2014-08-10 14:13:02 +0000 @@ -87,9 +87,9 @@ self.proxy = proxy_object # Mandos Client proxy object self.properties = dict() if properties is None else properties self.property_changed_match = ( - self.proxy.connect_to_signal("PropertyChanged", - self._property_changed, - client_interface, + self.proxy.connect_to_signal("PropertiesChanged", + self.properties_changed, + dbus.PROPERTIES_IFACE, byte_arrays=True)) if properties is None: @@ -100,16 +100,12 @@ super(MandosClientPropertyCache, self).__init__(**kwargs) - def _property_changed(self, property, value): - """Helper which takes positional arguments""" - return self.property_changed(property=property, value=value) - - def property_changed(self, property=None, value=None): - """This is called whenever we get a PropertyChanged signal - It updates the changed property in the "properties" dict. + def properties_changed(self, interface, properties, invalidated): + """This is called whenever we get a PropertiesChanged signal + It updates the changed properties in the "properties" dict. """ # Update properties dict with new value - self.properties[property] = value + self.properties.update(properties) def delete(self): self.property_changed_match.remove() @@ -377,14 +373,15 @@ else: return key - def property_changed(self, property=None, **kwargs): - """Call self.update() if old value is not new value. + def properties_changed(self, interface, properties, invalidated): + """Call self.update() if any properties changed. This overrides the method from MandosClientPropertyCache""" - property_name = str(property) - old_value = self.properties.get(property_name) - super(MandosClientWidget, self).property_changed( - property=property, **kwargs) - if self.properties.get(property_name) != old_value: + old_values = { key: self.properties.get(key) + for key in properties.keys() } + super(MandosClientWidget, self).properties_changed( + interface, properties, invalidated) + if any(old_values[key] != self.properties.get(key) + for key in old_values): self.update() === modified file 'mandos.service' --- mandos.service 2014-08-06 20:33:26 +0000 +++ mandos.service 2014-10-05 19:39:25 +0000 @@ -1,5 +1,6 @@ [Unit] Description=Server of encrypted passwords to Mandos clients +Documentation=man:intro(8mandos) man:mandos(8) [Service] Type=simple === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2014-03-29 02:38:15 +0000 +++ plugins.d/askpass-fifo.c 2014-08-09 23:37:07 +0000 @@ -23,7 +23,7 @@ */ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ -#include /* ssize_t */ +#include /* uid_t, gid_t, ssize_t */ #include /* mkfifo(), S_IRUSR, S_IWUSR */ #include /* and */ #include /* errno, EACCES, ENOTDIR, ELOOP, @@ -44,6 +44,8 @@ #include /* strerror() */ #include /* va_list, va_start(), ... */ +uid_t uid = 65534; +gid_t gid = 65534; /* Function to use when printing errors */ __attribute__((format (gnu_printf, 3, 4))) @@ -74,6 +76,9 @@ int ret = 0; ssize_t sret; + uid = getuid(); + gid = getgid(); + /* Create FIFO */ const char passfifo[] = "/lib/cryptsetup/passfifo"; ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); @@ -119,6 +124,16 @@ } } + /* Lower group privileges */ + if(setgid(gid) == -1){ + error_plus(0, errno, "setgid"); + } + + /* Lower user privileges */ + if(setuid(uid) == -1){ + error_plus(0, errno, "setuid"); + } + /* Read from FIFO */ char *buf = NULL; size_t buf_len = 0; === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2014-07-15 23:52:00 +0000 +++ plugins.d/mandos-client.c 2014-08-20 21:46:38 +0000 @@ -1650,6 +1650,11 @@ _exit(EXIT_FAILURE); } } else { + if(hook_pid == -1){ + perror_plus("fork"); + free(direntry); + continue; + } int status; if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){ perror_plus("waitpid");