=== modified file 'DBUS-API' --- DBUS-API 2011-02-27 17:26:35 +0000 +++ DBUS-API 2011-03-17 18:14:34 +0000 @@ -104,22 +104,22 @@ c) Setting this property is equivalent to calling StartChecker() or StopChecker(). - d) The creation time of this client object, as a RFC 3339 string. + d) The creation time of this client object, as an RFC 3339 string. e) Setting this property is equivalent to calling Enable() or Disable(). - f) The time of the last approval request, as a RFC 3339 string, or + f) The time of the last approval request, as an RFC 3339 string, or an empty string if this has not happened. - g) The last time a checker was successful, as a RFC 3339 string, or - an empty string if this has not happened. Setting this property - is equivalent to calling CheckedOK(), i.e. the current time is - set, regardless of the string sent. Please always use an empty - string when setting this property, to allow for possible future - expansion. + g) The last time a checker was successful, as an RFC 3339 string, + or an empty string if this has not happened. Setting this + property is equivalent to calling CheckedOK(), i.e. the current + time is set, regardless of the string sent. Please always use + an empty string when setting this property, to allow for + possible future expansion. - h) The last time this client was enabled, as a RFC 3339 string, or + h) The last time this client was enabled, as an RFC 3339 string, or an empty string if this has not happened. i) A raw byte array, not hexadecimal digits. @@ -140,10 +140,10 @@ This client will be approved or denied in exactly Timeout 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 'Makefile' --- Makefile 2011-03-08 19:41:59 +0000 +++ Makefile 2011-07-13 02:24:39 +0000 @@ -210,8 +210,8 @@ $@) plugins.d/mandos-client: plugins.d/mandos-client.c - $(LINK.c) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) $(strip\ - ) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ + $(LINK.c) $^ -lrt $(GNUTLS_LIBS) $(AVAHI_LIBS) $(strip\ + ) $(GPGME_LIBS) $(LOADLIBES) $(LDLIBS) -o $@ .PHONY : all doc html clean distclean run-client run-server install \ install-server install-client uninstall uninstall-server \ === modified file 'TODO' --- TODO 2010-11-16 17:58:49 +0000 +++ TODO 2011-07-25 18:47:45 +0000 @@ -4,36 +4,36 @@ * mandos-applet +* Convert README into intro(8mandos) man page + * mandos-client -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] Retry a server which has a non-definite reply: -*** A closed connection during the TLS handshake -*** A TCP timeout ** TODO [#B] Use capabilities instead of seteuid(). -** TODO [#A] Retry --connect forever +** TODO [#B] Use struct sockaddr_storage instead of a union +** TODO [#B] Use getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton() +** TODO [#B] Use getnameinfo(serv=NULL, NI_NUMERICHOST) instead of inet_ntop() +** TODO [#B] Accept [] around IPv6 address in --connect option; see [[http://tools.ietf.org/html/rfc5952][RFC 5952 - A Recommendation for IPv6 Address Text Representation]] * splashy ** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name * usplash ** TODO [#A] Make it work again ** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name ** TODO Use [[info:libc:Argz%20Functions][argz_extract]] * askpass-fifo -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name ** TODO [#B] Drop privileges after opening FIFO. * password-prompt -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name ** TODO [#B] lock stdin (with flock()?) +* plymouth + * TODO [#B] passdev * plugin-runner +** TODO handle printing for errors for plugins +*** Hook up stderr of plugins, buffer them, and prepend mandos pluig [plugin name] ** TODO [#B] use scandir(3) instead of readdir(3) ** TODO [#C] use same file name rules as run-parts(8) ** kernel command line option for debug info @@ -85,6 +85,7 @@ ** TODO [#B] break the wait on approval_delay if connection dies ** TODO Generate Client.runtime_expansions from client options + extra ** TODO Allow %%(checker)s as a runtime expansion +** TODO Use python-tlslite? * mandos.xml ** Add mandos contact info in manual pages @@ -99,7 +100,7 @@ arguments. * mandos-monitor -** TODO help should be toggable +** TODO help should be toggleable ** Urwid client data displayer Better view of client data in the listing *** Properties popup === modified file 'debian/control' --- debian/control 2011-02-27 17:46:47 +0000 +++ debian/control 2011-03-17 18:13:40 +0000 @@ -16,7 +16,7 @@ Architecture: all Depends: ${misc:Depends}, python (>=2.6), python-gnutls, python-dbus, python-avahi, python-gobject, avahi-daemon, adduser, - python-urwid + python-urwid, python (>=2.7) | python-argparse Recommends: fping Description: a server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows @@ -37,7 +37,7 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup, gnupg (<< 2) -Conflicts: dropbear (<< 0.52-5) +Conflicts: dropbear (<= 0.52-5) Enhances: cryptsetup Description: do unattended reboots with an encrypted root file system This is the client part of the Mandos system, which allows === modified file 'initramfs-tools-script' --- initramfs-tools-script 2009-09-16 23:28:39 +0000 +++ initramfs-tools-script 2011-07-16 00:29:19 +0000 @@ -108,6 +108,7 @@ # Our keyscript mandos=/lib/mandos/plugin-runner +test -x "$mandos" # parse /conf/conf.d/cryptroot. Format: # target=sda2_crypt,source=/dev/sda2,key=none,keyscript=/foo/bar/baz === modified file 'mandos' --- mandos 2011-03-08 19:41:59 +0000 +++ mandos 2011-06-23 22:27:15 +0000 @@ -36,7 +36,7 @@ import SocketServer as socketserver import socket -import optparse +import argparse import datetime import errno import gnutls.crypto @@ -151,6 +151,7 @@ self.group = None # our entry group self.server = None self.bus = bus + self.entry_group_state_changed_match = None def rename(self): """Derived from the Avahi example code""" if self.rename_count >= self.max_renames: @@ -168,25 +169,29 @@ self.remove() try: self.add() - except dbus.exceptions.DBusException, error: + except dbus.exceptions.DBusException as error: logger.critical("DBusException: %s", error) self.cleanup() os._exit(1) self.rename_count += 1 def remove(self): """Derived from the Avahi example code""" + if self.entry_group_state_changed_match is not None: + self.entry_group_state_changed_match.remove() + self.entry_group_state_changed_match = None if self.group is not None: self.group.Reset() def add(self): """Derived from the Avahi example code""" + self.remove() if self.group is None: self.group = dbus.Interface( self.bus.get_object(avahi.DBUS_NAME, self.server.EntryGroupNew()), avahi.DBUS_INTERFACE_ENTRY_GROUP) - self.group.connect_to_signal('StateChanged', - self - .entry_group_state_changed) + self.entry_group_state_changed_match = ( + self.group.connect_to_signal( + 'StateChanged', self .entry_group_state_changed)) logger.debug("Adding Zeroconf service '%s' of type '%s' ...", self.name, self.type) self.group.AddService( @@ -215,22 +220,44 @@ def cleanup(self): """Derived from the Avahi example code""" if self.group is not None: - self.group.Free() + try: + self.group.Free() + except (dbus.exceptions.UnknownMethodException, + dbus.exceptions.DBusException) as e: + pass self.group = None - def server_state_changed(self, state): + self.remove() + def server_state_changed(self, state, error=None): """Derived from the Avahi example code""" logger.debug("Avahi server state change: %i", state) - if state == avahi.SERVER_COLLISION: - logger.error("Zeroconf server name collision") - self.remove() + bad_states = { avahi.SERVER_INVALID: + "Zeroconf server invalid", + avahi.SERVER_REGISTERING: None, + avahi.SERVER_COLLISION: + "Zeroconf server name collision", + avahi.SERVER_FAILURE: + "Zeroconf server failure" } + if state in bad_states: + if bad_states[state] is not None: + if error is None: + logger.error(bad_states[state]) + else: + logger.error(bad_states[state] + ": %r", error) + self.cleanup() elif state == avahi.SERVER_RUNNING: self.add() + else: + if error is None: + logger.debug("Unknown state: %r", state) + else: + logger.debug("Unknown state: %r: %r", state, error) def activate(self): """Derived from the Avahi example code""" if self.server is None: self.server = dbus.Interface( self.bus.get_object(avahi.DBUS_NAME, - avahi.DBUS_PATH_SERVER), + avahi.DBUS_PATH_SERVER, + follow_name_owner_changes=True), avahi.DBUS_INTERFACE_SERVER) self.server.connect_to_signal("StateChanged", self.server_state_changed) @@ -445,7 +472,7 @@ # If a checker exists, make sure it is not a zombie try: pid, status = os.waitpid(self.checker.pid, os.WNOHANG) - except (AttributeError, OSError), error: + except (AttributeError, OSError) as error: if (isinstance(error, OSError) and error.errno != errno.ECHILD): raise error @@ -472,7 +499,7 @@ try: command = self.checker_command % escaped_attrs - except TypeError, error: + except TypeError as error: logger.error('Could not format string "%s":' ' %s', self.checker_command, error) return True # Try again later @@ -497,7 +524,7 @@ if pid: gobject.source_remove(self.checker_callback_tag) self.checker_callback(pid, status, command) - except OSError, error: + except OSError as error: logger.error("Failed to start subprocess: %s", error) # Re-run this periodically if run by gobject.timeout_add @@ -516,7 +543,7 @@ #time.sleep(0.5) #if self.checker.poll() is None: # os.kill(self.checker.pid, signal.SIGKILL) - except OSError, error: + except OSError as error: if error.errno != errno.ESRCH: # No such process raise self.checker = None @@ -704,7 +731,7 @@ xmlstring = document.toxml("utf-8") document.unlink() except (AttributeError, xml.dom.DOMException, - xml.parsers.expat.ExpatError), error: + xml.parsers.expat.ExpatError) as error: logger.error("Failed to override Introspection method", error) return xmlstring @@ -813,13 +840,12 @@ *args, **kwargs) def checked_ok(self, *args, **kwargs): - r = Client.checked_ok(self, *args, **kwargs) + Client.checked_ok(self, *args, **kwargs) # Emit D-Bus signal self.PropertyChanged( dbus.String("LastCheckedOK"), (self._datetime_to_dbus(self.last_checked_ok, variant_level=1))) - return r def need_approval(self, *args, **kwargs): r = Client.need_approval(self, *args, **kwargs) @@ -922,7 +948,7 @@ # CheckedOK - method @dbus.service.method(_interface) def CheckedOK(self): - return self.checked_ok() + self.checked_ok() # Enable - method @dbus.service.method(_interface) @@ -1204,14 +1230,14 @@ try: if int(line.strip().split()[0]) > 1: raise RuntimeError - except (ValueError, IndexError, RuntimeError), error: + except (ValueError, IndexError, RuntimeError) as error: logger.error("Unknown protocol version: %s", error) return # Start GnuTLS connection try: session.handshake() - except gnutls.errors.GNUTLSError, error: + except gnutls.errors.GNUTLSError as error: logger.warning("Handshake failed: %s", error) # Do not run session.bye() here: the session is not # established. Just abandon the request. @@ -1223,7 +1249,8 @@ try: fpr = self.fingerprint(self.peer_certificate (session)) - except (TypeError, gnutls.errors.GNUTLSError), error: + except (TypeError, + gnutls.errors.GNUTLSError) as error: logger.warning("Bad certificate: %s", error) return logger.debug("Fingerprint: %s", fpr) @@ -1241,7 +1268,7 @@ while True: if not client.enabled: - logger.warning("Client %s is disabled", + logger.info("Client %s is disabled", client.name) if self.server.use_dbus: # Emit D-Bus signal @@ -1292,7 +1319,7 @@ while sent_size < len(client.secret): try: sent = session.send(client.secret[sent_size:]) - except (gnutls.errors.GNUTLSError), error: + except gnutls.errors.GNUTLSError as error: logger.warning("gnutls send failed") return logger.debug("Sent: %d, remaining: %d", @@ -1312,7 +1339,7 @@ client.approvals_pending -= 1 try: session.bye() - except (gnutls.errors.GNUTLSError), error: + except gnutls.errors.GNUTLSError as error: logger.warning("GnuTLS bye failed") @staticmethod @@ -1442,7 +1469,7 @@ SO_BINDTODEVICE, str(self.interface + '\0')) - except socket.error, error: + except socket.error as error: if error[0] == errno.EPERM: logger.error("No permission to" " bind to interface %s", @@ -1542,8 +1569,8 @@ client = c break else: - logger.warning("Client not found for fingerprint: %s, ad" - "dress: %s", fpr, address) + logger.info("Client not found for fingerprint: %s, ad" + "dress: %s", fpr, address) if self.use_dbus: # Emit D-Bus signal mandos_dbus_service.ClientNotFound(fpr, address[0]) @@ -1613,7 +1640,7 @@ delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value) else: raise ValueError("Unknown suffix %r" % suffix) - except (ValueError, IndexError), e: + except (ValueError, IndexError) as e: raise ValueError(*(e.args)) timevalue += delta return timevalue @@ -1673,34 +1700,37 @@ ################################################################## # Parsing of options, both command line and config file - parser = optparse.OptionParser(version = "%%prog %s" % version) - parser.add_option("-i", "--interface", type="string", - metavar="IF", help="Bind to interface IF") - parser.add_option("-a", "--address", type="string", - help="Address to listen for requests on") - parser.add_option("-p", "--port", type="int", - help="Port number to receive requests on") - parser.add_option("--check", action="store_true", - help="Run self-test") - parser.add_option("--debug", action="store_true", - help="Debug mode; run in foreground and log to" - " terminal") - parser.add_option("--debuglevel", type="string", metavar="LEVEL", - help="Debug level for stdout output") - parser.add_option("--priority", type="string", help="GnuTLS" - " priority string (see GnuTLS documentation)") - parser.add_option("--servicename", type="string", - metavar="NAME", help="Zeroconf service name") - parser.add_option("--configdir", type="string", - default="/etc/mandos", metavar="DIR", - help="Directory to search for configuration" - " files") - parser.add_option("--no-dbus", action="store_false", - dest="use_dbus", help="Do not provide D-Bus" - " system bus interface") - parser.add_option("--no-ipv6", action="store_false", - dest="use_ipv6", help="Do not use IPv6") - options = parser.parse_args()[0] + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--version", action="version", + version = "%%(prog)s %s" % version, + help="show version number and exit") + parser.add_argument("-i", "--interface", metavar="IF", + help="Bind to interface IF") + parser.add_argument("-a", "--address", + help="Address to listen for requests on") + parser.add_argument("-p", "--port", type=int, + help="Port number to receive requests on") + parser.add_argument("--check", action="store_true", + help="Run self-test") + parser.add_argument("--debug", action="store_true", + help="Debug mode; run in foreground and log" + " to terminal") + parser.add_argument("--debuglevel", metavar="LEVEL", + help="Debug level for stdout output") + parser.add_argument("--priority", help="GnuTLS" + " priority string (see GnuTLS documentation)") + parser.add_argument("--servicename", + metavar="NAME", help="Zeroconf service name") + parser.add_argument("--configdir", + default="/etc/mandos", metavar="DIR", + help="Directory to search for configuration" + " files") + parser.add_argument("--no-dbus", action="store_false", + dest="use_dbus", help="Do not provide D-Bus" + " system bus interface") + parser.add_argument("--no-ipv6", action="store_false", + dest="use_ipv6", help="Do not use IPv6") + options = parser.parse_args() if options.check: import doctest @@ -1813,7 +1843,7 @@ try: os.setgid(gid) os.setuid(uid) - except OSError, error: + except OSError as error: if error[0] != errno.EPERM: raise error @@ -1863,7 +1893,7 @@ try: bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos", bus, do_not_queue=True) - except dbus.exceptions.NameExistsException, e: + except dbus.exceptions.NameExistsException as e: logger.error(unicode(e) + ", disabling D-Bus") use_dbus = False server_settings["use_dbus"] = False @@ -2019,7 +2049,7 @@ # From the Avahi example code try: service.activate() - except dbus.exceptions.DBusException, error: + except dbus.exceptions.DBusException as error: logger.critical("DBusException: %s", error) cleanup() sys.exit(1) @@ -2032,7 +2062,7 @@ logger.debug("Starting main loop") main_loop.run() - except AvahiError, error: + except AvahiError as error: logger.critical("AvahiError: %s", error) cleanup() sys.exit(1) === modified file 'mandos-ctl' --- mandos-ctl 2011-03-08 19:41:59 +0000 +++ mandos-ctl 2011-03-15 20:13:05 +0000 @@ -27,7 +27,7 @@ import sys import dbus -from optparse import OptionParser +import argparse import locale import datetime import re @@ -76,10 +76,9 @@ "seconds": td.seconds % 60, }) - def string_to_delta(interval): """Parse a string and return a datetime.timedelta - + >>> string_to_delta("7d") datetime.timedelta(7) >>> string_to_delta("60s") @@ -158,185 +157,190 @@ options.secret is not None, options.approve, options.deny)) - + def main(): - parser = OptionParser(version = "%%prog %s" % version) - parser.add_option("-a", "--all", action="store_true", - help="Select all clients") - parser.add_option("-v", "--verbose", action="store_true", - help="Print all fields") - parser.add_option("-e", "--enable", action="store_true", - help="Enable client") - parser.add_option("-d", "--disable", action="store_true", - help="disable client") - parser.add_option("-b", "--bump-timeout", action="store_true", - help="Bump timeout for client") - parser.add_option("--start-checker", action="store_true", - help="Start checker for client") - parser.add_option("--stop-checker", action="store_true", - help="Stop checker for client") - parser.add_option("-V", "--is-enabled", action="store_true", - help="Check if client is enabled") - parser.add_option("-r", "--remove", action="store_true", - help="Remove client") - parser.add_option("-c", "--checker", type="string", - help="Set checker command for client") - parser.add_option("-t", "--timeout", type="string", - help="Set timeout for client") - parser.add_option("-i", "--interval", type="string", - help="Set checker interval for client") - parser.add_option("--approve-by-default", action="store_true", - dest="approved_by_default", - help="Set client to be approved by default") - parser.add_option("--deny-by-default", action="store_false", - dest="approved_by_default", - help="Set client to be denied by default") - parser.add_option("--approval-delay", type="string", - help="Set delay before client approve/deny") - parser.add_option("--approval-duration", type="string", - help="Set duration of one client approval") - parser.add_option("-H", "--host", type="string", - help="Set host for client") - parser.add_option("-s", "--secret", type="string", - help="Set password blob (file) for client") - parser.add_option("-A", "--approve", action="store_true", - help="Approve any current client request") - parser.add_option("-D", "--deny", action="store_true", - help="Deny any current client request") - options, client_names = parser.parse_args() - - if has_actions(options) and not client_names and not options.all: - parser.error("Options require clients names or --all.") - if options.verbose and has_actions(options): - parser.error("--verbose can only be used alone or with" - " --all.") - if options.all and not has_actions(options): - parser.error("--all requires an action.") - - try: - bus = dbus.SystemBus() - mandos_dbus_objc = bus.get_object(busname, server_path) - except dbus.exceptions.DBusException: - print("Could not connect to Mandos server", - file=sys.stderr) - sys.exit(1) - - mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface = server_interface) - - #block stderr since dbus library prints to stderr - null = os.open(os.path.devnull, os.O_RDWR) - stderrcopy = os.dup(sys.stderr.fileno()) - os.dup2(null, sys.stderr.fileno()) - os.close(null) - try: - try: - mandos_clients = mandos_serv.GetAllClientsWithProperties() - finally: - #restore stderr - os.dup2(stderrcopy, sys.stderr.fileno()) - os.close(stderrcopy) - except dbus.exceptions.DBusException, e: - print("Access denied: Accessing mandos server through dbus.", - file=sys.stderr) - sys.exit(1) - - # Compile dict of (clients: properties) to process - clients={} - - if options.all or not client_names: - clients = dict((bus.get_object(busname, path), properties) - for path, properties in - mandos_clients.iteritems()) - else: - for name in client_names: - for path, client in mandos_clients.iteritems(): - if client["Name"] == name: - client_objc = bus.get_object(busname, path) - clients[client_objc] = client - break - else: - print("Client not found on server: %r" % name, - file=sys.stderr) - sys.exit(1) - - if not has_actions(options) and clients: - if options.verbose: - keywords = ("Name", "Enabled", "Timeout", - "LastCheckedOK", "Created", "Interval", - "Host", "Fingerprint", "CheckerRunning", - "LastEnabled", "ApprovalPending", - "ApprovedByDefault", - "LastApprovalRequest", "ApprovalDelay", - "ApprovalDuration", "Checker") + parser = argparse.ArgumentParser() + parser.add_argument("--version", action="version", + version = "%%prog %s" % version, + help="show version number and exit") + parser.add_argument("-a", "--all", action="store_true", + help="Select all clients") + parser.add_argument("-v", "--verbose", action="store_true", + help="Print all fields") + parser.add_argument("-e", "--enable", action="store_true", + help="Enable client") + parser.add_argument("-d", "--disable", action="store_true", + help="disable client") + parser.add_argument("-b", "--bump-timeout", action="store_true", + help="Bump timeout for client") + parser.add_argument("--start-checker", action="store_true", + help="Start checker for client") + parser.add_argument("--stop-checker", action="store_true", + help="Stop checker for client") + parser.add_argument("-V", "--is-enabled", action="store_true", + help="Check if client is enabled") + parser.add_argument("-r", "--remove", action="store_true", + help="Remove client") + parser.add_argument("-c", "--checker", + help="Set checker command for client") + parser.add_argument("-t", "--timeout", + help="Set timeout for client") + parser.add_argument("-i", "--interval", + help="Set checker interval for client") + parser.add_argument("--approve-by-default", action="store_true", + default=None, dest="approved_by_default", + help="Set client to be approved by default") + parser.add_argument("--deny-by-default", action="store_false", + dest="approved_by_default", + help="Set client to be denied by default") + parser.add_argument("--approval-delay", + help="Set delay before client approve/deny") + parser.add_argument("--approval-duration", + help="Set duration of one client approval") + parser.add_argument("-H", "--host", help="Set host for client") + parser.add_argument("-s", "--secret", type=file, + help="Set password blob (file) for client") + parser.add_argument("-A", "--approve", action="store_true", + help="Approve any current client request") + parser.add_argument("-D", "--deny", action="store_true", + help="Deny any current client request") + parser.add_argument("client", nargs="*", help="Client name") + options = parser.parse_args() + + if has_actions(options) and not options.client and not options.all: + parser.error("Options require clients names or --all.") + if options.verbose and has_actions(options): + parser.error("--verbose can only be used alone or with" + " --all.") + if options.all and not has_actions(options): + parser.error("--all requires an action.") + + try: + bus = dbus.SystemBus() + mandos_dbus_objc = bus.get_object(busname, server_path) + except dbus.exceptions.DBusException: + print("Could not connect to Mandos server", + file=sys.stderr) + sys.exit(1) + + mandos_serv = dbus.Interface(mandos_dbus_objc, + dbus_interface = server_interface) + + #block stderr since dbus library prints to stderr + null = os.open(os.path.devnull, os.O_RDWR) + stderrcopy = os.dup(sys.stderr.fileno()) + os.dup2(null, sys.stderr.fileno()) + os.close(null) + try: + try: + mandos_clients = mandos_serv.GetAllClientsWithProperties() + finally: + #restore stderr + os.dup2(stderrcopy, sys.stderr.fileno()) + os.close(stderrcopy) + except dbus.exceptions.DBusException, e: + print("Access denied: Accessing mandos server through dbus.", + file=sys.stderr) + sys.exit(1) + + # Compile dict of (clients: properties) to process + clients={} + + if options.all or not options.client: + clients = dict((bus.get_object(busname, path), properties) + for path, properties in + mandos_clients.iteritems()) + else: + for name in options.client: + for path, client in mandos_clients.iteritems(): + if client["Name"] == name: + client_objc = bus.get_object(busname, path) + clients[client_objc] = client + break else: - keywords = defaultkeywords - - print_clients(clients.values(), keywords) + print("Client not found on server: %r" % name, + file=sys.stderr) + sys.exit(1) + + if not has_actions(options) and clients: + if options.verbose: + keywords = ("Name", "Enabled", "Timeout", + "LastCheckedOK", "Created", "Interval", + "Host", "Fingerprint", "CheckerRunning", + "LastEnabled", "ApprovalPending", + "ApprovedByDefault", + "LastApprovalRequest", "ApprovalDelay", + "ApprovalDuration", "Checker") else: - # Process each client in the list by all selected options - for client in clients: - if options.remove: - mandos_serv.RemoveClient(client.__dbus_object_path__) - if options.enable: - client.Enable(dbus_interface=client_interface) - if options.disable: - client.Disable(dbus_interface=client_interface) - if options.bump_timeout: - client.CheckedOK(dbus_interface=client_interface) - if options.start_checker: - client.StartChecker(dbus_interface=client_interface) - if options.stop_checker: - client.StopChecker(dbus_interface=client_interface) - if options.is_enabled: - sys.exit(0 if client.Get(client_interface, - "Enabled", - dbus_interface=dbus.PROPERTIES_IFACE) - else 1) - if options.checker: - client.Set(client_interface, "Checker", options.checker, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.host: - client.Set(client_interface, "Host", options.host, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.interval: - client.Set(client_interface, "Interval", - timedelta_to_milliseconds - (string_to_delta(options.interval)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_delay: - client.Set(client_interface, "ApprovalDelay", - timedelta_to_milliseconds - (string_to_delta(options. - approval_delay)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_duration: - client.Set(client_interface, "ApprovalDuration", - timedelta_to_milliseconds - (string_to_delta(options. - approval_duration)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.timeout: - client.Set(client_interface, "Timeout", - timedelta_to_milliseconds - (string_to_delta(options.timeout)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.secret: - client.Set(client_interface, "Secret", - dbus.ByteArray(open(options.secret, - "rb").read()), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approved_by_default is not None: - client.Set(client_interface, "ApprovedByDefault", - dbus.Boolean(options - .approved_by_default), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approve: - client.Approve(dbus.Boolean(True), - dbus_interface=client_interface) - elif options.deny: - client.Approve(dbus.Boolean(False), - dbus_interface=client_interface) + keywords = defaultkeywords + + print_clients(clients.values(), keywords) + else: + # Process each client in the list by all selected options + for client in clients: + if options.remove: + mandos_serv.RemoveClient(client.__dbus_object_path__) + if options.enable: + client.Enable(dbus_interface=client_interface) + if options.disable: + client.Disable(dbus_interface=client_interface) + if options.bump_timeout: + client.CheckedOK(dbus_interface=client_interface) + if options.start_checker: + client.StartChecker(dbus_interface=client_interface) + if options.stop_checker: + client.StopChecker(dbus_interface=client_interface) + if options.is_enabled: + sys.exit(0 if client.Get(client_interface, + "Enabled", + dbus_interface= + dbus.PROPERTIES_IFACE) + else 1) + if options.checker: + client.Set(client_interface, "Checker", + options.checker, + dbus_interface=dbus.PROPERTIES_IFACE) + if options.host: + client.Set(client_interface, "Host", options.host, + dbus_interface=dbus.PROPERTIES_IFACE) + if options.interval: + client.Set(client_interface, "Interval", + timedelta_to_milliseconds + (string_to_delta(options.interval)), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.approval_delay: + client.Set(client_interface, "ApprovalDelay", + timedelta_to_milliseconds + (string_to_delta(options. + approval_delay)), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.approval_duration: + client.Set(client_interface, "ApprovalDuration", + timedelta_to_milliseconds + (string_to_delta(options. + approval_duration)), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.timeout: + client.Set(client_interface, "Timeout", + timedelta_to_milliseconds + (string_to_delta(options.timeout)), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.secret: + client.Set(client_interface, "Secret", + dbus.ByteArray(open(options.secret, + "rb").read()), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.approved_by_default is not None: + client.Set(client_interface, "ApprovedByDefault", + dbus.Boolean(options + .approved_by_default), + dbus_interface=dbus.PROPERTIES_IFACE) + if options.approve: + client.Approve(dbus.Boolean(True), + dbus_interface=client_interface) + elif options.deny: + client.Approve(dbus.Boolean(False), + dbus_interface=client_interface) if __name__ == "__main__": main() === modified file 'mandos-monitor' --- mandos-monitor 2011-03-08 19:41:59 +0000 +++ mandos-monitor 2011-07-25 18:49:17 +0000 @@ -22,7 +22,8 @@ # Contact the authors at . # -from __future__ import division, absolute_import, print_function, unicode_literals +from __future__ import (division, absolute_import, print_function, + unicode_literals) import sys import os @@ -86,16 +87,17 @@ self.proxy = proxy_object # Mandos Client proxy object self.properties = dict() - self.proxy.connect_to_signal("PropertyChanged", - self.property_changed, - client_interface, - byte_arrays=True) + self.property_changed_match = ( + self.proxy.connect_to_signal("PropertyChanged", + self.property_changed, + client_interface, + byte_arrays=True)) self.properties.update( self.proxy.GetAll(client_interface, dbus_interface = dbus.PROPERTIES_IFACE)) - #XXX This break good super behaviour! + #XXX This breaks good super behaviour # super(MandosClientPropertyCache, self).__init__( # *args, **kwargs) @@ -105,6 +107,11 @@ """ # Update properties dict with new value self.properties[property] = value + + def delete(self, *args, **kwargs): + self.property_changed_match.remove() + super(MandosClientPropertyCache, self).__init__( + *args, **kwargs) class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache): @@ -154,26 +161,28 @@ if self.need_approval: self.using_timer(True) - self.proxy.connect_to_signal("CheckerCompleted", - self.checker_completed, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal("CheckerStarted", - self.checker_started, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal("GotSecret", - self.got_secret, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal("NeedApproval", - self.need_approval, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal("Rejected", - self.rejected, - client_interface, - byte_arrays=True) + self.match_objects = ( + self.proxy.connect_to_signal("CheckerCompleted", + self.checker_completed, + client_interface, + byte_arrays=True), + self.proxy.connect_to_signal("CheckerStarted", + self.checker_started, + client_interface, + byte_arrays=True), + self.proxy.connect_to_signal("GotSecret", + self.got_secret, + client_interface, + byte_arrays=True), + self.proxy.connect_to_signal("NeedApproval", + self.need_approval, + client_interface, + byte_arrays=True), + self.proxy.connect_to_signal("Rejected", + self.rejected, + client_interface, + byte_arrays=True)) + #self.logger('Created client %s' % (self.properties["Name"])) def property_changed(self, property=None, value=None): super(self, MandosClientWidget).property_changed(property, @@ -341,12 +350,16 @@ self.update() return True # Keep calling this - def delete(self): + def delete(self, *args, **kwargs): if self._update_timer_callback_tag is not None: gobject.source_remove(self._update_timer_callback_tag) self._update_timer_callback_tag = None + for match in self.match_objects: + match.remove() + self.match_objects = () if self.delete_hook is not None: self.delete_hook(self) + return super(MandosClientWidget, self).delete(*args, **kwargs) def render(self, maxcolrow, focus=False): """Render differently if we have focus. @@ -358,24 +371,32 @@ """Handle keys. This overrides the method from urwid.FlowWidget""" if key == "+": - self.proxy.Enable(dbus_interface = client_interface) + self.proxy.Enable(dbus_interface = client_interface, + ignore_reply=True) elif key == "-": - self.proxy.Disable(dbus_interface = client_interface) + self.proxy.Disable(dbus_interface = client_interface, + ignore_reply=True) elif key == "a": self.proxy.Approve(dbus.Boolean(True, variant_level=1), - dbus_interface = client_interface) + dbus_interface = client_interface, + ignore_reply=True) elif key == "d": self.proxy.Approve(dbus.Boolean(False, variant_level=1), - dbus_interface = client_interface) + dbus_interface = client_interface, + ignore_reply=True) elif key == "R" or key == "_" or key == "ctrl k": self.server_proxy_object.RemoveClient(self.proxy - .object_path) + .object_path, + ignore_reply=True) elif key == "s": - self.proxy.StartChecker(dbus_interface = client_interface) + self.proxy.StartChecker(dbus_interface = client_interface, + ignore_reply=True) elif key == "S": - self.proxy.StopChecker(dbus_interface = client_interface) + self.proxy.StopChecker(dbus_interface = client_interface, + ignore_reply=True) elif key == "C": - self.proxy.CheckedOK(dbus_interface = client_interface) + self.proxy.CheckedOK(dbus_interface = client_interface, + ignore_reply=True) # xxx # elif key == "p" or key == "=": # self.proxy.pause() @@ -572,7 +593,7 @@ #self.log_message("Wrap mode: " + self.log_wrap) def find_and_remove_client(self, path, name): - """Find an client from its object path and remove it. + """Find a client by its object path and remove it. This is connected to the ClientRemoved signal from the Mandos server object.""" @@ -580,8 +601,10 @@ client = self.clients_dict[path] except KeyError: # not found? + self.log_message("Unknown client %r (%r) removed", name, + path) return - self.remove_client(client, path) + client.delete() def add_new_client(self, path): client_proxy_object = self.bus.get_object(self.busname, path) === modified file 'plugin-runner.c' --- plugin-runner.c 2010-09-26 21:27:28 +0000 +++ plugin-runner.c 2011-07-16 00:29:19 +0000 @@ -742,7 +742,29 @@ } } - /* Strip permissions down to nobody */ + { + /* Work around Debian bug #633582: + */ + int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY); + if(plugindir_fd == -1){ + error(0, errno, "open"); + } else { + ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st)); + if(ret == -1){ + error(0, errno, "fstat"); + } else { + if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){ + ret = fchown(plugindir_fd, uid, gid); + if(ret == -1){ + error(0, errno, "fchown"); + } + } + } + TEMP_FAILURE_RETRY(close(plugindir_fd)); + } + } + + /* Lower permissions */ setgid(gid); if(ret == -1){ error(0, errno, "setgid"); === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2011-02-27 17:26:35 +0000 +++ plugins.d/askpass-fifo.c 2011-07-13 02:24:39 +0000 @@ -32,6 +32,8 @@ ENFILE, ENOMEM, EBADF, EINVAL, EIO, EISDIR, EFBIG */ #include /* error() */ +#include /* fprintf(), vfprintf(), + vasprintf() */ #include /* EXIT_FAILURE, NULL, size_t, free(), realloc(), EXIT_SUCCESS */ #include /* open(), O_RDONLY */ @@ -39,7 +41,32 @@ STDOUT_FILENO */ #include /* EX_OSERR, EX_OSFILE, EX_UNAVAILABLE, EX_IOERR */ - +#include /* strerror() */ +#include /* va_list, va_start(), ... */ + + +/* Function to use when printing errors */ +void error_plus(int status, int errnum, const char *formatstring, + ...){ + va_list ap; + char *text; + int ret; + + va_start(ap, formatstring); + ret = vasprintf(&text, formatstring, ap); + if (ret == -1){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + vfprintf(stderr, formatstring, ap); + fprintf(stderr, ": "); + fprintf(stderr, "%s\n", strerror(errnum)); + error(status, errno, "vasprintf while printing error"); + return; + } + fprintf(stderr, "Mandos plugin "); + error(status, errnum, "%s", text); + free(text); +} int main(__attribute__((unused))int argc, __attribute__((unused))char **argv){ @@ -51,19 +78,19 @@ ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); if(ret == -1){ int e = errno; - error(0, errno, "mkfifo"); switch(e){ case EACCES: case ENOTDIR: case ELOOP: - return EX_OSFILE; + error_plus(EX_OSFILE, errno, "mkfifo"); case ENAMETOOLONG: case ENOSPC: case EROFS: default: - return EX_OSERR; + error_plus(EX_OSERR, errno, "mkfifo"); case ENOENT: - return EX_UNAVAILABLE; /* no "/lib/cryptsetup"? */ + /* no "/lib/cryptsetup"? */ + error_plus(EX_UNAVAILABLE, errno, "mkfifo"); case EEXIST: break; /* not an error */ } @@ -73,7 +100,7 @@ int fifo_fd = open(passfifo, O_RDONLY); if(fifo_fd == -1){ int e = errno; - error(0, errno, "open"); + error_plus(0, errno, "open"); switch(e){ case EACCES: case ENOENT: @@ -101,7 +128,7 @@ if(buf_len + blocksize > buf_allocated){ char *tmp = realloc(buf, buf_allocated + blocksize); if(tmp == NULL){ - error(0, errno, "realloc"); + error_plus(0, errno, "realloc"); free(buf); return EX_OSERR; } @@ -113,7 +140,7 @@ int e = errno; free(buf); errno = e; - error(0, errno, "read"); + error_plus(0, errno, "read"); switch(e){ case EBADF: case EFAULT: @@ -141,7 +168,7 @@ int e = errno; free(buf); errno = e; - error(0, errno, "write"); + error_plus(0, errno, "write"); switch(e){ case EBADF: case EFAULT: @@ -161,7 +188,7 @@ ret = close(STDOUT_FILENO); if(ret == -1){ int e = errno; - error(0, errno, "close"); + error_plus(0, errno, "close"); switch(e){ case EBADF: return EX_OSFILE; === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2011-02-27 17:26:35 +0000 +++ plugins.d/mandos-client.c 2011-07-16 00:29:19 +0000 @@ -62,7 +62,8 @@ #include /* PRIu16, PRIdMAX, intmax_t, strtoimax() */ #include /* assert() */ -#include /* perror(), errno */ +#include /* perror(), errno, + program_invocation_short_name */ #include /* nanosleep(), time(), sleep() */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS, if_indextoname(), @@ -73,7 +74,7 @@ #include /* close(), SEEK_SET, off_t, write(), getuid(), getgid(), seteuid(), setgid(), pause() */ -#include /* inet_pton(), htons */ +#include /* inet_pton(), htons, inet_ntop() */ #include /* not, or, and */ #include /* struct argp_option, error_t, struct argp_state, struct argp, @@ -130,6 +131,17 @@ static const char sys_class_net[] = "/sys/class/net"; char *connect_to = NULL; +/* Doubly linked list that need to be circularly linked when used */ +typedef struct server{ + const char *ip; + uint16_t port; + AvahiIfIndex if_index; + int af; + struct timespec last_seen; + struct server *next; + struct server *prev; +} server; + /* Used for passing in values through the Avahi callback functions */ typedef struct { AvahiSimplePoll *simple_poll; @@ -139,16 +151,25 @@ gnutls_dh_params_t dh_params; const char *priority; gpgme_ctx_t ctx; + server *current_server; } mandos_context; /* global context so signal handler can reach it*/ mandos_context mc = { .simple_poll = NULL, .server = NULL, .dh_bits = 1024, .priority = "SECURE256" - ":!CTYPE-X.509:+CTYPE-OPENPGP" }; + ":!CTYPE-X.509:+CTYPE-OPENPGP", + .current_server = NULL }; sig_atomic_t quit_now = 0; int signal_received = 0; +/* Function to use when printing errors */ +void perror_plus(const char *print_text){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + perror(print_text); +} + /* * Make additional room in "buffer" for at least BUFFER_SIZE more * bytes. "buffer_capacity" is how much is currently allocated, @@ -166,6 +187,43 @@ return buffer_capacity; } +int add_server(const char *ip, uint16_t port, + AvahiIfIndex if_index, + int af){ + int ret; + server *new_server = malloc(sizeof(server)); + if(new_server == NULL){ + perror_plus("malloc"); + return -1; + } + *new_server = (server){ .ip = strdup(ip), + .port = port, + .if_index = if_index, + .af = af }; + if(new_server->ip == NULL){ + perror_plus("strdup"); + return -1; + } + /* unique case of first server */ + if (mc.current_server == NULL){ + new_server->next = new_server; + new_server->prev = new_server; + mc.current_server = new_server; + /* Placing the new server last in the list */ + } else { + new_server->next = mc.current_server; + new_server->prev = mc.current_server->prev; + new_server->prev->next = new_server; + mc.current_server->prev = new_server; + } + ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen); + if(ret == -1){ + perror_plus("clock_gettime"); + return -1; + } + return 0; +} + /* * Initialize GPGME. */ @@ -185,7 +243,7 @@ fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); if(fd == -1){ - perror("open"); + perror_plus("open"); return false; } @@ -205,7 +263,7 @@ ret = (int)TEMP_FAILURE_RETRY(close(fd)); if(ret == -1){ - perror("close"); + perror_plus("close"); } gpgme_data_release(pgp_data); return true; @@ -336,7 +394,7 @@ /* Seek back to the beginning of the GPGME plaintext data buffer */ if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){ - perror("gpgme_data_seek"); + perror_plus("gpgme_data_seek"); plaintext_length = -1; goto decrypt_end; } @@ -347,7 +405,7 @@ (size_t)plaintext_length, plaintext_capacity); if(plaintext_capacity == 0){ - perror("incbuffer"); + perror_plus("incbuffer"); plaintext_length = -1; goto decrypt_end; } @@ -360,7 +418,7 @@ break; } if(ret < 0){ - perror("gpgme_data_read"); + perror_plus("gpgme_data_read"); plaintext_length = -1; goto decrypt_end; } @@ -423,12 +481,9 @@ } /* OpenPGP credentials */ - gnutls_certificate_allocate_credentials(&mc.cred); + ret = gnutls_certificate_allocate_credentials(&mc.cred); if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning - from - -Wunreachable-code - */ + fprintf(stderr, "GnuTLS memory error: %s\n", safer_gnutls_strerror(ret)); gnutls_global_deinit(); return -1; @@ -589,7 +644,7 @@ tcp_sd = socket(pf, SOCK_STREAM, 0); if(tcp_sd < 0){ int e = errno; - perror("socket"); + perror_plus("socket"); errno = e; goto mandos_end; } @@ -609,7 +664,7 @@ } if(ret < 0 ){ int e = errno; - perror("inet_pton"); + perror_plus("inet_pton"); errno = e; goto mandos_end; } @@ -651,7 +706,7 @@ if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){ char interface[IF_NAMESIZE]; if(if_indextoname((unsigned int)if_index, interface) == NULL){ - perror("if_indextoname"); + perror_plus("if_indextoname"); } else { fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n", ip, interface, port); @@ -671,7 +726,7 @@ sizeof(addrstr)); } if(pcret == NULL){ - perror("inet_ntop"); + perror_plus("inet_ntop"); } else { if(strcmp(addrstr, ip) != 0){ fprintf(stderr, "Canonical address form: %s\n", addrstr); @@ -692,7 +747,7 @@ if(ret < 0){ if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){ int e = errno; - perror("connect"); + perror_plus("connect"); errno = e; } goto mandos_end; @@ -711,7 +766,7 @@ out_size - written)); if(ret == -1){ int e = errno; - perror("write"); + perror_plus("write"); errno = e; goto mandos_end; } @@ -742,6 +797,7 @@ goto mandos_end; } + /* Spurious warning from -Wint-to-pointer-cast */ gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd); if(quit_now){ @@ -784,7 +840,7 @@ buffer_capacity); if(buffer_capacity == 0){ int e = errno; - perror("incbuffer"); + perror_plus("incbuffer"); errno = e; goto mandos_end; } @@ -895,14 +951,14 @@ if(e == 0){ e = errno; } - perror("close"); + perror_plus("close"); } gnutls_deinit(session); + errno = e; if(quit_now){ - e = EINTR; + errno = EINTR; retval = -1; } - errno = e; } return retval; } @@ -951,6 +1007,9 @@ avahi_proto_to_af(proto)); if(ret == 0){ avahi_simple_poll_quit(mc.simple_poll); + } else { + ret = add_server(ip, port, interface, + avahi_proto_to_af(proto)); } } } @@ -1010,7 +1069,7 @@ } } -/* stop main loop after sigterm has been called */ +/* Signal handler that stops main loop after SIGTERM */ static void handle_sigterm(int sig){ if(quit_now){ return; @@ -1018,6 +1077,7 @@ quit_now = 1; signal_received = sig; int old_errno = errno; + /* set main loop to exit */ if(mc.simple_poll != NULL){ avahi_simple_poll_quit(mc.simple_poll); } @@ -1032,27 +1092,29 @@ int good_interface(const struct dirent *if_entry){ ssize_t ssret; char *flagname = NULL; + if(if_entry->d_name[0] == '.'){ + return 0; + } int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net, if_entry->d_name); if(ret < 0){ - perror("asprintf"); - return 0; - } - if(if_entry->d_name[0] == '.'){ + perror_plus("asprintf"); return 0; } int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY)); if(flags_fd == -1){ - perror("open"); + perror_plus("open"); + free(flagname); return 0; } + free(flagname); typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */ /* read line from flags_fd */ - ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */ + ssize_t to_read = 2+(sizeof(ifreq_flags)*2)+1; /* "0x1003\n" */ char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */ flagstring[(size_t)to_read] = '\0'; if(flagstring == NULL){ - perror("malloc"); + perror_plus("malloc"); close(flags_fd); return 0; } @@ -1060,7 +1122,7 @@ ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring, (size_t)to_read)); if(ssret == -1){ - perror("read"); + perror_plus("read"); free(flagstring); close(flags_fd); return 0; @@ -1111,6 +1173,14 @@ } return 0; } + /* Reject non-ARP interfaces (including dummy interfaces) */ + if(flags & IFF_NOARP){ + if(debug){ + fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n", + if_entry->d_name); + } + return 0; + } /* Accept this device */ if(debug){ fprintf(stderr, "Interface \"%s\" is acceptable\n", @@ -1119,6 +1189,88 @@ return 1; } +int notdotentries(const struct dirent *direntry){ + /* Skip "." and ".." */ + if(direntry->d_name[0] == '.' + and (direntry->d_name[1] == '\0' + or (direntry->d_name[1] == '.' + and direntry->d_name[2] == '\0'))){ + return 0; + } + return 1; +} + +int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){ + int ret; + struct timespec now; + struct timespec waited_time; + intmax_t block_time; + + while(true){ + if(mc.current_server == NULL){ + if (debug){ + fprintf(stderr, + "Wait until first server is found. No timeout!\n"); + } + ret = avahi_simple_poll_iterate(s, -1); + } else { + if (debug){ + fprintf(stderr, "Check current_server if we should run it," + " or wait\n"); + } + /* the current time */ + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if(ret == -1){ + perror_plus("clock_gettime"); + return -1; + } + /* Calculating in ms how long time between now and server + who we visted longest time ago. Now - last seen. */ + waited_time.tv_sec = (now.tv_sec + - mc.current_server->last_seen.tv_sec); + waited_time.tv_nsec = (now.tv_nsec + - mc.current_server->last_seen.tv_nsec); + /* total time is 10s/10,000ms. + Converting to s from ms by dividing by 1,000, + and ns to ms by dividing by 1,000,000. */ + block_time = ((retry_interval + - ((intmax_t)waited_time.tv_sec * 1000)) + - ((intmax_t)waited_time.tv_nsec / 1000000)); + + if (debug){ + fprintf(stderr, "Blocking for %ld ms\n", block_time); + } + + if(block_time <= 0){ + ret = start_mandos_communication(mc.current_server->ip, + mc.current_server->port, + mc.current_server->if_index, + mc.current_server->af); + if(ret == 0){ + avahi_simple_poll_quit(mc.simple_poll); + return 0; + } + ret = clock_gettime(CLOCK_MONOTONIC, + &mc.current_server->last_seen); + if(ret == -1){ + perror_plus("clock_gettime"); + return -1; + } + mc.current_server = mc.current_server->next; + block_time = 0; /* Call avahi to find new Mandos + servers, but don't block */ + } + + ret = avahi_simple_poll_iterate(s, (int)block_time); + } + if(ret != 0){ + if (ret > 0 or errno != EINTR) { + return (ret != 1) ? ret : 0; + } + } + } +} + int main(int argc, char *argv[]){ AvahiSServiceBrowser *sb = NULL; int error; @@ -1141,6 +1293,8 @@ bool gnutls_initialized = false; bool gpgme_initialized = false; float delay = 2.5f; + double retry_interval = 10; /* 10s between trying a server and + retrying the same server again */ struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL }; struct sigaction sigterm_action = { .sa_handler = handle_sigterm }; @@ -1152,14 +1306,14 @@ errno = 0; ret = setgid(gid); if(ret == -1){ - perror("setgid"); + perror_plus("setgid"); } /* Lower user privileges (temporarily) */ errno = 0; ret = seteuid(uid); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } if(quit_now){ @@ -1200,6 +1354,10 @@ .arg = "SECONDS", .doc = "Maximum delay to wait for interface startup", .group = 2 }, + { .name = "retry", .key = 132, + .arg = "SECONDS", + .doc = "Retry interval used when denied by the mandos server", + .group = 2 }, /* * These reproduce what we would get without ARGP_NO_HELP */ @@ -1249,6 +1407,13 @@ if(errno != 0 or tmp == arg or *tmp != '\0'){ argp_error(state, "Bad delay"); } + case 132: /* --retry */ + errno = 0; + retry_interval = strtod(arg, &tmp); + if(errno != 0 or tmp == arg or *tmp != '\0' + or (retry_interval * 1000) > INT_MAX){ + argp_error(state, "Bad retry interval"); + } break; /* * These reproduce what we would get without ARGP_NO_HELP @@ -1282,7 +1447,7 @@ case ENOMEM: default: errno = ret; - perror("argp_parse"); + perror_plus("argp_parse"); exitcode = EX_OSERR; goto end; case EINVAL: @@ -1290,6 +1455,62 @@ goto end; } } + + { + /* Work around Debian bug #633582: + */ + struct stat st; + + /* Re-raise priviliges */ + errno = 0; + ret = seteuid(0); + if(ret == -1){ + perror_plus("seteuid"); + } + + int seckey_fd = open(PATHDIR "/" SECKEY, O_RDONLY); + if(seckey_fd == -1){ + perror_plus("open"); + } else { + ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st)); + if(ret == -1){ + perror_plus("fstat"); + } else { + if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){ + ret = fchown(seckey_fd, uid, gid); + if(ret == -1){ + perror_plus("fchown"); + } + } + } + TEMP_FAILURE_RETRY(close(seckey_fd)); + } + + int pubkey_fd = open(PATHDIR "/" PUBKEY, O_RDONLY); + if(pubkey_fd == -1){ + perror_plus("open"); + } else { + ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st)); + if(ret == -1){ + perror_plus("fstat"); + } else { + if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){ + ret = fchown(pubkey_fd, uid, gid); + if(ret == -1){ + perror_plus("fchown"); + } + } + } + TEMP_FAILURE_RETRY(close(pubkey_fd)); + } + + /* Lower privileges */ + errno = 0; + ret = seteuid(uid); + if(ret == -1){ + perror_plus("seteuid"); + } + } if(not debug){ avahi_set_log_function(empty_log); @@ -1306,7 +1527,7 @@ fprintf(stderr, "Using interface \"%s\"\n", interface); } if(interface == NULL){ - perror("malloc"); + perror_plus("malloc"); free(direntries); exitcode = EXIT_FAILURE; goto end; @@ -1334,19 +1555,19 @@ sigemptyset(&sigterm_action.sa_mask); ret = sigaddset(&sigterm_action.sa_mask, SIGINT); if(ret == -1){ - perror("sigaddset"); + perror_plus("sigaddset"); exitcode = EX_OSERR; goto end; } ret = sigaddset(&sigterm_action.sa_mask, SIGHUP); if(ret == -1){ - perror("sigaddset"); + perror_plus("sigaddset"); exitcode = EX_OSERR; goto end; } ret = sigaddset(&sigterm_action.sa_mask, SIGTERM); if(ret == -1){ - perror("sigaddset"); + perror_plus("sigaddset"); exitcode = EX_OSERR; goto end; } @@ -1356,39 +1577,39 @@ */ ret = sigaction(SIGINT, NULL, &old_sigterm_action); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); return EX_OSERR; } if(old_sigterm_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &sigterm_action, NULL); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); exitcode = EX_OSERR; goto end; } } ret = sigaction(SIGHUP, NULL, &old_sigterm_action); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); return EX_OSERR; } if(old_sigterm_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &sigterm_action, NULL); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); exitcode = EX_OSERR; goto end; } } ret = sigaction(SIGTERM, NULL, &old_sigterm_action); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); return EX_OSERR; } if(old_sigterm_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &sigterm_action, NULL); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); exitcode = EX_OSERR; goto end; } @@ -1411,7 +1632,7 @@ errno = 0; ret = seteuid(0); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } #ifdef __linux__ @@ -1421,19 +1642,19 @@ bool restore_loglevel = true; if(ret == -1){ restore_loglevel = false; - perror("klogctl"); + perror_plus("klogctl"); } #endif /* __linux__ */ sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); if(sd < 0){ - perror("socket"); + perror_plus("socket"); exitcode = EX_OSERR; #ifdef __linux__ if(restore_loglevel){ ret = klogctl(7, NULL, 0); if(ret == -1){ - perror("klogctl"); + perror_plus("klogctl"); } } #endif /* __linux__ */ @@ -1441,19 +1662,19 @@ errno = 0; ret = seteuid(uid); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } goto end; } strcpy(network.ifr_name, interface); ret = ioctl(sd, SIOCGIFFLAGS, &network); if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); + perror_plus("ioctl SIOCGIFFLAGS"); #ifdef __linux__ if(restore_loglevel){ ret = klogctl(7, NULL, 0); if(ret == -1){ - perror("klogctl"); + perror_plus("klogctl"); } } #endif /* __linux__ */ @@ -1462,7 +1683,7 @@ errno = 0; ret = seteuid(uid); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } goto end; } @@ -1472,13 +1693,13 @@ ret = ioctl(sd, SIOCSIFFLAGS, &network); if(ret == -1){ take_down_interface = false; - perror("ioctl SIOCSIFFLAGS +IFF_UP"); + perror_plus("ioctl SIOCSIFFLAGS +IFF_UP"); exitcode = EX_OSERR; #ifdef __linux__ if(restore_loglevel){ ret = klogctl(7, NULL, 0); if(ret == -1){ - perror("klogctl"); + perror_plus("klogctl"); } } #endif /* __linux__ */ @@ -1486,30 +1707,31 @@ errno = 0; ret = seteuid(uid); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } goto end; } } - /* sleep checking until interface is running */ + /* Sleep checking until interface is running. + Check every 0.25s, up to total time of delay */ for(int i=0; i < delay * 4; i++){ ret = ioctl(sd, SIOCGIFFLAGS, &network); if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); + perror_plus("ioctl SIOCGIFFLAGS"); } else if(network.ifr_flags & IFF_RUNNING){ break; } struct timespec sleeptime = { .tv_nsec = 250000000 }; ret = nanosleep(&sleeptime, NULL); if(ret == -1 and errno != EINTR){ - perror("nanosleep"); + perror_plus("nanosleep"); } } if(not take_down_interface){ /* We won't need the socket anymore */ ret = (int)TEMP_FAILURE_RETRY(close(sd)); if(ret == -1){ - perror("close"); + perror_plus("close"); } } #ifdef __linux__ @@ -1517,7 +1739,7 @@ /* Restores kernel loglevel to default */ ret = klogctl(7, NULL, 0); if(ret == -1){ - perror("klogctl"); + perror_plus("klogctl"); } } #endif /* __linux__ */ @@ -1527,13 +1749,13 @@ /* Lower privileges */ ret = seteuid(uid); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } } else { /* Lower privileges permanently */ ret = setuid(uid); if(ret == -1){ - perror("setuid"); + perror_plus("setuid"); } } } @@ -1555,12 +1777,11 @@ goto end; } + if(mkdtemp(tempdir) == NULL){ + perror_plus("mkdtemp"); + goto end; + } tempdir_created = true; - if(mkdtemp(tempdir) == NULL){ - tempdir_created = false; - perror("mkdtemp"); - goto end; - } if(quit_now){ goto end; @@ -1626,7 +1847,7 @@ if(quit_now or ret == 0){ break; } - sleep(15); + sleep((int)retry_interval or 1); }; if (not quit_now){ @@ -1690,8 +1911,13 @@ if(debug){ fprintf(stderr, "Starting Avahi loop search\n"); } - - avahi_simple_poll_loop(mc.simple_poll); + + ret = avahi_loop_with_timeout(mc.simple_poll, + (int)(retry_interval * 1000)); + if(debug){ + fprintf(stderr, "avahi_loop_with_timeout exited %s\n", + (ret == 0) ? "successfully" : "with error"); + } end: @@ -1718,6 +1944,17 @@ if(gpgme_initialized){ gpgme_release(mc.ctx); } + + /* Cleans up the circular linked list of Mandos servers the client + has seen */ + if(mc.current_server != NULL){ + mc.current_server->prev->next = NULL; + while(mc.current_server != NULL){ + server *next = mc.current_server->next; + free(mc.current_server); + mc.current_server = next; + } + } /* Take down the network interface */ if(take_down_interface){ @@ -1725,59 +1962,45 @@ errno = 0; ret = seteuid(0); if(ret == -1){ - perror("seteuid"); + perror_plus("seteuid"); } if(geteuid() == 0){ ret = ioctl(sd, SIOCGIFFLAGS, &network); if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); + perror_plus("ioctl SIOCGIFFLAGS"); } else if(network.ifr_flags & IFF_UP) { network.ifr_flags &= ~(short)IFF_UP; /* clear flag */ ret = ioctl(sd, SIOCSIFFLAGS, &network); if(ret == -1){ - perror("ioctl SIOCSIFFLAGS -IFF_UP"); + perror_plus("ioctl SIOCSIFFLAGS -IFF_UP"); } } ret = (int)TEMP_FAILURE_RETRY(close(sd)); if(ret == -1){ - perror("close"); + perror_plus("close"); } /* Lower privileges permanently */ errno = 0; ret = setuid(uid); if(ret == -1){ - perror("setuid"); + perror_plus("setuid"); } } } - /* Removes the temp directory used by GPGME */ + /* Removes the GPGME temp directory and all files inside */ if(tempdir_created){ - DIR *d; - struct dirent *direntry; - d = opendir(tempdir); - if(d == NULL){ - if(errno != ENOENT){ - perror("opendir"); - } - } else { - while(true){ - direntry = readdir(d); - if(direntry == NULL){ - break; - } - /* Skip "." and ".." */ - if(direntry->d_name[0] == '.' - and (direntry->d_name[1] == '\0' - or (direntry->d_name[1] == '.' - and direntry->d_name[2] == '\0'))){ - continue; - } + struct dirent **direntries = NULL; + struct dirent *direntry = NULL; + ret = scandir(tempdir, &direntries, notdotentries, alphasort); + if (ret > 0){ + for(int i = 0; i < ret; i++){ + direntry = direntries[i]; char *fullname = NULL; ret = asprintf(&fullname, "%s/%s", tempdir, direntry->d_name); if(ret < 0){ - perror("asprintf"); + perror_plus("asprintf"); continue; } ret = remove(fullname); @@ -1787,11 +2010,17 @@ } free(fullname); } - closedir(d); + } + + /* need to be cleaned even if ret == 0 because man page doesn't + specify */ + free(direntries); + if (ret == -1){ + perror_plus("scandir"); } ret = rmdir(tempdir); if(ret == -1 and errno != ENOENT){ - perror("rmdir"); + perror_plus("rmdir"); } } @@ -1802,13 +2031,13 @@ &old_sigterm_action, NULL)); if(ret == -1){ - perror("sigaction"); + perror_plus("sigaction"); } do { ret = raise(signal_received); } while(ret != 0 and errno == EINTR); if(ret != 0){ - perror("raise"); + perror_plus("raise"); abort(); } TEMP_FAILURE_RETRY(pause()); === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2010-09-26 18:32:58 +0000 +++ plugins.d/mandos-client.xml 2011-06-23 22:27:15 +0000 @@ -97,6 +97,10 @@ + + + + @@ -293,6 +297,19 @@ + + + + + + All Mandos servers servers are tried repeatedly until a + password is received. This value specifies, in seconds, + how long between each successive try for the + same server. The default is 10 seconds. + + + === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2011-03-08 11:08:35 +0000 +++ plugins.d/password-prompt.c 2011-07-25 18:47:45 +0000 @@ -41,8 +41,8 @@ getenv(), free() */ #include /* scandir(), alphasort() */ #include /* fprintf(), stderr, getline(), - stdin, feof(), fputc() - */ + stdin, feof(), fputc(), vfprintf(), + vasprintf() */ #include /* errno, EBADF, ENOTTY, EINVAL, EFAULT, EFBIG, EIO, ENOSPC, EINTR */ @@ -51,7 +51,8 @@ #include /* bool, false, true */ #include /* strtoumax() */ #include /* struct stat, lstat(), open() */ -#include /* strlen, rindex, memcmp */ +#include /* strlen, rindex, memcmp, strerror() + */ #include /* struct argp_option, struct argp_state, struct argp, argp_parse(), error_t, @@ -60,6 +61,7 @@ #include /* EX_SOFTWARE, EX_OSERR, EX_UNAVAILABLE, EX_IOERR, EX_OK */ #include /* open() */ +#include /* va_list, va_start(), ... */ volatile sig_atomic_t quit_now = 0; int signal_received; @@ -70,6 +72,29 @@ /* Needed for conflict resolution */ const char plymouth_name[] = "plymouthd"; +/* Function to use when printing errors */ +void error_plus(int status, int errnum, const char *formatstring, + ...){ + va_list ap; + char *text; + int ret; + + va_start(ap, formatstring); + ret = vasprintf(&text, formatstring, ap); + if (ret == -1){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + vfprintf(stderr, formatstring, ap); + fprintf(stderr, ": "); + fprintf(stderr, "%s\n", strerror(errnum)); + error(status, errno, "vasprintf while printing error"); + return; + } + fprintf(stderr, "Mandos plugin "); + error(status, errnum, "%s", text); + free(text); +} + static void termination_handler(int signum){ if(quit_now){ return; @@ -111,7 +136,9 @@ cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); if(cl_fd == -1){ - error(0, errno, "open"); + if(errno != ENOENT){ + error(0, errno, "open"); + } return 0; } @@ -181,12 +208,13 @@ return 1; } - struct dirent **direntries; + struct dirent **direntries = NULL; int ret; ret = scandir("/proc", &direntries, is_plymouth, alphasort); if (ret == -1){ error(1, errno, "scandir"); } + free(dirent); return ret > 0; } === modified file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2011-02-27 17:26:35 +0000 +++ plugins.d/plymouth.c 2011-07-13 02:24:39 +0000 @@ -36,7 +36,8 @@ #include /* NULL */ #include /* strchr(), memcmp() */ #include /* asprintf(), perror(), fopen(), - fscanf() */ + fscanf(), vasprintf(), fprintf(), + vfprintf() */ #include /* close(), readlink(), read(), fork(), setsid(), chdir(), dup2(), STDERR_FILENO, execv(), access() */ @@ -50,6 +51,7 @@ #include /* error() */ #include /* TEMP_FAILURE_RETRY */ #include /* argz_count(), argz_extract() */ +#include /* va_list, va_start(), ... */ sig_atomic_t interrupted_by_signal = 0; const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid"; @@ -70,6 +72,29 @@ interrupted_by_signal = 1; } +/* Function to use when printing errors */ +void error_plus(int status, int errnum, const char *formatstring, + ...){ + va_list ap; + char *text; + int ret; + + va_start(ap, formatstring); + ret = vasprintf(&text, formatstring, ap); + if (ret == -1){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + vfprintf(stderr, formatstring, ap); + fprintf(stderr, ": "); + fprintf(stderr, "%s\n", strerror(errnum)); + error(status, errno, "vasprintf while printing error"); + return; + } + fprintf(stderr, "Mandos plugin "); + error(status, errnum, "%s", text); + free(text); +} + /* Create prompt string */ char *makeprompt(void){ int ret = 0; @@ -109,18 +134,18 @@ bool become_a_daemon(void){ int ret = setuid(geteuid()); if(ret == -1){ - error(0, errno, "setuid"); + error_plus(0, errno, "setuid"); } setsid(); ret = chdir("/"); if(ret == -1){ - error(0, errno, "chdir"); + error_plus(0, errno, "chdir"); return false; } ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ - error(0, errno, "dup2"); + error_plus(0, errno, "dup2"); return false; } return true; @@ -134,7 +159,7 @@ pid_t pid; pid = fork(); if(pid == -1){ - error(0, errno, "fork"); + error_plus(0, errno, "fork"); return false; } if(pid == 0){ @@ -151,7 +176,7 @@ for (; argv[i]!=NULL; i++){ tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1)); if (tmp == NULL){ - error(0, errno, "realloc"); + error_plus(0, errno, "realloc"); free(new_argv); _exit(EX_OSERR); } @@ -161,7 +186,7 @@ new_argv[i] = NULL; execv(path, (char *const *)new_argv); - error(0, errno, "execv"); + error_plus(0, errno, "execv"); _exit(EXIT_FAILURE); } if(pid_return != NULL){ @@ -176,7 +201,7 @@ return false; } if(ret == -1){ - error(0, errno, "waitpid"); + error_plus(0, errno, "waitpid"); return false; } if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){ @@ -202,7 +227,7 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name); if(ret == -1){ - error(0, errno, "asprintf"); + error_plus(0, errno, "asprintf"); return 0; } @@ -211,7 +236,7 @@ if(ret == -1){ free(exe_link); if(errno != ENOENT){ - error(0, errno, "lstat"); + error_plus(0, errno, "lstat"); } return 0; } @@ -245,17 +270,20 @@ fclose(pidfile); } if(maxvalue == 0){ - struct dirent **direntries; + struct dirent **direntries = NULL; ret = scandir("/proc", &direntries, is_plymouth, alphasort); if (ret == -1){ - error(0, errno, "scandir"); + error_plus(0, errno, "scandir"); } if (ret > 0){ ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue); if (ret < 0){ - error(0, errno, "sscanf"); + error_plus(0, errno, "sscanf"); } } + /* scandir might preallocate for this variable (man page unclear). + even if ret == 0, therefore we need to free it. */ + free(direntries); } pid_t pid; pid = (pid_t)maxvalue; @@ -275,7 +303,7 @@ ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline", (uintmax_t)pid); if(ret == -1){ - error(0, errno, "asprintf"); + error_plus(0, errno, "asprintf"); return NULL; } @@ -283,7 +311,7 @@ cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); if(cl_fd == -1){ - error(0, errno, "open"); + error_plus(0, errno, "open"); return NULL; } @@ -297,7 +325,7 @@ if(cmdline_len + blocksize > cmdline_allocated){ tmp = realloc(cmdline, cmdline_allocated + blocksize); if(tmp == NULL){ - error(0, errno, "realloc"); + error_plus(0, errno, "realloc"); free(cmdline); close(cl_fd); return NULL; @@ -310,7 +338,7 @@ sret = read(cl_fd, cmdline + cmdline_len, cmdline_allocated - cmdline_len); if(sret == -1){ - error(0, errno, "read"); + error_plus(0, errno, "read"); free(cmdline); close(cl_fd); return NULL; @@ -319,7 +347,7 @@ } while(sret != 0); ret = close(cl_fd); if(ret == -1){ - error(0, errno, "close"); + error_plus(0, errno, "close"); free(cmdline); return NULL; } @@ -328,7 +356,7 @@ char **argv = malloc((argz_count(cmdline, cmdline_len) + 1) * sizeof(char *)); /* Get number of args */ if(argv == NULL){ - error(0, errno, "argv = malloc()"); + error_plus(0, errno, "argv = malloc()"); free(cmdline); return NULL; } @@ -361,16 +389,16 @@ *sig != 0; sig++){ ret = sigaddset(&new_action.sa_mask, *sig); if(ret == -1){ - error(EX_OSERR, errno, "sigaddset"); + error_plus(EX_OSERR, errno, "sigaddset"); } ret = sigaction(*sig, NULL, &old_action); if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); + error_plus(EX_OSERR, errno, "sigaction"); } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(*sig, &new_action, NULL); if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); + error_plus(EX_OSERR, errno, "sigaction"); } } } @@ -395,7 +423,7 @@ ret = asprintf(&prompt_arg, "--prompt=%s", prompt); free(prompt); if(ret == -1){ - error(EX_OSERR, errno, "asprintf"); + error_plus(EX_OSERR, errno, "asprintf"); } /* plymouth ask-for-password --prompt="$prompt" */ @@ -417,7 +445,7 @@ const char **plymouthd_argv; pid_t pid = get_pid(); if(pid == 0){ - error(0, 0, "plymouthd pid not found"); + error_plus(0, 0, "plymouthd pid not found"); plymouthd_argv = plymouthd_default_argv; } else { plymouthd_argv = getargv(pid); === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2011-02-27 17:26:35 +0000 +++ plugins.d/splashy.c 2011-07-13 02:24:39 +0000 @@ -29,7 +29,8 @@ SIG_IGN, kill(), SIGKILL */ #include /* NULL */ #include /* getenv() */ -#include /* asprintf() */ +#include /* asprintf(), vasprintf(), vprintf(), + fprintf() */ #include /* EXIT_FAILURE, free(), EXIT_SUCCESS */ #include /* pid_t, DIR, struct dirent, @@ -42,7 +43,7 @@ sleep(), dup2() STDERR_FILENO, STDOUT_FILENO, _exit(), pause() */ -#include /* memcmp() */ +#include /* memcmp(), strerror() */ #include /* errno, EACCES, ENOTDIR, ELOOP, ENOENT, ENAMETOOLONG, EMFILE, ENFILE, ENOMEM, ENOEXEC, EINVAL, @@ -54,10 +55,35 @@ WEXITSTATUS() */ #include /* EX_OSERR, EX_OSFILE, EX_UNAVAILABLE */ +#include /* va_list, va_start(), ... */ sig_atomic_t interrupted_by_signal = 0; int signal_received; +/* Function to use when printing errors */ +void error_plus(int status, int errnum, const char *formatstring, + ...){ + va_list ap; + char *text; + int ret; + + va_start(ap, formatstring); + ret = vasprintf(&text, formatstring, ap); + if (ret == -1){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + vfprintf(stderr, formatstring, ap); + fprintf(stderr, ": "); + fprintf(stderr, "%s\n", strerror(errnum)); + error(status, errno, "vasprintf while printing error"); + return; + } + fprintf(stderr, "Mandos plugin "); + error(status, errnum, "%s", text); + free(text); +} + + static void termination_handler(int signum){ if(interrupted_by_signal){ return; @@ -110,7 +136,7 @@ proc_dir = opendir("/proc"); if(proc_dir == NULL){ int e = errno; - error(0, errno, "opendir"); + error_plus(0, errno, "opendir"); switch(e){ case EACCES: case ENOTDIR: @@ -152,7 +178,7 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); if(ret == -1){ - error(0, errno, "asprintf"); + error_plus(0, errno, "asprintf"); exitstatus = EX_OSERR; goto failure; } @@ -166,7 +192,7 @@ continue; } int e = errno; - error(0, errno, "lstat"); + error_plus(0, errno, "lstat"); free(exe_link); switch(e){ case EACCES: @@ -214,60 +240,60 @@ sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGHUP); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGTERM); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } @@ -284,7 +310,7 @@ goto failure; } if(splashy_command_pid == -1){ - error(0, errno, "fork"); + error_plus(0, errno, "fork"); exitstatus = EX_OSERR; goto failure; } @@ -294,7 +320,7 @@ const char splashy_command[] = "/sbin/splashy_update"; execl(splashy_command, splashy_command, prompt, (char *)NULL); int e = errno; - error(0, errno, "execl"); + error_plus(0, errno, "execl"); switch(e){ case EACCES: case ENOENT: @@ -344,7 +370,7 @@ goto failure; } if(ret == -1){ - error(0, errno, "waitpid"); + error_plus(0, errno, "waitpid"); if(errno == ECHILD){ splashy_command_pid = 0; } @@ -382,27 +408,27 @@ the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ - error(0, errno, "setuid"); + error_plus(0, errno, "setuid"); } setsid(); ret = chdir("/"); if(ret == -1){ - error(0, errno, "chdir"); + error_plus(0, errno, "chdir"); } /* if(fork() != 0){ */ /* _exit(EXIT_SUCCESS); */ /* } */ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */ if(ret == -1){ - error(0, errno, "dup2"); + error_plus(0, errno, "dup2"); _exit(EX_OSERR); } execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); { int e = errno; - error(0, errno, "execl"); + error_plus(0, errno, "execl"); switch(e){ case EACCES: case ENOENT: @@ -428,13 +454,13 @@ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, &signal_action, NULL)); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); } do { ret = raise(signal_received); } while(ret != 0 and errno == EINTR); if(ret != 0){ - error(0, errno, "raise"); + error_plus(0, errno, "raise"); abort(); } TEMP_FAILURE_RETRY(pause()); === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2011-02-27 17:26:35 +0000 +++ plugins.d/usplash.c 2011-07-13 02:24:39 +0000 @@ -35,8 +35,9 @@ #include /* size_t, ssize_t, pid_t, DIR, struct dirent */ #include /* NULL */ -#include /* strlen(), memcmp() */ -#include /* asprintf()*/ +#include /* strlen(), memcmp(), strerror() */ +#include /* asprintf(), vasprintf(), vprintf(), + fprintf() */ #include /* close(), write(), readlink(), read(), STDOUT_FILENO, sleep(), fork(), setuid(), geteuid(), @@ -50,11 +51,35 @@ #include /* struct stat, lstat(), S_ISLNK */ #include /* EX_OSERR, EX_UNAVAILABLE */ #include /* argz_count(), argz_extract() */ +#include /* va_list, va_start(), ... */ sig_atomic_t interrupted_by_signal = 0; int signal_received; const char usplash_name[] = "/sbin/usplash"; +/* Function to use when printing errors */ +void error_plus(int status, int errnum, const char *formatstring, + ...){ + va_list ap; + char *text; + int ret; + + va_start(ap, formatstring); + ret = vasprintf(&text, formatstring, ap); + if (ret == -1){ + fprintf(stderr, "Mandos plugin %s: ", + program_invocation_short_name); + vfprintf(stderr, formatstring, ap); + fprintf(stderr, ": "); + fprintf(stderr, "%s\n", strerror(errnum)); + error(status, errno, "vasprintf while printing error"); + return; + } + fprintf(stderr, "Mandos plugin "); + error(status, errnum, "%s", text); + free(text); +} + static void termination_handler(int signum){ if(interrupted_by_signal){ return; @@ -155,7 +180,7 @@ size_t cmdline_len = 0; DIR *proc_dir = opendir("/proc"); if(proc_dir == NULL){ - error(0, errno, "opendir"); + error_plus(0, errno, "opendir"); return -1; } errno = 0; @@ -183,7 +208,7 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); if(ret == -1){ - error(0, errno, "asprintf"); + error_plus(0, errno, "asprintf"); goto fail_find_usplash; } @@ -195,7 +220,7 @@ free(exe_link); continue; } - error(0, errno, "lstat"); + error_plus(0, errno, "lstat"); free(exe_link); goto fail_find_usplash; } @@ -226,13 +251,13 @@ ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_ent->d_name); if(ret == -1){ - error(0, errno, "asprintf"); + error_plus(0, errno, "asprintf"); goto fail_find_usplash; } cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); if(cl_fd == -1){ - error(0, errno, "open"); + error_plus(0, errno, "open"); goto fail_find_usplash; } } @@ -244,7 +269,7 @@ if(cmdline_len + blocksize > cmdline_allocated){ tmp = realloc(cmdline, cmdline_allocated + blocksize); if(tmp == NULL){ - error(0, errno, "realloc"); + error_plus(0, errno, "realloc"); close(cl_fd); goto fail_find_usplash; } @@ -255,7 +280,7 @@ sret = read(cl_fd, cmdline + cmdline_len, cmdline_allocated - cmdline_len); if(sret == -1){ - error(0, errno, "read"); + error_plus(0, errno, "read"); close(cl_fd); goto fail_find_usplash; } @@ -263,14 +288,14 @@ } while(sret != 0); ret = close(cl_fd); if(ret == -1){ - error(0, errno, "close"); + error_plus(0, errno, "close"); goto fail_find_usplash; } } /* Close directory */ ret = closedir(proc_dir); if(ret == -1){ - error(0, errno, "closedir"); + error_plus(0, errno, "closedir"); goto fail_find_usplash; } /* Success */ @@ -325,26 +350,26 @@ sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGHUP); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGTERM); if(ret == -1){ - error(0, errno, "sigaddset"); + error_plus(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -353,7 +378,7 @@ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -362,7 +387,7 @@ ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -371,7 +396,7 @@ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -380,7 +405,7 @@ ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -389,7 +414,7 @@ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -401,7 +426,7 @@ /* Write command to FIFO */ if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){ if(errno != EINTR){ - error(0, errno, "usplash_write"); + error_plus(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -413,7 +438,7 @@ if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){ if(errno != EINTR){ - error(0, errno, "usplash_write"); + error_plus(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -431,7 +456,7 @@ outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); if(outfifo_fd == -1){ if(errno != EINTR){ - error(0, errno, "open"); + error_plus(0, errno, "open"); status = EX_OSERR; } goto failure; @@ -450,7 +475,7 @@ char *tmp = realloc(buf, buf_allocated + blocksize); if(tmp == NULL){ if(errno != EINTR){ - error(0, errno, "realloc"); + error_plus(0, errno, "realloc"); status = EX_OSERR; } goto failure; @@ -462,7 +487,7 @@ buf_allocated - buf_len); if(sret == -1){ if(errno != EINTR){ - error(0, errno, "read"); + error_plus(0, errno, "read"); status = EX_OSERR; } TEMP_FAILURE_RETRY(close(outfifo_fd)); @@ -477,7 +502,7 @@ ret = close(outfifo_fd); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "close"); + error_plus(0, errno, "close"); status = EX_OSERR; } goto failure; @@ -490,7 +515,7 @@ if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){ if(errno != EINTR){ - error(0, errno, "usplash_write"); + error_plus(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -503,7 +528,7 @@ ret = close(fifo_fd); if(ret == -1){ if(errno != EINTR){ - error(0, errno, "close"); + error_plus(0, errno, "close"); status = EX_OSERR; } goto failure; @@ -517,7 +542,7 @@ sret = write(STDOUT_FILENO, buf + written, buf_len - written); if(sret == -1){ if(errno != EINTR){ - error(0, errno, "write"); + error_plus(0, errno, "write"); status = EX_OSERR; } goto failure; @@ -554,7 +579,7 @@ if(fifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); + error_plus(0, errno, "close"); } fifo_fd = -1; } @@ -563,7 +588,7 @@ if(outfifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd)); if(ret == -1){ - error(0, errno, "close"); + error_plus(0, errno, "close"); } } @@ -571,7 +596,7 @@ char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1) * sizeof(char *)); /* Count args */ if(cmdline_argv == NULL){ - error(0, errno, "malloc"); + error_plus(0, errno, "malloc"); return status; } argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */ @@ -592,13 +617,13 @@ the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ - error(0, errno, "setuid"); + error_plus(0, errno, "setuid"); } setsid(); ret = chdir("/"); if(ret == -1){ - error(0, errno, "chdir"); + error_plus(0, errno, "chdir"); _exit(EX_OSERR); } /* if(fork() != 0){ */ @@ -606,13 +631,13 @@ /* } */ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ - error(0, errno, "dup2"); + error_plus(0, errno, "dup2"); _exit(EX_OSERR); } execv(usplash_name, cmdline_argv); if(not interrupted_by_signal){ - error(0, errno, "execv"); + error_plus(0, errno, "execv"); } free(cmdline); free(cmdline_argv); @@ -623,7 +648,7 @@ sleep(2); if(not usplash_write(&fifo_fd, "PULSATE", NULL)){ if(errno != EINTR){ - error(0, errno, "usplash_write"); + error_plus(0, errno, "usplash_write"); } } @@ -631,7 +656,7 @@ if(fifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); + error_plus(0, errno, "close"); } fifo_fd = -1; } @@ -642,13 +667,13 @@ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, &signal_action, NULL)); if(ret == -1){ - error(0, errno, "sigaction"); + error_plus(0, errno, "sigaction"); } do { ret = raise(signal_received); } while(ret != 0 and errno == EINTR); if(ret != 0){ - error(0, errno, "raise"); + error_plus(0, errno, "raise"); abort(); } TEMP_FAILURE_RETRY(pause());