=== modified file 'TODO' --- TODO 2010-09-09 18:16:14 +0000 +++ TODO 2010-09-09 22:26:35 +0000 @@ -2,10 +2,12 @@ * Use _attribute_((nonnull)) wherever possible. +* Release critical +** TODO Change make-run-server to not include --no-dbus + * 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 use error() instead of perror() ** TODO [#B] Retry a server which has a non-definite reply: *** A closed connection during the TLS handshake *** A TCP timeout @@ -15,31 +17,30 @@ * splashy ** TODO [#B] use scandir(3) instead of readdir(3) ** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] use error() instead of perror() * 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 [#B] use error() instead of perror() +** 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] use error() instead of perror() ** TODO [#B] Drop privileges after opening FIFO. * password-prompt ** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] use error() instead of perror() ** TODO [#B] lock stdin (with flock()?) +* plymouth +** TODO Use [[info:libc:Argz%20Functions][argz_extract]] + * TODO [#B] passdev * plugin-runner ** 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 -** TODO [#B] use error() instead of perror() ** TODO [#B] Use openat() * mandos (server) @@ -82,6 +83,9 @@ ** TODO [#C] python-parsedatetime ** TODO [#C] systemd/launchd http://0pointer.de/blog/projects/systemd.html +** TODO Separate logging logic to own object +** TODO make clients to a dict! +** TODO [#A] Limit approved_delay to max gnutls/tls timeout value * mandos.xml ** [[file:mandos.xml::XXX][Document D-Bus interface]] @@ -100,9 +104,12 @@ arguments. * mandos-monitor +** TODO help should be toggable ** Urwid client data displayer Better view of client data in the listing *** Properties popup +** Nicer crashes. Stack traces Messes up shell. +*** Print a nice "We are sorry" message, save stack trace to log. * mandos-keygen ** TODO Loop until passwords match when run interactively @@ -125,5 +132,9 @@ ** TODO [#C] /etc/bash_completion.d/mandos From XML sources directly? +* Side Stuff +** TODO Locate which packet move the other bin/sh when busy box is deactivated +** TODO contact owner of packet, and ask them to have that shell static in position regardless of busybox + #+STARTUP: showall === modified file 'debian/control' --- debian/control 2010-09-09 18:16:14 +0000 +++ debian/control 2010-09-09 22:26:35 +0000 @@ -15,7 +15,8 @@ Package: mandos Architecture: all Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus, - python-avahi, python-gobject, avahi-daemon, adduser + python-avahi, python-gobject, avahi-daemon, adduser, + python-urwid Recommends: fping Description: a server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows === modified file 'mandos' --- mandos 2010-09-09 18:16:14 +0000 +++ mandos 2010-09-09 22:26:35 +0000 @@ -60,7 +60,7 @@ import fcntl import functools import cPickle as pickle -import select +import multiprocessing import dbus import dbus.service @@ -83,6 +83,7 @@ version = "1.0.14" +#logger = logging.getLogger(u'mandos') logger = logging.Logger(u'mandos') syslogger = (logging.handlers.SysLogHandler (facility = logging.handlers.SysLogHandler.LOG_DAEMON, @@ -156,15 +157,20 @@ u" after %i retries, exiting.", self.rename_count) raise AvahiServiceError(u"Too many renames") - self.name = self.server.GetAlternativeServiceName(self.name) + self.name = unicode(self.server.GetAlternativeServiceName(self.name)) logger.info(u"Changing Zeroconf service name to %r ...", - unicode(self.name)) + self.name) syslogger.setFormatter(logging.Formatter (u'Mandos (%s) [%%(process)d]:' u' %%(levelname)s: %%(message)s' % self.name)) self.remove() - self.add() + try: + self.add() + except dbus.exceptions.DBusException, error: + logger.critical(u"DBusException: %s", error) + self.cleanup() + os._exit(1) self.rename_count += 1 def remove(self): """Derived from the Avahi example code""" @@ -259,6 +265,9 @@ runtime with vars(self) as dict, so that for instance %(name)s can be used in the command. current_checker_command: string; current running checker_command + approved_delay: datetime.timedelta(); Time to wait for approval + _approved: bool(); 'None' if not yet approved/disapproved + approved_duration: datetime.timedelta(); Duration of one approval """ @staticmethod @@ -275,6 +284,9 @@ def interval_milliseconds(self): "Return the 'interval' attribute in milliseconds" return self._timedelta_to_milliseconds(self.interval) + + def approved_delay_milliseconds(self): + return self._timedelta_to_milliseconds(self.approved_delay) def __init__(self, name = None, disable_hook=None, config=None): """Note: the 'checker' key in 'config' sets the @@ -315,12 +327,27 @@ self.checker_command = config[u"checker"] self.current_checker_command = None self.last_connect = None + self._approved = None + self.approved_by_default = config.get(u"approved_by_default", + True) + self.approvals_pending = 0 + self.approved_delay = string_to_delta( + config[u"approved_delay"]) + self.approved_duration = string_to_delta( + config[u"approved_duration"]) + self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock()) + def send_changedstate(self): + self.changedstate.acquire() + self.changedstate.notify_all() + self.changedstate.release() + def enable(self): """Start this client's checker and timeout hooks""" if getattr(self, u"enabled", False): # Already enabled return + self.send_changedstate() self.last_enabled = datetime.datetime.utcnow() # Schedule a new checker to be started an 'interval' from now, # and every interval from then on. @@ -340,6 +367,8 @@ if not getattr(self, "enabled", False): return False if not quiet: + self.send_changedstate() + if not quiet: logger.info(u"Disabling client %s", self.name) if getattr(self, u"disable_initiator_tag", False): gobject.source_remove(self.disable_initiator_tag) @@ -478,7 +507,6 @@ raise self.checker = None - def dbus_service_property(dbus_interface, signature=u"v", access=u"readwrite", byte_arrays=False): """Decorators for marking methods of a DBusObjectWithProperties to @@ -678,6 +706,7 @@ # dbus.service.Object doesn't use super(), so we can't either. def __init__(self, bus = None, *args, **kwargs): + self._approvals_pending = 0 self.bus = bus Client.__init__(self, *args, **kwargs) # Only now, when this client is initialized, can it show up on @@ -687,6 +716,22 @@ + self.name.replace(u".", u"_"))) DBusObjectWithProperties.__init__(self, self.bus, self.dbus_object_path) + + def _get_approvals_pending(self): + return self._approvals_pending + def _set_approvals_pending(self, value): + old_value = self._approvals_pending + self._approvals_pending = value + bval = bool(value) + if (hasattr(self, "dbus_object_path") + and bval is not bool(old_value)): + dbus_bool = dbus.Boolean(bval, variant_level=1) + self.PropertyChanged(dbus.String(u"approved_pending"), + dbus_bool) + + approvals_pending = property(_get_approvals_pending, + _set_approvals_pending) + del _get_approvals_pending, _set_approvals_pending @staticmethod def _datetime_to_dbus(dt, variant_level=0): @@ -781,6 +826,17 @@ self.PropertyChanged(dbus.String(u"checker_running"), dbus.Boolean(False, variant_level=1)) return r + + def _reset_approved(self): + self._approved = None + return False + + def approve(self, value=True): + self.send_changedstate() + self._approved = value + gobject.timeout_add(self._timedelta_to_milliseconds(self.approved_duration), + self._reset_approved) + ## D-Bus methods, signals & properties _interface = u"se.bsnet.fukt.Mandos.Client" @@ -808,17 +864,31 @@ # GotSecret - signal @dbus.service.signal(_interface) def GotSecret(self): - "D-Bus signal" + """D-Bus signal + Is sent after a successful transfer of secret from the Mandos + server to mandos-client + """ pass # Rejected - signal - @dbus.service.signal(_interface) - def Rejected(self): + @dbus.service.signal(_interface, signature=u"s") + def Rejected(self, reason): + "D-Bus signal" + pass + + # NeedApproval - signal + @dbus.service.signal(_interface, signature=u"db") + def NeedApproval(self, timeout, default): "D-Bus signal" pass ## Methods - + + # Approve - method + @dbus.service.method(_interface, in_signature=u"b") + def Approve(self, value): + self.approve(value) + # CheckedOK - method @dbus.service.method(_interface) def CheckedOK(self): @@ -849,6 +919,30 @@ ## Properties + # approved_pending - property + @dbus_service_property(_interface, signature=u"b", access=u"read") + def approved_pending_dbus_property(self): + return dbus.Boolean(bool(self.approvals_pending)) + + # approved_by_default - property + @dbus_service_property(_interface, signature=u"b", + access=u"readwrite") + def approved_by_default_dbus_property(self): + return dbus.Boolean(self.approved_by_default) + + # approved_delay - property + @dbus_service_property(_interface, signature=u"t", + access=u"readwrite") + def approved_delay_dbus_property(self): + return dbus.UInt64(self.approved_delay_milliseconds()) + + # approved_duration - property + @dbus_service_property(_interface, signature=u"t", + access=u"readwrite") + def approved_duration_dbus_property(self): + return dbus.UInt64(self._timedelta_to_milliseconds( + self.approved_duration)) + # name - property @dbus_service_property(_interface, signature=u"s", access=u"read") def name_dbus_property(self): @@ -988,6 +1082,32 @@ del _interface +class ProxyClient(object): + def __init__(self, child_pipe, fpr, address): + self._pipe = child_pipe + self._pipe.send(('init', fpr, address)) + if not self._pipe.recv(): + raise KeyError() + + def __getattribute__(self, name): + if(name == '_pipe'): + return super(ProxyClient, self).__getattribute__(name) + self._pipe.send(('getattr', name)) + data = self._pipe.recv() + if data[0] == 'data': + return data[1] + if data[0] == 'function': + def func(*args, **kwargs): + self._pipe.send(('funcall', name, args, kwargs)) + return self._pipe.recv()[1] + return func + + def __setattr__(self, name, value): + if(name == '_pipe'): + return super(ProxyClient, self).__setattr__(name, value) + self._pipe.send(('setattr', name, value)) + + class ClientHandler(socketserver.BaseRequestHandler, object): """A class to handle client connections. @@ -995,24 +1115,22 @@ Note: This will run in its own forked process.""" def handle(self): - logger.info(u"TCP connection from: %s", - unicode(self.client_address)) - logger.debug(u"IPC Pipe FD: %d", - self.server.child_pipe[1].fileno()) - # Open IPC pipe to parent process - with contextlib.nested(self.server.child_pipe[1], - self.server.parent_pipe[0] - ) as (ipc, ipc_return): + with contextlib.closing(self.server.child_pipe) as child_pipe: + logger.info(u"TCP connection from: %s", + unicode(self.client_address)) + logger.debug(u"Pipe FD: %d", + self.server.child_pipe.fileno()) + session = (gnutls.connection .ClientSession(self.request, gnutls.connection .X509Credentials())) - + # Note: gnutls.connection.X509Credentials is really a # generic GnuTLS certificate credentials object so long as # no X.509 keys are added to it. Therefore, we can use it # here despite using OpenPGP certificates. - + #priority = u':'.join((u"NONE", u"+VERS-TLS1.1", # u"+AES-256-CBC", u"+SHA1", # u"+COMP-NULL", u"+CTYPE-OPENPGP", @@ -1024,7 +1142,7 @@ (gnutls.library.functions .gnutls_priority_set_direct(session._c_object, priority, None)) - + # Start communication using the Mandos protocol # Get protocol number line = self.request.makefile().readline() @@ -1035,7 +1153,7 @@ except (ValueError, IndexError, RuntimeError), error: logger.error(u"Unknown protocol version: %s", error) return - + # Start GnuTLS connection try: session.handshake() @@ -1045,6 +1163,8 @@ # established. Just abandon the request. return logger.debug(u"Handshake succeeded") + + approval_required = False try: try: fpr = self.fingerprint(self.peer_certificate @@ -1054,48 +1174,92 @@ return logger.debug(u"Fingerprint: %s", fpr) - for c in self.server.clients: - if c.fingerprint == fpr: - client = c + try: + client = ProxyClient(child_pipe, fpr, + self.client_address) + except KeyError: + return + + if client.approved_delay: + delay = client.approved_delay + client.approvals_pending += 1 + approval_required = True + + while True: + if not client.enabled: + logger.warning(u"Client %s is disabled", + client.name) + if self.server.use_dbus: + # Emit D-Bus signal + client.Rejected("Disabled") + return + + if client._approved or not client.approved_delay: + #We are approved or approval is disabled break - else: - ipc.write(u"NOTFOUND %s %s\n" - % (fpr, unicode(self.client_address))) - return - - class ClientProxy(object): - """Client proxy object. Not for calling methods.""" - def __init__(self, client): - self.client = client - def __getattr__(self, name): - if name.startswith("ipc_"): - def tempfunc(): - ipc.write("%s %s\n" % (name[4:].upper(), - self.client.name)) - return tempfunc - if not hasattr(self.client, name): - raise AttributeError - ipc.write(u"GETATTR %s %s\n" - % (name, self.client.fingerprint)) - return pickle.load(ipc_return) - clientproxy = ClientProxy(client) - # Have to check if client.enabled, since it is - # possible that the client was disabled since the - # GnuTLS session was established. - if not clientproxy.enabled: - clientproxy.ipc_disabled() - return - - clientproxy.ipc_sending() + elif client._approved is None: + logger.info(u"Client %s need approval", + client.name) + if self.server.use_dbus: + # Emit D-Bus signal + client.NeedApproval( + client.approved_delay_milliseconds(), + client.approved_by_default) + else: + logger.warning(u"Client %s was not approved", + client.name) + if self.server.use_dbus: + # Emit D-Bus signal + client.Rejected("Disapproved") + return + + #wait until timeout or approved + #x = float(client._timedelta_to_milliseconds(delay)) + time = datetime.datetime.now() + client.changedstate.acquire() + client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000)) + client.changedstate.release() + time2 = datetime.datetime.now() + if (time2 - time) >= delay: + if not client.approved_by_default: + logger.warning("Client %s timed out while" + " waiting for approval", + client.name) + if self.server.use_dbus: + # Emit D-Bus signal + client.Rejected("Time out") + return + else: + break + else: + delay -= time2 - time + sent_size = 0 while sent_size < len(client.secret): - sent = session.send(client.secret[sent_size:]) + try: + sent = session.send(client.secret[sent_size:]) + except (gnutls.errors.GNUTLSError), error: + logger.warning("gnutls send failed") + return logger.debug(u"Sent: %d, remaining: %d", sent, len(client.secret) - (sent_size + sent)) sent_size += sent + + logger.info(u"Sending secret to %s", client.name) + # bump the timeout as if seen + client.checked_ok() + if self.server.use_dbus: + # Emit D-Bus signal + client.GotSecret() + finally: - session.bye() + if approval_required: + client.approvals_pending -= 1 + try: + session.bye() + except (gnutls.errors.GNUTLSError), error: + logger.warning("gnutls bye failed") @staticmethod def peer_certificate(session): @@ -1161,30 +1325,39 @@ return hex_fpr -class ForkingMixInWithPipes(socketserver.ForkingMixIn, object): - """Like socketserver.ForkingMixIn, but also pass a pipe pair.""" +class MultiprocessingMixIn(object): + """Like socketserver.ThreadingMixIn, but with multiprocessing""" + def sub_process_main(self, request, address): + try: + self.finish_request(request, address) + except: + self.handle_error(request, address) + self.close_request(request) + + def process_request(self, request, address): + """Start a new process to process the request.""" + multiprocessing.Process(target = self.sub_process_main, + args = (request, address)).start() + +class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object): + """ adds a pipe to the MixIn """ def process_request(self, request, client_address): """Overrides and wraps the original process_request(). This function creates a new pipe in self.pipe """ - # Child writes to child_pipe - self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0)) - # Parent writes to parent_pipe - self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0)) - super(ForkingMixInWithPipes, + parent_pipe, self.child_pipe = multiprocessing.Pipe() + + super(MultiprocessingMixInWithPipe, self).process_request(request, client_address) - # Close unused ends for parent - self.parent_pipe[0].close() # close read end - self.child_pipe[1].close() # close write end - self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1]) - def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd): + self.child_pipe.close() + self.add_pipe(parent_pipe) + + def add_pipe(self, parent_pipe): """Dummy function; override as necessary""" - child_pipe_fd.close() - parent_pipe_fd.close() - - -class IPv6_TCPServer(ForkingMixInWithPipes, + pass + +class IPv6_TCPServer(MultiprocessingMixInWithPipe, socketserver.TCPServer, object): """IPv6-capable TCP server. Accepts 'None' as address and/or port @@ -1275,14 +1448,15 @@ return socketserver.TCPServer.server_activate(self) def enable(self): self.enabled = True - def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd): + def add_pipe(self, parent_pipe): # Call "handle_ipc" for both data and EOF events - gobject.io_add_watch(child_pipe_fd.fileno(), + gobject.io_add_watch(parent_pipe.fileno(), gobject.IO_IN | gobject.IO_HUP, functools.partial(self.handle_ipc, - reply = parent_pipe_fd, - sender= child_pipe_fd)) - def handle_ipc(self, source, condition, reply=None, sender=None): + parent_pipe = parent_pipe)) + + def handle_ipc(self, source, condition, parent_pipe=None, + client_object=None): condition_names = { gobject.IO_IN: u"IN", # There is data to read. gobject.IO_OUT: u"OUT", # Data can be written (without @@ -1299,68 +1473,60 @@ if cond & condition) logger.debug(u"Handling IPC: FD = %d, condition = %s", source, conditions_string) - - # Read a line from the file object - cmdline = sender.readline() - if not cmdline: # Empty line means end of file - # close the IPC pipes - sender.close() - reply.close() - - # Stop calling this function - return False - - logger.debug(u"IPC command: %r", cmdline) - - # Parse and act on command - cmd, args = cmdline.rstrip(u"\r\n").split(None, 1) - - if cmd == u"NOTFOUND": - fpr, address = args.split(None, 1) - logger.warning(u"Client not found for fingerprint: %s, ad" - u"dress: %s", fpr, address) - if self.use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientNotFound(fpr, address) - elif cmd == u"DISABLED": - for client in self.clients: - if client.name == args: - logger.warning(u"Client %s is disabled", args) - if self.use_dbus: - # Emit D-Bus signal - client.Rejected() - break - else: - logger.error(u"Unknown client %s is disabled", args) - elif cmd == u"SENDING": - for client in self.clients: - if client.name == args: - logger.info(u"Sending secret to %s", client.name) - client.checked_ok() - if self.use_dbus: - # Emit D-Bus signal - client.GotSecret() - break - else: - logger.error(u"Sending secret to unknown client %s", - args) - elif cmd == u"GETATTR": - attr_name, fpr = args.split(None, 1) - for client in self.clients: - if client.fingerprint == fpr: - attr_value = getattr(client, attr_name, None) - logger.debug("IPC reply: %r", attr_value) - pickle.dump(attr_value, reply) - break - else: - logger.error(u"Client %s on address %s requesting " - u"attribute %s not found", fpr, address, - attr_name) - pickle.dump(None, reply) - else: - logger.error(u"Unknown IPC command: %r", cmdline) - - # Keep calling this function + + # error or the other end of multiprocessing.Pipe has closed + if condition & (gobject.IO_ERR | condition & gobject.IO_HUP): + return False + + # Read a request from the child + request = parent_pipe.recv() + logger.debug(u"IPC request: %s", repr(request)) + command = request[0] + + if command == 'init': + fpr = request[1] + address = request[2] + + for c in self.clients: + if c.fingerprint == fpr: + client = c + break + else: + logger.warning(u"Client not found for fingerprint: %s, ad" + u"dress: %s", fpr, address) + if self.use_dbus: + # Emit D-Bus signal + mandos_dbus_service.ClientNotFound(fpr, address) + parent_pipe.send(False) + return False + + gobject.io_add_watch(parent_pipe.fileno(), + gobject.IO_IN | gobject.IO_HUP, + functools.partial(self.handle_ipc, + parent_pipe = parent_pipe, + client_object = client)) + parent_pipe.send(True) + # remove the old hook in favor of the new above hook on same fileno + return False + if command == 'funcall': + funcname = request[1] + args = request[2] + kwargs = request[3] + + parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs))) + + if command == 'getattr': + attrname = request[1] + if callable(client_object.__getattribute__(attrname)): + parent_pipe.send(('function',)) + else: + parent_pipe.send(('data', client_object.__getattribute__(attrname))) + + if command == 'setattr': + attrname = request[1] + value = request[2] + setattr(client_object, attrname, value) + return True @@ -1469,6 +1635,8 @@ parser.add_option("--debug", action=u"store_true", help=u"Debug mode; run in foreground and log to" u" terminal") + parser.add_option("--debuglevel", type=u"string", metavar="Level", + help=u"Debug level for stdout output") parser.add_option("--priority", type=u"string", help=u"GnuTLS" u" priority string (see GnuTLS documentation)") parser.add_option("--servicename", type=u"string", @@ -1499,6 +1667,7 @@ u"servicename": u"Mandos", u"use_dbus": u"True", u"use_ipv6": u"True", + u"debuglevel": u"", } # Parse config file for server-global settings @@ -1521,7 +1690,7 @@ # options, if set. for option in (u"interface", u"address", u"port", u"debug", u"priority", u"servicename", u"configdir", - u"use_dbus", u"use_ipv6"): + u"use_dbus", u"use_ipv6", u"debuglevel"): value = getattr(options, option) if value is not None: server_settings[option] = value @@ -1536,13 +1705,10 @@ # For convenience debug = server_settings[u"debug"] + debuglevel = server_settings[u"debuglevel"] use_dbus = server_settings[u"use_dbus"] use_ipv6 = server_settings[u"use_ipv6"] - - if not debug: - syslogger.setLevel(logging.WARNING) - console.setLevel(logging.WARNING) - + if server_settings[u"servicename"] != u"Mandos": syslogger.setFormatter(logging.Formatter (u'Mandos (%s) [%%(process)d]:' @@ -1554,6 +1720,8 @@ u"interval": u"5m", u"checker": u"fping -q -- %%(host)s", u"host": u"", + u"approved_delay": u"0s", + u"approved_duration": u"1s", } client_config = configparser.SafeConfigParser(client_defaults) client_config.read(os.path.join(server_settings[u"configdir"], @@ -1599,6 +1767,16 @@ raise error # Enable all possible GnuTLS debugging + + + if not debug and not debuglevel: + syslogger.setLevel(logging.WARNING) + console.setLevel(logging.WARNING) + if debuglevel: + level = getattr(logging, debuglevel.upper()) + syslogger.setLevel(level) + console.setLevel(level) + if debug: # "Use a log level over 10 to enable all debugging options." # - GnuTLS manual @@ -1610,6 +1788,16 @@ (gnutls.library.functions .gnutls_global_set_log_function(debug_gnutls)) + + # Redirect stdin so all checkers get /dev/null + null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR) + os.dup2(null, sys.stdin.fileno()) + if null > 2: + os.close(null) + else: + # No console logging + logger.removeHandler(console) + global main_loop # From the Avahi example code @@ -1633,29 +1821,37 @@ if server_settings["interface"]: service.interface = (if_nametoindex (str(server_settings[u"interface"]))) + + if not debug: + # Close all input and output, do double fork, etc. + daemon() + + global multiprocessing_manager + multiprocessing_manager = multiprocessing.Manager() client_class = Client if use_dbus: client_class = functools.partial(ClientDBus, bus = bus) + def client_config_items(config, section): + special_settings = { + "approved_by_default": + lambda: config.getboolean(section, + "approved_by_default"), + } + for name, value in config.items(section): + try: + yield (name, special_settings[name]()) + except KeyError: + yield (name, value) + tcp_server.clients.update(set( client_class(name = section, - config= dict(client_config.items(section))) + config= dict(client_config_items( + client_config, section))) for section in client_config.sections())) if not tcp_server.clients: logger.warning(u"No clients defined") - - if debug: - # Redirect stdin so all checkers get /dev/null - null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR) - os.dup2(null, sys.stdin.fileno()) - if null > 2: - os.close(null) - else: - # No console logging - logger.removeHandler(console) - # Close all input and output, do double fork, etc. - daemon() - + try: with pidfile: pid = os.getpid() === modified file 'mandos-ctl' --- mandos-ctl 2010-09-09 18:16:14 +0000 +++ mandos-ctl 2010-09-09 22:26:35 +0000 @@ -31,8 +31,12 @@ server_interface = domain + '.Mandos' client_interface = domain + '.Mandos.Client' version = "1.0.14" -bus = dbus.SystemBus() -mandos_dbus_objc = bus.get_object(busname, server_path) +try: + bus = dbus.SystemBus() + mandos_dbus_objc = bus.get_object(busname, server_path) +except dbus.exceptions.DBusException: + sys.exit(1) + mandos_serv = dbus.Interface(mandos_dbus_objc, dbus_interface = server_interface) mandos_clients = mandos_serv.GetAllClientsWithProperties() @@ -141,6 +145,10 @@ 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() # Compile list of clients to process @@ -202,3 +210,7 @@ client.Set(client_interface, u"secret", dbus.ByteArray(open(options.secret, u'rb').read()), dbus_interface=dbus.PROPERTIES_IFACE) + if options.approve: + client.Approve(dbus.Boolean(True), dbus_interface=client_interface) + if options.deny: + client.Approve(dbus.Boolean(False), dbus_interface=client_interface) === modified file 'mandos-monitor' --- mandos-monitor 2010-09-05 20:19:02 +0000 +++ mandos-monitor 2010-09-08 19:12:04 +0000 @@ -23,11 +23,14 @@ locale.setlocale(locale.LC_ALL, u'') +import logging +logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL) + # Some useful constants domain = 'se.bsnet.fukt' server_interface = domain + '.Mandos' client_interface = domain + '.Mandos.Client' -version = "1.0.14" +version = "1.0.15" # Always run in monochrome mode urwid.curses_display.curses.has_colors = lambda : False @@ -70,8 +73,10 @@ self.properties.update( self.proxy.GetAll(client_interface, dbus_interface = dbus.PROPERTIES_IFACE)) - super(MandosClientPropertyCache, self).__init__( - proxy_object=proxy_object, *args, **kwargs) + + #XXX This break good super behaviour! +# super(MandosClientPropertyCache, self).__init__( +# *args, **kwargs) def property_changed(self, property=None, value=None): """This is called whenever we get a PropertyChanged signal @@ -120,6 +125,10 @@ self.got_secret, client_interface, byte_arrays=True) + self.proxy.connect_to_signal(u"NeedApproval", + self.need_approval, + client_interface, + byte_arrays=True) self.proxy.connect_to_signal(u"Rejected", self.rejected, client_interface, @@ -175,16 +184,26 @@ self.update() def checker_started(self, command): - self.logger(u'Client %s started checker "%s"' - % (self.properties[u"name"], unicode(command))) + #self.logger(u'Client %s started checker "%s"' + # % (self.properties[u"name"], unicode(command))) + pass def got_secret(self): + self.last_checker_failed = False self.logger(u'Client %s received its secret' % self.properties[u"name"]) - def rejected(self): - self.logger(u'Client %s was rejected' - % self.properties[u"name"]) + def need_approval(self, timeout, default): + if not default: + message = u'Client %s needs approval within %s seconds' + else: + message = u'Client %s will get its secret in %s seconds' + self.logger(message + % (self.properties[u"name"], timeout/1000)) + + def rejected(self, reason): + self.logger(u'Client %s was rejected; reason: %s' + % (self.properties[u"name"], reason)) def selectable(self): """Make this a "selectable" widget. @@ -212,31 +231,33 @@ u"bold-underline-blink": u"bold-underline-blink-standout", } - + # Rebuild focus and non-focus widgets using current properties - self._text = (u'%(name)s: %(enabled)s%(timer)s' - % { u"name": self.properties[u"name"], - u"enabled": - (u"enabled" - if self.properties[u"enabled"] - else u"DISABLED"), - u"timer": (unicode(datetime.timedelta - (milliseconds = - self.properties - [u"timeout"]) - - (datetime.datetime - .utcnow() - - isoformat_to_datetime - (max((self.properties - ["last_checked_ok"] - or - self.properties - ["created"]), - self.properties[u"last_enabled"])))) - if (self.last_checker_failed - and self.properties - [u"enabled"]) - else u"")}) + + # Base part of a client. Name! + base = (u'%(name)s: ' + % {u"name": self.properties[u"name"]}) + if not self.properties[u"enabled"]: + message = u"DISABLED" + elif self.properties[u"approved_pending"]: + if self.properties[u"approved_by_default"]: + message = u"Connection established to client. (d)eny?" + else: + message = u"Seeks approval to send secret. (a)pprove?" + elif self.last_checker_failed: + timeout = datetime.timedelta(milliseconds + = self.properties[u"timeout"]) + last_ok = isoformat_to_datetime( + max((self.properties["last_checked_ok"] + or self.properties["created"]), + self.properties[u"last_enabled"])) + timer = timeout - (datetime.datetime.utcnow() - last_ok) + message = (u'A checker has failed! Time until client gets diabled: %s' + % unicode(timer).rsplit(".", 1)[0]) + else: + message = u"enabled" + self._text = "%s%s" % (base, message) + if not urwid.supports_unicode(): self._text = self._text.encode("ascii", "replace") textlist = [(u"normal", self._text)] @@ -274,19 +295,25 @@ def keypress(self, (maxcol,), key): """Handle keys. This overrides the method from urwid.FlowWidget""" - if key == u"e" or key == u"+": - self.proxy.Enable() - elif key == u"d" or key == u"-": - self.proxy.Disable() + if key == u"+": + self.proxy.Enable(dbus_interface = client_interface) + elif key == u"-": + self.proxy.Disable(dbus_interface = client_interface) + elif key == u"a": + self.proxy.Approve(dbus.Boolean(True, variant_level=1), + dbus_interface = client_interface) + elif key == u"d": + self.proxy.Approve(dbus.Boolean(False, variant_level=1), + dbus_interface = client_interface) elif key == u"r" or key == u"_" or key == u"ctrl k": self.server_proxy_object.RemoveClient(self.proxy .object_path) elif key == u"s": - self.proxy.StartChecker() + self.proxy.StartChecker(dbus_interface = client_interface) elif key == u"S": - self.proxy.StopChecker() + self.proxy.StopChecker(dbus_interface = client_interface) elif key == u"C": - self.proxy.CheckedOK() + self.proxy.CheckedOK(dbus_interface = client_interface) # xxx # elif key == u"p" or key == "=": # self.proxy.pause() @@ -294,6 +321,10 @@ # self.proxy.unpause() # elif key == u"RET": # self.open() +# elif key == u"+": +# self.proxy.Approve(True) +# elif key == u"-": +# self.proxy.Approve(False) else: return key @@ -591,12 +622,14 @@ self.log_message_raw((u"bold", u" " .join((u"Clients:", - u"e: Enable", - u"d: Disable", + u"+: Enable", + u"-: Disable", u"r: Remove", u"s: Start new checker", u"S: Stop checker", - u"C: Checker OK")))) + u"C: Checker OK", + u"a: Approve", + u"d: Deny")))) self.refresh() elif key == u"tab": if self.topwidget.get_focus() is self.logbox: @@ -630,6 +663,8 @@ ui = UserInterface() try: ui.run() +except KeyboardInterrupt: + ui.screen.stop() except Exception, e: ui.log_message(unicode(e)) ui.screen.stop() === modified file 'plugin-runner.c' --- plugin-runner.c 2010-03-27 18:39:02 +0000 +++ plugin-runner.c 2010-09-09 22:06:10 +0000 @@ -28,7 +28,7 @@ #include /* malloc(), exit(), EXIT_SUCCESS, realloc() */ #include /* bool, true, false */ -#include /* perror, fileno(), fprintf(), +#include /* fileno(), fprintf(), stderr, STDOUT_FILENO */ #include /* DIR, fdopendir(), stat(), struct stat, waitpid(), WIFEXITED(), @@ -70,6 +70,8 @@ #include /* intmax_t, PRIdMAX, strtoimax() */ #include /* EX_OSERR, EX_USAGE, EX_IOERR, EX_CONFIG, EX_UNAVAILABLE, EX_OK */ +#include /* errno */ +#include /* error() */ #define BUFFER_SIZE 256 @@ -266,7 +268,7 @@ /* No child processes */ break; } - perror("waitpid"); + error(0, errno, "waitpid"); } /* A child exited, find it in process_list */ @@ -357,13 +359,13 @@ sigemptyset(&sigchld_action.sa_mask); ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto fallback; } ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto fallback; } @@ -599,7 +601,7 @@ case ENOMEM: default: errno = ret; - perror("argp_parse"); + error(0, errno, "argp_parse"); exitstatus = EX_OSERR; goto fallback; case EINVAL: @@ -626,7 +628,7 @@ custom_argc = 1; custom_argv = malloc(sizeof(char*) * 2); if(custom_argv == NULL){ - perror("malloc"); + error(0, errno, "malloc"); exitstatus = EX_OSERR; goto fallback; } @@ -649,7 +651,7 @@ } new_arg = strdup(p); if(new_arg == NULL){ - perror("strdup"); + error(0, errno, "strdup"); exitstatus = EX_OSERR; free(org_line); goto fallback; @@ -659,7 +661,7 @@ custom_argv = realloc(custom_argv, sizeof(char *) * ((unsigned int) custom_argc + 1)); if(custom_argv == NULL){ - perror("realloc"); + error(0, errno, "realloc"); exitstatus = EX_OSERR; free(org_line); goto fallback; @@ -672,7 +674,7 @@ ret = fclose(conffp); } while(ret == EOF and errno == EINTR); if(ret == EOF){ - perror("fclose"); + error(0, errno, "fclose"); exitstatus = EX_IOERR; goto fallback; } @@ -681,7 +683,7 @@ /* Check for harmful errors and go to fallback. Other errors might not affect opening plugins */ if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){ - perror("fopen"); + error(0, errno, "fopen"); exitstatus = EX_OSERR; goto fallback; } @@ -698,7 +700,7 @@ case ENOMEM: default: errno = ret; - perror("argp_parse"); + error(0, errno, "argp_parse"); exitstatus = EX_OSERR; goto fallback; case EINVAL: @@ -718,7 +720,7 @@ case ENOMEM: default: errno = ret; - perror("argp_parse"); + error(0, errno, "argp_parse"); exitstatus = EX_OSERR; goto fallback; case EINVAL: @@ -743,11 +745,11 @@ /* Strip permissions down to nobody */ setgid(gid); if(ret == -1){ - perror("setgid"); + error(0, errno, "setgid"); } ret = setuid(uid); if(ret == -1){ - perror("setuid"); + error(0, errno, "setuid"); } /* Open plugin directory with close_on_exec flag */ @@ -771,7 +773,7 @@ ); } if(dir_fd == -1){ - perror("Could not open plugin dir"); + error(0, errno, "Could not open plugin dir"); exitstatus = EX_UNAVAILABLE; goto fallback; } @@ -780,7 +782,7 @@ /* Set the FD_CLOEXEC flag on the directory */ ret = set_cloexec_flag(dir_fd); if(ret < 0){ - perror("set_cloexec_flag"); + error(0, errno, "set_cloexec_flag"); TEMP_FAILURE_RETRY(close(dir_fd)); exitstatus = EX_OSERR; goto fallback; @@ -789,7 +791,7 @@ dir = fdopendir(dir_fd); if(dir == NULL){ - perror("Could not open plugin dir"); + error(0, errno, "Could not open plugin dir"); TEMP_FAILURE_RETRY(close(dir_fd)); exitstatus = EX_OSERR; goto fallback; @@ -807,7 +809,7 @@ /* All directory entries have been processed */ if(dirst == NULL){ if(errno == EBADF){ - perror("readdir"); + error(0, errno, "readdir"); exitstatus = EX_IOERR; goto fallback; } @@ -870,13 +872,13 @@ dirst->d_name)); } if(ret < 0){ - perror("asprintf"); + error(0, errno, "asprintf"); continue; } ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st)); if(ret == -1){ - perror("stat"); + error(0, errno, "stat"); free(filename); continue; } @@ -894,7 +896,7 @@ plugin *p = getplugin(dirst->d_name); if(p == NULL){ - perror("getplugin"); + error(0, errno, "getplugin"); free(filename); continue; } @@ -912,13 +914,13 @@ if(g != NULL){ for(char **a = g->argv + 1; *a != NULL; a++){ if(not add_argument(p, *a)){ - perror("add_argument"); + error(0, errno, "add_argument"); } } /* Add global environment variables */ for(char **e = g->environ; *e != NULL; e++){ if(not add_environment(p, *e, false)){ - perror("add_environment"); + error(0, errno, "add_environment"); } } } @@ -929,7 +931,7 @@ if(p->environ[0] != NULL){ for(char **e = environ; *e != NULL; e++){ if(not add_environment(p, *e, false)){ - perror("add_environment"); + error(0, errno, "add_environment"); } } } @@ -937,20 +939,20 @@ int pipefd[2]; ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd)); if(ret == -1){ - perror("pipe"); + error(0, errno, "pipe"); exitstatus = EX_OSERR; goto fallback; } /* Ask OS to automatic close the pipe on exec */ ret = set_cloexec_flag(pipefd[0]); if(ret < 0){ - perror("set_cloexec_flag"); + error(0, errno, "set_cloexec_flag"); exitstatus = EX_OSERR; goto fallback; } ret = set_cloexec_flag(pipefd[1]); if(ret < 0){ - perror("set_cloexec_flag"); + error(0, errno, "set_cloexec_flag"); exitstatus = EX_OSERR; goto fallback; } @@ -959,7 +961,7 @@ &sigchld_action.sa_mask, NULL)); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); exitstatus = EX_OSERR; goto fallback; } @@ -969,7 +971,7 @@ pid = fork(); } while(pid == -1 and errno == EINTR); if(pid == -1){ - perror("fork"); + error(0, errno, "fork"); exitstatus = EX_OSERR; goto fallback; } @@ -977,18 +979,18 @@ /* this is the child process */ ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); if(ret < 0){ - perror("sigaction"); + error(0, errno, "sigaction"); _exit(EX_OSERR); } ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); _exit(EX_OSERR); } ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ - perror("dup2"); + error(0, errno, "dup2"); _exit(EX_OSERR); } @@ -999,12 +1001,12 @@ } if(p->environ[0] == NULL){ if(execv(filename, p->argv) < 0){ - perror("execv"); + error(0, errno, "execv for %s", filename); _exit(EX_OSERR); } } else { if(execve(filename, p->argv, p->environ) < 0){ - perror("execve"); + error(0, errno, "execve for %s", filename); _exit(EX_OSERR); } } @@ -1016,12 +1018,12 @@ free(filename); plugin *new_plugin = getplugin(dirst->d_name); if(new_plugin == NULL){ - perror("getplugin"); + error(0, errno, "getplugin"); ret = (int)(TEMP_FAILURE_RETRY (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL))); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); } exitstatus = EX_OSERR; goto fallback; @@ -1036,7 +1038,7 @@ &sigchld_action.sa_mask, NULL)); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); exitstatus = EX_OSERR; goto fallback; } @@ -1069,7 +1071,7 @@ fd_set rfds = rfds_all; int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL); if(select_ret == -1 and errno != EINTR){ - perror("select"); + error(0, errno, "select"); exitstatus = EX_OSERR; goto fallback; } @@ -1111,7 +1113,7 @@ &sigchld_action.sa_mask, NULL)); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); exitstatus = EX_OSERR; goto fallback; } @@ -1125,7 +1127,7 @@ (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL))); if(ret < 0){ - perror("sigprocmask"); + error(0, errno, "sigprocmask"); exitstatus = EX_OSERR; goto fallback; } @@ -1142,7 +1144,7 @@ bool bret = print_out_password(proc->buffer, proc->buffer_length); if(not bret){ - perror("print_out_password"); + error(0, errno, "print_out_password"); exitstatus = EX_IOERR; } goto fallback; @@ -1161,7 +1163,7 @@ proc->buffer = realloc(proc->buffer, proc->buffer_size + (size_t) BUFFER_SIZE); if(proc->buffer == NULL){ - perror("malloc"); + error(0, errno, "malloc"); exitstatus = EX_OSERR; goto fallback; } @@ -1204,7 +1206,7 @@ } bret = print_out_password(passwordbuffer, len); if(not bret){ - perror("print_out_password"); + error(0, errno, "print_out_password"); exitstatus = EX_IOERR; } } @@ -1212,7 +1214,7 @@ /* Restore old signal handler */ ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; } @@ -1234,7 +1236,7 @@ ret = kill(p->pid, SIGTERM); if(ret == -1 and errno != ESRCH){ /* Set-uid proccesses might not get closed */ - perror("kill"); + error(0, errno, "kill"); } } } @@ -1244,7 +1246,7 @@ ret = wait(NULL); } while(ret >= 0); if(errno != ECHILD){ - perror("wait"); + error(0, errno, "wait"); } free_plugin_list(); === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2009-10-18 16:09:05 +0000 +++ plugins.d/askpass-fifo.c 2010-09-07 16:48:58 +0000 @@ -31,7 +31,7 @@ ENOENT, EEXIST, EFAULT, EMFILE, ENFILE, ENOMEM, EBADF, EINVAL, EIO, EISDIR, EFBIG */ -#include /* perror() */ +#include /* error() */ #include /* EXIT_FAILURE, NULL, size_t, free(), realloc(), EXIT_SUCCESS */ #include /* open(), O_RDONLY */ @@ -51,7 +51,7 @@ ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); if(ret == -1){ int e = errno; - perror("mkfifo"); + error(0, errno, "mkfifo"); switch(e){ case EACCES: case ENOTDIR: @@ -73,7 +73,7 @@ int fifo_fd = open(passfifo, O_RDONLY); if(fifo_fd == -1){ int e = errno; - perror("open"); + error(0, errno, "open"); switch(e){ case EACCES: case ENOENT: @@ -101,7 +101,7 @@ if(buf_len + blocksize > buf_allocated){ char *tmp = realloc(buf, buf_allocated + blocksize); if(tmp == NULL){ - perror("realloc"); + error(0, errno, "realloc"); free(buf); return EX_OSERR; } @@ -113,7 +113,7 @@ int e = errno; free(buf); errno = e; - perror("read"); + error(0, errno, "read"); switch(e){ case EBADF: case EFAULT: @@ -141,7 +141,7 @@ int e = errno; free(buf); errno = e; - perror("write"); + error(0, errno, "write"); switch(e){ case EBADF: case EFAULT: @@ -161,7 +161,7 @@ ret = close(STDOUT_FILENO); if(ret == -1){ int e = errno; - perror("close"); + error(0, errno, "close"); switch(e){ case EBADF: return EX_OSFILE; === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2010-03-27 18:39:02 +0000 +++ plugins.d/password-prompt.c 2010-09-07 16:48:58 +0000 @@ -39,11 +39,12 @@ #include /* EXIT_SUCCESS, EXIT_FAILURE, getenv() */ #include /* fprintf(), stderr, getline(), - stdin, feof(), perror(), fputc() + stdin, feof(), fputc() */ #include /* errno, EBADF, ENOTTY, EINVAL, EFAULT, EFBIG, EIO, ENOSPC, EINTR */ +#include /* error() */ #include /* or, not */ #include /* bool, false, true */ #include /* strlen, rindex */ @@ -70,7 +71,8 @@ } int main(int argc, char **argv){ - ssize_t ret; + ssize_t sret; + int ret; size_t n; struct termios t_new, t_old; char *buffer = NULL; @@ -139,7 +141,7 @@ case ENOMEM: default: errno = ret; - perror("argp_parse"); + error(0, errno, "argp_parse"); return EX_OSERR; case EINVAL: return EX_USAGE; @@ -155,7 +157,7 @@ if(tcgetattr(STDIN_FILENO, &t_old) != 0){ int e = errno; - perror("tcgetattr"); + error(0, errno, "tcgetattr"); switch(e){ case EBADF: case ENOTTY: @@ -168,17 +170,17 @@ sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); return EX_OSERR; } ret = sigaddset(&new_action.sa_mask, SIGHUP); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); return EX_OSERR; } ret = sigaddset(&new_action.sa_mask, SIGTERM); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); return EX_OSERR; } /* Need to check if the handler is SIG_IGN before handling: @@ -187,37 +189,37 @@ */ ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); return EX_OSERR; } } @@ -231,7 +233,7 @@ t_new.c_lflag &= ~(tcflag_t)ECHO; if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ int e = errno; - perror("tcsetattr-echo"); + error(0, errno, "tcsetattr-echo"); switch(e){ case EBADF: case ENOTTY: @@ -286,11 +288,11 @@ } } } - ret = getline(&buffer, &n, stdin); - if(ret > 0){ + sret = getline(&buffer, &n, stdin); + if(sret > 0){ status = EXIT_SUCCESS; /* Make n = data size instead of allocated buffer size */ - n = (size_t)ret; + n = (size_t)sret; /* Strip final newline */ if(n > 0 and buffer[n-1] == '\n'){ buffer[n-1] = '\0'; /* not strictly necessary */ @@ -298,10 +300,10 @@ } size_t written = 0; while(written < n){ - ret = write(STDOUT_FILENO, buffer + written, n - written); - if(ret < 0){ + sret = write(STDOUT_FILENO, buffer + written, n - written); + if(sret < 0){ int e = errno; - perror("write"); + error(0, errno, "write"); switch(e){ case EBADF: case EFAULT: @@ -318,12 +320,12 @@ } break; } - written += (size_t)ret; + written += (size_t)sret; } - ret = close(STDOUT_FILENO); - if(ret == -1){ + sret = close(STDOUT_FILENO); + if(sret == -1){ int e = errno; - perror("close"); + error(0, errno, "close"); switch(e){ case EBADF: status = EX_OSFILE; @@ -336,10 +338,10 @@ } break; } - if(ret < 0){ + if(sret < 0){ int e = errno; if(errno != EINTR and not feof(stdin)){ - perror("getline"); + error(0, errno, "getline"); switch(e){ case EBADF: status = EX_UNAVAILABLE; @@ -352,7 +354,7 @@ break; } } - /* if(ret == 0), then the only sensible thing to do is to retry to + /* if(sret == 0), then the only sensible thing to do is to retry to read from stdin */ fputc('\n', stderr); if(debug and not quit_now){ @@ -368,7 +370,7 @@ fprintf(stderr, "Restoring terminal attributes\n"); } if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ - perror("tcsetattr+echo"); + error(0, errno, "tcsetattr+echo"); } if(quit_now){ @@ -376,7 +378,7 @@ old_action.sa_handler = SIG_DFL; ret = sigaction(signal_received, &old_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); } raise(signal_received); } === added file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 1970-01-01 00:00:00 +0000 +++ plugins.d/plymouth.c 2010-09-07 18:48:56 +0000 @@ -0,0 +1,405 @@ +#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */ +#include /* sig_atomic_t, struct sigaction, + sigemptyset(), sigaddset(), SIGINT, + SIGHUP, SIGTERM, sigaction(), + kill(), SIG_IGN */ +#include /* bool, false, true */ +#include /* open(), O_RDONLY */ +#include /* and, or, not*/ +#include /* size_t, ssize_t, pid_t, struct dirent, + waitpid() */ +#include /* waitpid() */ +#include /* NULL */ +#include /* strchr(), memcmp() */ +#include /* asprintf(), perror(), fopen(), fscanf() */ +#include /* close(), readlink(), read(), fork() + setsid(), chdir(), dup2() + STDERR_FILENO, execv(), access() */ +#include /* free(), EXIT_FAILURE, realloc(), + EXIT_SUCCESS, malloc(), _exit(), + getenv() */ +#include /* scandir(), alphasort() */ +#include /* intmax_t, strtoumax(), SCNuMAX */ +#include /* struct stat, lstat() */ +#include /* EX_OSERR */ +#include /* error() */ +#include /* TEMP_FAILURE_RETRY */ +#include + +sig_atomic_t interrupted_by_signal = 0; +const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid"; +const char plymouth_path[] = "/bin/plymouth"; +const char plymouthd_path[] = "/sbin/plymouthd"; +const char *plymouthd_default_argv[] = {"/sbin/plymouthd", "--mode=boot", + "--attach-to-session", + "--pid-file=/dev/.initramfs/plymouth.pid", + NULL }; + +static void termination_handler(__attribute__((unused))int signum){ + if(interrupted_by_signal){ + return; + } + interrupted_by_signal = 1; +} + +/* Create prompt string */ +char *makeprompt(void){ + int ret = 0; + char *prompt; + const char *const cryptsource = getenv("cryptsource"); + const char *const crypttarget = getenv("crypttarget"); + const char prompt_start[] = "Enter passphrase to unlock the disk"; + + if(cryptsource == NULL){ + if(crypttarget == NULL){ + ret = asprintf(&prompt, "%s: ", prompt_start); + } else { + ret = asprintf(&prompt, "%s (%s): ", prompt_start, + crypttarget); + } + } else { + if(crypttarget == NULL){ + ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); + } else { + ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, + cryptsource, crypttarget); + } + } + if(ret == -1){ + return NULL; + } + return prompt; +} + +void kill_and_wait(pid_t pid){ + TEMP_FAILURE_RETRY(kill(pid, SIGTERM)); + TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); +} + +bool become_a_daemon(void){ + int ret = setuid(geteuid()); + if(ret == -1){ + error(0, errno, "setuid"); + } + + setsid(); + ret = chdir("/"); + if(ret == -1){ + error(0, errno, "chdir"); + return false; + } + ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ + if(ret == -1){ + error(0, errno, "dup2"); + return false; + } + return true; +} + +bool exec_and_wait(pid_t *pid_return, const char *path, + const char **argv, bool interruptable, + bool daemonize){ + int status; + int ret; + pid_t pid; + pid = fork(); + if(pid == -1){ + error(0, errno, "fork"); + return false; + } + if(pid == 0){ + /* Child */ + if(daemonize){ + if(not become_a_daemon()){ + _exit(EX_OSERR); + } + } + + char **new_argv = NULL; + char *tmp; + int i = 0; + for (; argv[i]!=(char *)NULL; i++){ + tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1)); + if (tmp == NULL){ + error(0, errno, "realloc"); + free(new_argv); + _exit(EXIT_FAILURE); + } + new_argv = (char **)tmp; + new_argv[i] = strdup(argv[i]); + } + new_argv[i] = (char *) NULL; + + execv(path, (char *const *)new_argv); + error(0, errno, "execv"); + _exit(EXIT_FAILURE); + } + if(pid_return != NULL){ + *pid_return = pid; + } + do { + ret = waitpid(pid, &status, 0); + } while(ret == -1 and errno == EINTR + and ((not interrupted_by_signal) + or (not interruptable))); + if(interrupted_by_signal and interruptable){ + return false; + } + if(ret == -1){ + error(0, errno, "waitpid"); + return false; + } + if(WIFEXITED(status) and WEXITSTATUS(status) == 0){ + return true; + } + return false; +} + +int is_plymouth(const struct dirent *proc_entry){ + int ret; + { + uintmax_t maxvalue; + char *tmp; + errno = 0; + maxvalue = strtoumax(proc_entry->d_name, &tmp, 10); + + if(errno != 0 or *tmp != '\0' or maxvalue != (uintmax_t)((pid_t)maxvalue)){ + return 0; + } + } + char exe_target[sizeof(plymouth_path)]; + char *exe_link; + ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name); + if(ret == -1){ + error(0, errno, "asprintf"); + return 0; + } + + struct stat exe_stat; + ret = lstat(exe_link, &exe_stat); + if(ret == -1){ + free(exe_link); + if(errno != ENOENT){ + error(0, errno, "lstat"); + } + return 0; + } + + if(not S_ISLNK(exe_stat.st_mode) + or exe_stat.st_uid != 0 + or exe_stat.st_gid != 0){ + free(exe_link); + return 0; + } + + ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target)); + free(exe_link); + if((sret != (ssize_t)sizeof(plymouth_path)-1) or + (memcmp(plymouth_path, exe_target, + sizeof(plymouth_path)-1) != 0)){ + return 0; + } + return 1; +} + +pid_t get_pid(void){ + int ret; + FILE *pidfile = fopen(plymouth_pid, "r"); + uintmax_t maxvalue = 0; + if(pidfile != NULL){ + ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue); + if(ret != 1){ + maxvalue = 0; + } + fclose(pidfile); + } + if(maxvalue == 0){ + struct dirent **direntries; + ret = scandir("/proc", &direntries, is_plymouth, alphasort); + sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue); + } + pid_t pid; + pid = (pid_t)maxvalue; + if((uintmax_t)pid == maxvalue){ + return pid; + } + + return 0; +} + +const char **getargv(pid_t pid){ + int cl_fd; + char *cmdline_filename; + ssize_t sret; + int ret; + + ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline", + (uintmax_t)pid); + if(ret == -1){ + error(0, errno, "asprintf"); + return NULL; + } + + /* Open /proc//cmdline */ + cl_fd = open(cmdline_filename, O_RDONLY); + free(cmdline_filename); + if(cl_fd == -1){ + error(0, errno, "open"); + return NULL; + } + + size_t cmdline_allocated = 0; + size_t cmdline_len = 0; + char *cmdline = NULL; + char *tmp; + const size_t blocksize = 1024; + do { + /* Allocate more space? */ + if(cmdline_len + blocksize > cmdline_allocated){ + tmp = realloc(cmdline, cmdline_allocated + blocksize); + if(tmp == NULL){ + error(0, errno, "realloc"); + free(cmdline); + close(cl_fd); + return NULL; + } + cmdline = tmp; + cmdline_allocated += blocksize; + } + + /* Read data */ + sret = read(cl_fd, cmdline + cmdline_len, + cmdline_allocated - cmdline_len); + if(sret == -1){ + error(0, errno, "read"); + free(cmdline); + close(cl_fd); + return NULL; + } + cmdline_len += (size_t)sret; + } while(sret != 0); + ret = close(cl_fd); + if(ret == -1){ + error(0, errno, "close"); + free(cmdline); + return NULL; + } + + /* we got cmdline and cmdline_len, ignore rest... */ + const char **argv = NULL; + size_t argv_size = 0; + for(char *arg = cmdline; arg-cmdline < (ssize_t)cmdline_len; + arg = strchr(arg, '\0')+1){ + tmp = realloc(argv, ((++argv_size)+1)*sizeof(char *)); + if(tmp == NULL){ + error(0, errno, "realloc"); + free(argv); + return NULL; + } + argv = (const char **)tmp; + argv[argv_size-1] = arg; + } + argv[argv_size] = NULL; + return argv; +} + +int main(__attribute__((unused))int argc, + __attribute__((unused))char **argv){ + char *prompt; + char *prompt_arg; + pid_t plymouth_command_pid; + int ret; + bool bret; + + /* test -x /bin/plymouth */ + ret = access(plymouth_path, X_OK); + if(ret == -1){ + exit(EXIT_FAILURE); + } + + { /* Add signal handlers */ + struct sigaction old_action, + new_action = { .sa_handler = termination_handler, + .sa_flags = 0 }; + sigemptyset(&new_action.sa_mask); + for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; *sig != 0; sig++){ + ret = sigaddset(&new_action.sa_mask, *sig); + if(ret == -1){ + error(0, errno, "sigaddset"); + exit(EX_OSERR); + } + ret = sigaction(*sig, NULL, &old_action); + if(ret == -1){ + error(0, errno, "sigaction"); + exit(EX_OSERR); + } + if(old_action.sa_handler != SIG_IGN){ + ret = sigaction(*sig, &new_action, NULL); + if(ret == -1){ + error(0, errno, "sigaction"); + exit(EX_OSERR); + } + } + } + } + + /* plymouth --ping */ + bret = exec_and_wait(&plymouth_command_pid, plymouth_path, + (const char *[]){ (const char *)plymouth_path, (const char *)"--ping", (const char *)NULL}, + true, false); + if(not bret){ + if(interrupted_by_signal){ + kill_and_wait(plymouth_command_pid); + } + exit(EXIT_FAILURE); + } + + prompt = makeprompt(); + ret = asprintf(&prompt_arg, "--prompt=%s", prompt); + free(prompt); + if(ret == -1){ + error(0, errno, "asprintf"); + exit(EXIT_FAILURE); + } + + /* plymouth ask-for-password --prompt="$prompt" */ + bret = exec_and_wait(&plymouth_command_pid, plymouth_path, + (const char *[]){plymouth_path, "ask-for-password", prompt_arg, NULL}, + true, false); + free(prompt_arg); + if(not bret){ + if(interrupted_by_signal){ + kill_and_wait(plymouth_command_pid); + } else { + exit(EXIT_FAILURE); + } + } + + if(bret){ + exit(EXIT_SUCCESS); + } + + const char **plymouthd_argv = NULL; + pid_t pid = get_pid(); + if(pid == 0){ + error(0, 0, "plymouthd pid not found"); + } else { + plymouthd_argv = getargv(pid); + } + if(plymouthd_argv == NULL){ + plymouthd_argv = plymouthd_default_argv; + } + + bret = exec_and_wait(NULL, plymouth_path, + (const char *[]){plymouth_path, "quit", NULL}, false, false); + if(not bret){ + exit(EXIT_FAILURE); + } + bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, false, true); + if(not bret){ + exit(EXIT_FAILURE); + } + exec_and_wait(NULL, plymouth_path, + (const char *[]){ plymouth_path, "show-splash", NULL }, false, false); + exit(EXIT_FAILURE); +} === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2009-10-18 16:09:05 +0000 +++ plugins.d/splashy.c 2010-09-07 16:48:58 +0000 @@ -29,7 +29,7 @@ SIG_IGN, kill(), SIGKILL */ #include /* NULL */ #include /* getenv() */ -#include /* asprintf(), perror() */ +#include /* asprintf() */ #include /* EXIT_FAILURE, free(), EXIT_SUCCESS */ #include /* pid_t, DIR, struct dirent, @@ -49,6 +49,7 @@ E2BIG, EFAULT, EIO, ETXTBSY, EISDIR, ELIBBAD, EPERM, EINTR, ECHILD */ +#include /* error() */ #include /* waitpid(), WIFEXITED(), WEXITSTATUS() */ #include /* EX_OSERR, EX_OSFILE, @@ -109,7 +110,7 @@ proc_dir = opendir("/proc"); if(proc_dir == NULL){ int e = errno; - perror("opendir"); + error(0, errno, "opendir"); switch(e){ case EACCES: case ENOTDIR: @@ -151,7 +152,7 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); if(ret == -1){ - perror("asprintf"); + error(0, errno, "asprintf"); exitstatus = EX_OSERR; goto failure; } @@ -165,7 +166,7 @@ continue; } int e = errno; - perror("lstat"); + error(0, errno, "lstat"); free(exe_link); switch(e){ case EACCES: @@ -213,60 +214,60 @@ sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGHUP); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGTERM); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); exitstatus = EX_OSERR; goto failure; } ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); exitstatus = EX_OSERR; goto failure; } @@ -283,7 +284,7 @@ goto failure; } if(splashy_command_pid == -1){ - perror("fork"); + error(0, errno, "fork"); exitstatus = EX_OSERR; goto failure; } @@ -293,7 +294,7 @@ const char splashy_command[] = "/sbin/splashy_update"; execl(splashy_command, splashy_command, prompt, (char *)NULL); int e = errno; - perror("execl"); + error(0, errno, "execl"); switch(e){ case EACCES: case ENOENT: @@ -341,7 +342,7 @@ goto failure; } if(ret == -1){ - perror("waitpid"); + error(0, errno, "waitpid"); if(errno == ECHILD){ splashy_command_pid = 0; } @@ -379,27 +380,27 @@ the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ - perror("setuid"); + error(0, errno, "setuid"); } setsid(); ret = chdir("/"); if(ret == -1){ - perror("chdir"); + error(0, errno, "chdir"); } /* if(fork() != 0){ */ /* _exit(EXIT_SUCCESS); */ /* } */ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */ if(ret == -1){ - perror("dup2"); + error(0, errno, "dup2"); _exit(EX_OSERR); } execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); { int e = errno; - perror("execl"); + error(0, errno, "execl"); switch(e){ case EACCES: case ENOENT: @@ -425,13 +426,13 @@ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, &signal_action, NULL)); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); } do { ret = raise(signal_received); } while(ret != 0 and errno == EINTR); if(ret != 0){ - perror("raise"); + error(0, errno, "raise"); abort(); } TEMP_FAILURE_RETRY(pause()); === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2010-03-27 18:39:02 +0000 +++ plugins.d/usplash.c 2010-09-07 16:48:58 +0000 @@ -31,19 +31,20 @@ #include /* open(), O_WRONLY, O_RDONLY */ #include /* and, or, not*/ #include /* errno, EINTR */ +#include #include /* size_t, ssize_t, pid_t, DIR, struct dirent */ #include /* NULL */ #include /* strlen(), memcmp() */ -#include /* asprintf(), perror() */ +#include /* asprintf()*/ #include /* close(), write(), readlink(), read(), STDOUT_FILENO, sleep(), fork(), setuid(), geteuid(), setsid(), chdir(), dup2(), STDERR_FILENO, execv() */ #include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit() */ -#include /* getenv() */ + EXIT_SUCCESS, malloc(), _exit(), + getenv() */ #include /* opendir(), readdir(), closedir() */ #include /* intmax_t, strtoimax() */ #include /* struct stat, lstat(), S_ISLNK */ @@ -153,7 +154,7 @@ size_t cmdline_len = 0; DIR *proc_dir = opendir("/proc"); if(proc_dir == NULL){ - perror("opendir"); + error(0, errno, "opendir"); return -1; } errno = 0; @@ -181,7 +182,7 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); if(ret == -1){ - perror("asprintf"); + error(0, errno, "asprintf"); goto fail_find_usplash; } @@ -193,7 +194,7 @@ free(exe_link); continue; } - perror("lstat"); + error(0, errno, "lstat"); free(exe_link); goto fail_find_usplash; } @@ -224,13 +225,13 @@ ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_ent->d_name); if(ret == -1){ - perror("asprintf"); + error(0, errno, "asprintf"); goto fail_find_usplash; } cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); if(cl_fd == -1){ - perror("open"); + error(0, errno, "open"); goto fail_find_usplash; } } @@ -242,7 +243,7 @@ if(cmdline_len + blocksize > cmdline_allocated){ tmp = realloc(cmdline, cmdline_allocated + blocksize); if(tmp == NULL){ - perror("realloc"); + error(0, errno, "realloc"); close(cl_fd); goto fail_find_usplash; } @@ -253,7 +254,7 @@ sret = read(cl_fd, cmdline + cmdline_len, cmdline_allocated - cmdline_len); if(sret == -1){ - perror("read"); + error(0, errno, "read"); close(cl_fd); goto fail_find_usplash; } @@ -261,14 +262,14 @@ } while(sret != 0); ret = close(cl_fd); if(ret == -1){ - perror("close"); + error(0, errno, "close"); goto fail_find_usplash; } } /* Close directory */ ret = closedir(proc_dir); if(ret == -1){ - perror("closedir"); + error(0, errno, "closedir"); goto fail_find_usplash; } /* Success */ @@ -323,26 +324,26 @@ sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGHUP); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaddset(&new_action.sa_mask, SIGTERM); if(ret == -1){ - perror("sigaddset"); + error(0, errno, "sigaddset"); status = EX_OSERR; goto failure; } ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -351,7 +352,7 @@ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -360,7 +361,7 @@ ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -369,7 +370,7 @@ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -378,7 +379,7 @@ ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -387,7 +388,7 @@ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ if(errno != EINTR){ - perror("sigaction"); + error(0, errno, "sigaction"); status = EX_OSERR; } goto failure; @@ -399,7 +400,7 @@ /* Write command to FIFO */ if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){ if(errno != EINTR){ - perror("usplash_write"); + error(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -411,7 +412,7 @@ if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){ if(errno != EINTR){ - perror("usplash_write"); + error(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -429,7 +430,7 @@ outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); if(outfifo_fd == -1){ if(errno != EINTR){ - perror("open"); + error(0, errno, "open"); status = EX_OSERR; } goto failure; @@ -448,7 +449,7 @@ char *tmp = realloc(buf, buf_allocated + blocksize); if(tmp == NULL){ if(errno != EINTR){ - perror("realloc"); + error(0, errno, "realloc"); status = EX_OSERR; } goto failure; @@ -460,7 +461,7 @@ buf_allocated - buf_len); if(sret == -1){ if(errno != EINTR){ - perror("read"); + error(0, errno, "read"); status = EX_OSERR; } TEMP_FAILURE_RETRY(close(outfifo_fd)); @@ -475,7 +476,7 @@ ret = close(outfifo_fd); if(ret == -1){ if(errno != EINTR){ - perror("close"); + error(0, errno, "close"); status = EX_OSERR; } goto failure; @@ -488,7 +489,7 @@ if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){ if(errno != EINTR){ - perror("usplash_write"); + error(0, errno, "usplash_write"); status = EX_OSERR; } goto failure; @@ -501,7 +502,7 @@ ret = close(fifo_fd); if(ret == -1){ if(errno != EINTR){ - perror("close"); + error(0, errno, "close"); status = EX_OSERR; } goto failure; @@ -515,7 +516,7 @@ sret = write(STDOUT_FILENO, buf + written, buf_len - written); if(sret == -1){ if(errno != EINTR){ - perror("write"); + error(0, errno, "write"); status = EX_OSERR; } goto failure; @@ -552,7 +553,7 @@ if(fifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ - perror("close"); + error(0, errno, "close"); } fifo_fd = -1; } @@ -561,7 +562,7 @@ if(outfifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd)); if(ret == -1){ - perror("close"); + error(0, errno, "close"); } } @@ -575,7 +576,7 @@ (sizeof(char *) * (size_t)(cmdline_argc + 2))); if(tmp == NULL){ - perror("realloc"); + error(0, errno, "realloc"); free(cmdline_argv); return status; } @@ -602,23 +603,27 @@ the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ - perror("setuid"); + error(0, errno, "setuid"); } setsid(); ret = chdir("/"); + if(ret == -1){ + error(0, errno, "chdir"); + _exit(EX_OSERR); + } /* if(fork() != 0){ */ /* _exit(EXIT_SUCCESS); */ /* } */ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ - perror("dup2"); + error(0, errno, "dup2"); _exit(EX_OSERR); } execv(usplash_name, cmdline_argv); if(not interrupted_by_signal){ - perror("execv"); + error(0, errno, "execv"); } free(cmdline); free(cmdline_argv); @@ -629,7 +634,7 @@ sleep(2); if(not usplash_write(&fifo_fd, "PULSATE", NULL)){ if(errno != EINTR){ - perror("usplash_write"); + error(0, errno, "usplash_write"); } } @@ -637,7 +642,7 @@ if(fifo_fd != -1){ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ - perror("close"); + error(0, errno, "close"); } fifo_fd = -1; } @@ -648,13 +653,13 @@ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, &signal_action, NULL)); if(ret == -1){ - perror("sigaction"); + error(0, errno, "sigaction"); } do { ret = raise(signal_received); } while(ret != 0 and errno == EINTR); if(ret != 0){ - perror("raise"); + error(0, errno, "raise"); abort(); } TEMP_FAILURE_RETRY(pause());