=== modified file 'Makefile' --- Makefile 2008-10-28 18:00:20 +0000 +++ Makefile 2009-01-05 23:26:06 +0000 @@ -45,7 +45,7 @@ CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \ $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \ -DVERSION='"$(version)"' -LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) +LDFLAGS=$(COVERAGE) $(foreach flag,$(LINK_FORTIFY),-Xlinker $(flag)) # Commands to format a DocBook document into a manual page DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \ === modified file 'README' --- README 2008-10-28 18:00:20 +0000 +++ README 2009-01-04 21:54:55 +0000 @@ -160,8 +160,8 @@ * Copyright - Copyright © 2008 Teddy Hogeborn - Copyright © 2008 Björn Påhlsson + Copyright © 2008,2009 Teddy Hogeborn + Copyright © 2008,2009 Björn Påhlsson ** License: === modified file 'TODO' --- TODO 2008-10-28 18:00:20 +0000 +++ TODO 2008-12-29 02:44:54 +0000 @@ -6,6 +6,9 @@ klogctl(6, NULL, 0); klogctl(7, NULL, 0); ** TODO [#C] IPv4 support +* plugin-runner +** TODO [#B] use scandir(3) instead of readdir(3) + * mandos (server) ** TODO [#B] Log level :bugs: ** TODO /etc/mandos/clients.d/*.conf @@ -14,6 +17,12 @@ ** TODO [#B] Run-time communication with server :bugs: Probably using D-Bus See also [[*Mandos-tools]] +*** Client class +*** Main server + + SetLogLevel + syslogger.setLevel(logging.WARNING) + + Quit + + [[http://log.ometer.com/2007-05.html][Best D-Bus practices]] ** TODO Implement --foreground :bugs: [[info:standards:Option%20Table][Table of Long Options]] ** TODO Implement --socket @@ -21,14 +30,23 @@ ** TODO Date+time on console log messages :bugs: Is this the default? ** TODO delete hook when clients fall out by timeout - -* Mandos-tools/utilities - All of this probably using D-Bus -** TODO List clients + This will not be strictly necessary when the D-Bus interface is + implemented. + +* mandos.xml +** [[file:mandos.xml::XXX][Document D-Bus interface]] + +* Provide and install /etc/dbus-1/system.d/mandos.conf + +* mandos-list +*** Handle "no D-Bus server" and/or "no Mandos server found" better +*** [#B] --dump option ** TODO Disable client ** TODO Enable client ** TODO Reset timer +* Curses interface + * mandos-keygen ** TODO "--secfile" option Using the "secfile" option instead of "secret" === modified file 'debian/control' --- debian/control 2008-09-30 03:19:39 +0000 +++ debian/control 2009-01-04 21:20:50 +0000 @@ -4,9 +4,9 @@ Maintainer: Mandos Maintainers Uploaders: Teddy Hogeborn , Björn Påhlsson -Build-Depends: debhelper (>= 7), docbook-xsl, libavahi-core-dev, - libgpgme11-dev, libgnutls-dev, xsltproc, po-debconf, - pkg-config +Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl, + libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc, + po-debconf, pkg-config Standards-Version: 3.8.0 Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files === modified file 'debian/copyright' --- debian/copyright 2008-10-15 19:54:11 +0000 +++ debian/copyright 2009-01-04 21:54:55 +0000 @@ -5,8 +5,8 @@ Upstream-Source: Files: * -Copyright: Copyright © 2008 Teddy Hogeborn -Copyright: Copyright © 2008 Björn Påhlsson +Copyright: Copyright © 2008,2009 Teddy Hogeborn +Copyright: Copyright © 2008,2009 Björn Påhlsson License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2008-09-26 04:54:35 +0000 +++ debian/mandos-client.postinst 2008-12-10 01:26:02 +0000 @@ -27,10 +27,19 @@ # Add user and group add_mandos_user(){ - if ! getent passwd mandos >/dev/null; then - adduser --disabled-password --quiet --system \ - --home /nonexistent --no-create-home \ - --gecos "Mandos password system" --group mandos + # Rename old "mandos" user and group + case "$(getent passwd mandos)" in + *:Mandos\ password\ system,,,:/nonexistent:/bin/false) + usermod --login _mandos mandos + groupmod --new-name _mandos mandos + return + ;; + esac + # Create new user and group + if ! getent passwd _mandos >/dev/null; then + adduser --system --force-badname --quiet --home /nonexistent \ + --no-create-home --group --disabled-password \ + --gecos "Mandos password system" _mandos fi } === removed file 'debian/mandos-client.templates' --- debian/mandos-client.templates 2008-09-21 04:22:50 +0000 +++ debian/mandos-client.templates 1970-01-01 00:00:00 +0000 @@ -1,8 +0,0 @@ -Template: mandos-client/not-yet-configured -Type: note -_Description: Your system needs more configuration[ mandos-client] - Your system can not function as a Mandos client until a - password for this client has been added to the - configuration on the Mandos server. Please read - /usr/share/doc/mandos-client/README.Debian.gz to find out - how. === modified file 'debian/mandos.README.Debian' --- debian/mandos.README.Debian 2008-09-21 04:22:50 +0000 +++ debian/mandos.README.Debian 2009-01-04 22:15:01 +0000 @@ -1,7 +1,7 @@ -The Mandos server cannot run without at least one configured client in +The Mandos server is useless without at least one configured client in /etc/mandos/clients.conf. To create one, install the "mandos-client" package on a client computer, and run "mandos-keygen --password" there to get a config file stanza. Append that to /etc/mandos/clients.conf on the Mandos server. - -- Teddy Hogeborn , Sat, 20 Sep 2008 21:21:19 +0200 + -- Teddy Hogeborn , Sun, 4 Jan 2009 22:59:22 +0100 === modified file 'debian/mandos.postinst' --- debian/mandos.postinst 2008-09-26 04:54:35 +0000 +++ debian/mandos.postinst 2008-12-10 01:26:02 +0000 @@ -19,10 +19,19 @@ case "$1" in configure) - if ! getent passwd mandos >/dev/null; then - adduser --disabled-password --quiet --system \ - --home /nonexistent --no-create-home \ - --gecos "Mandos password system" --group mandos + # Rename old "mandos" user and group + case "$(getent passwd mandos)" in + *:Mandos\ password\ system,,,:/nonexistent:/bin/false) + usermod --login _mandos mandos + groupmod --new-name _mandos mandos + ;; + esac + # Create new user and group + if ! getent passwd _mandos >/dev/null; then + adduser --system --force-badname --quiet \ + --home /nonexistent --no-create-home --group \ + --disabled-password --gecos "Mandos password system" \ + _mandos fi ;; === removed file 'debian/mandos.templates' --- debian/mandos.templates 2008-09-21 04:22:50 +0000 +++ debian/mandos.templates 1970-01-01 00:00:00 +0000 @@ -1,9 +0,0 @@ -Template: mandos/not-yet-configured -Type: note -_Description: Your system needs more configuration[ mandos] - Your system has not yet been completely configured as a - Mandos server - clients need to be added to to - /etc/mandos/clients.conf. Please read - /usr/share/doc/mandos/README.Debian.gz to find out how. - . - (The server has not been started.) === removed file 'debian/po/POTFILES.in' --- debian/po/POTFILES.in 2008-09-21 04:22:50 +0000 +++ debian/po/POTFILES.in 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -[type: gettext/rfc822deb] mandos.templates -[type: gettext/rfc822deb] mandos-client.templates === removed file 'debian/po/sv.po' --- debian/po/sv.po 2008-09-21 04:22:50 +0000 +++ debian/po/sv.po 1970-01-01 00:00:00 +0000 @@ -1,66 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the mandos package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: 1.0\n" -"Report-Msgid-Bugs-To: mandos@packages.debian.org\n" -"POT-Creation-Date: 2008-09-20 23:01+0200\n" -"PO-Revision-Date: 2008-09-21 06:01+0200\n" -"Last-Translator: Teddy Hogeborn \n" -"Language-Team: Swedish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: note -#. Description -#: ../mandos.templates:1001 -msgid "Your system needs more configuration[ mandos]" -msgstr "Ditt system behöver ytterligare konfigurering" - -#. Type: note -#. Description -#: ../mandos.templates:1001 -#| msgid "" -#| "Your system has not yet been completely configured as a Mandos server - " -#| "you need to setup /etc/mandos/clients.conf. Please read /usr/share/doc/" -#| "mandos/README.Debian.gz to find out how." -msgid "" -"Your system has not yet been completely configured as a Mandos server - " -"clients need to be added to to /etc/mandos/clients.conf. Please read /usr/" -"share/doc/mandos/README.Debian.gz to find out how." -msgstr "" -"Ditt system är inte helt inställd som en Mandos-server än -\n" -"det behövs läggas till klienter i Mandos-serverns\n" -"inställingar. Var vänlig läs\n" -"/usr/share/doc/mandos-client/README.Debian.gz för att få\n" -"veta hur." - -#. Type: note -#. Description -#: ../mandos.templates:1001 -msgid "(The server has not been started.)" -msgstr "(Servern har inte startats.)" - -#. Type: note -#. Description -#: ../mandos-client.templates:1001 -msgid "Your system needs more configuration[ mandos-client]" -msgstr "Ditt system behöver ytterligare konfigurering" - -#. Type: note -#. Description -#: ../mandos-client.templates:1001 -msgid "" -"Your system can not function as a Mandos client until a password for this " -"client has been added to the configuration on the Mandos server. Please " -"read /usr/share/doc/mandos-client/README.Debian.gz to find out how." -msgstr "" -"Ditt system kan inte fungera som en Mandos-klient förrän\n" -"ett krypterat lösenord har lagts till i Mandos-serverns\n" -"inställingar. Var vänlig läs\n" -"/usr/share/doc/mandos-client/README.Debian.gz för att få\n" -"veta hur." === modified file 'debian/rules' --- debian/rules 2008-09-30 18:59:44 +0000 +++ debian/rules 2009-01-04 22:26:07 +0000 @@ -38,7 +38,6 @@ rm -f build-arch-stamp build-indep-stamp configure-stamp dh_auto_clean dh_clean - debconf-updatepo install: install-indep install-arch install-indep: @@ -66,7 +65,6 @@ dh_testroot dh_installchangelogs dh_installdocs - dh_installdebconf dh_link dh_strip dh_compress === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2008-11-01 02:26:00 +0000 +++ initramfs-tools-hook 2008-12-10 01:26:02 +0000 @@ -51,11 +51,13 @@ exit 1 fi -mandos_user="`{ getent passwd mandos \ +mandos_user="`{ getent passwd _mandos \ + || getent passwd mandos \ || getent passwd nobody \ || echo ::65534::::; } \ | awk --field-separator=: '{ print $3 }'`" -mandos_group="`{ getent group mandos \ +mandos_group="`{ getent group _mandos \ + || getent group mandos \ || getent group nogroup \ || echo ::65534:; } \ | awk --field-separator=: '{ print $3 }'`" === modified file 'mandos' --- mandos 2008-11-08 17:26:35 +0000 +++ mandos 2009-01-05 23:26:06 +0000 @@ -11,7 +11,8 @@ # and some lines in "main". # # Everything else is -# Copyright © 2008 Teddy Hogeborn & Björn Påhlsson +# Copyright © 2008,2009 Teddy Hogeborn +# Copyright © 2008,2009 Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -34,7 +35,7 @@ import SocketServer import socket -from optparse import OptionParser +import optparse import datetime import errno import gnutls.crypto @@ -58,6 +59,7 @@ from contextlib import closing import dbus +import dbus.service import gobject import avahi from dbus.mainloop.glib import DBusGMainLoop @@ -67,11 +69,11 @@ version = "1.0.2" logger = logging.Logger('mandos') -syslogger = logging.handlers.SysLogHandler\ - (facility = logging.handlers.SysLogHandler.LOG_DAEMON, - address = "/dev/log") -syslogger.setFormatter(logging.Formatter\ - ('Mandos: %(levelname)s: %(message)s')) +syslogger = (logging.handlers.SysLogHandler + (facility = logging.handlers.SysLogHandler.LOG_DAEMON, + address = "/dev/log")) +syslogger.setFormatter(logging.Formatter + ('Mandos: %(levelname)s: %(message)s')) logger.addHandler(syslogger) console = logging.StreamHandler() @@ -80,11 +82,11 @@ logger.addHandler(console) class AvahiError(Exception): - def __init__(self, value): + def __init__(self, value, *args, **kwargs): self.value = value - super(AvahiError, self).__init__() - def __str__(self): - return repr(self.value) + super(AvahiError, self).__init__(value, *args, **kwargs) + def __unicode__(self): + return unicode(repr(self.value)) class AvahiServiceError(AvahiError): pass @@ -110,16 +112,13 @@ a sensible number of times """ def __init__(self, interface = avahi.IF_UNSPEC, name = None, - servicetype = None, port = None, TXT = None, domain = "", - host = "", max_renames = 32768): + servicetype = None, port = None, TXT = None, + domain = "", host = "", max_renames = 32768): self.interface = interface self.name = name self.type = servicetype self.port = port - if TXT is None: - self.TXT = [] - else: - self.TXT = TXT + self.TXT = TXT if TXT is not None else [] self.domain = domain self.host = host self.rename_count = 0 @@ -130,13 +129,13 @@ logger.critical(u"No suitable Zeroconf service name found" u" after %i retries, exiting.", self.rename_count) - raise AvahiServiceError("Too many renames") + raise AvahiServiceError(u"Too many renames") self.name = server.GetAlternativeServiceName(self.name) logger.info(u"Changing Zeroconf service name to %r ...", str(self.name)) - syslogger.setFormatter(logging.Formatter\ + syslogger.setFormatter(logging.Formatter ('Mandos (%s): %%(levelname)s:' - ' %%(message)s' % self.name)) + ' %%(message)s' % self.name)) self.remove() self.add() self.rename_count += 1 @@ -148,10 +147,10 @@ """Derived from the Avahi example code""" global group if group is None: - group = dbus.Interface\ - (bus.get_object(avahi.DBUS_NAME, + group = dbus.Interface(bus.get_object + (avahi.DBUS_NAME, server.EntryGroupNew()), - avahi.DBUS_INTERFACE_ENTRY_GROUP) + avahi.DBUS_INTERFACE_ENTRY_GROUP) group.connect_to_signal('StateChanged', entry_group_state_changed) logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...", @@ -171,156 +170,199 @@ # End of Avahi example code -class Client(object): +def _datetime_to_dbus(dt, variant_level=0): + """Convert a UTC datetime.datetime() to a D-Bus type.""" + return dbus.String(dt.isoformat(), variant_level=variant_level) + + +class Client(dbus.service.Object): """A representation of a client host served by this server. Attributes: - name: string; from the config file, used in log messages + name: string; from the config file, used in log messages fingerprint: string (40 or 32 hexadecimal digits); used to uniquely identify the client - secret: bytestring; sent verbatim (over TLS) to client - host: string; available for use by the checker command - created: datetime.datetime(); object creation, not client host - last_checked_ok: datetime.datetime() or None if not yet checked OK - timeout: datetime.timedelta(); How long from last_checked_ok - until this client is invalid - interval: datetime.timedelta(); How often to start a new checker - stop_hook: If set, called by stop() as stop_hook(self) - checker: subprocess.Popen(); a running checker process used - to see if the client lives. - 'None' if no process is running. + secret: bytestring; sent verbatim (over TLS) to client + host: string; available for use by the checker command + created: datetime.datetime(); (UTC) object creation + last_enabled: datetime.datetime(); (UTC) + enabled: bool() + last_checked_ok: datetime.datetime(); (UTC) or None + timeout: datetime.timedelta(); How long from last_checked_ok + until this client is invalid + interval: datetime.timedelta(); How often to start a new checker + disable_hook: If set, called by disable() as disable_hook(self) + checker: subprocess.Popen(); a running checker process used + to see if the client lives. + 'None' if no process is running. checker_initiator_tag: a gobject event source tag, or None - stop_initiator_tag: - '' - + disable_initiator_tag: - '' - checker_callback_tag: - '' - checker_command: string; External command which is run to check if client lives. %() expansions are done at runtime with vars(self) as dict, so that for instance %(name)s can be used in the command. - Private attibutes: - _timeout: Real variable for 'timeout' - _interval: Real variable for 'interval' - _timeout_milliseconds: Used when calling gobject.timeout_add() - _interval_milliseconds: - '' - + use_dbus: bool(); Whether to provide D-Bus interface and signals + dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus """ - def _set_timeout(self, timeout): - "Setter function for 'timeout' attribute" - self._timeout = timeout - self._timeout_milliseconds = ((self.timeout.days - * 24 * 60 * 60 * 1000) - + (self.timeout.seconds * 1000) - + (self.timeout.microseconds - // 1000)) - timeout = property(lambda self: self._timeout, - _set_timeout) - del _set_timeout - def _set_interval(self, interval): - "Setter function for 'interval' attribute" - self._interval = interval - self._interval_milliseconds = ((self.interval.days - * 24 * 60 * 60 * 1000) - + (self.interval.seconds - * 1000) - + (self.interval.microseconds - // 1000)) - interval = property(lambda self: self._interval, - _set_interval) - del _set_interval - def __init__(self, name = None, stop_hook=None, config=None): + def timeout_milliseconds(self): + "Return the 'timeout' attribute in milliseconds" + return ((self.timeout.days * 24 * 60 * 60 * 1000) + + (self.timeout.seconds * 1000) + + (self.timeout.microseconds // 1000)) + + def interval_milliseconds(self): + "Return the 'interval' attribute in milliseconds" + return ((self.interval.days * 24 * 60 * 60 * 1000) + + (self.interval.seconds * 1000) + + (self.interval.microseconds // 1000)) + + def __init__(self, name = None, disable_hook=None, config=None, + use_dbus=True): """Note: the 'checker' key in 'config' sets the 'checker_command' attribute and *not* the 'checker' attribute.""" + self.name = name if config is None: config = {} - self.name = name logger.debug(u"Creating client %r", self.name) + self.use_dbus = use_dbus + if self.use_dbus: + self.dbus_object_path = (dbus.ObjectPath + ("/Mandos/clients/" + + self.name.replace(".", "_"))) + dbus.service.Object.__init__(self, bus, + self.dbus_object_path) # Uppercase and remove spaces from fingerprint for later # comparison purposes with return value from the fingerprint() # function - self.fingerprint = config["fingerprint"].upper()\ - .replace(u" ", u"") + self.fingerprint = (config["fingerprint"].upper() + .replace(u" ", u"")) logger.debug(u" Fingerprint: %s", self.fingerprint) if "secret" in config: self.secret = config["secret"].decode(u"base64") elif "secfile" in config: with closing(open(os.path.expanduser (os.path.expandvars - (config["secfile"])))) \ - as secfile: + (config["secfile"])))) as secfile: self.secret = secfile.read() else: raise TypeError(u"No secret or secfile for client %s" % self.name) self.host = config.get("host", "") - self.created = datetime.datetime.now() + self.created = datetime.datetime.utcnow() + self.enabled = False + self.last_enabled = None self.last_checked_ok = None self.timeout = string_to_delta(config["timeout"]) self.interval = string_to_delta(config["interval"]) - self.stop_hook = stop_hook + self.disable_hook = disable_hook self.checker = None self.checker_initiator_tag = None - self.stop_initiator_tag = None + self.disable_initiator_tag = None self.checker_callback_tag = None - self.check_command = config["checker"] - def start(self): + self.checker_command = config["checker"] + + def enable(self): """Start this client's checker and timeout hooks""" + self.last_enabled = datetime.datetime.utcnow() # Schedule a new checker to be started an 'interval' from now, # and every interval from then on. - self.checker_initiator_tag = gobject.timeout_add\ - (self._interval_milliseconds, - self.start_checker) + self.checker_initiator_tag = (gobject.timeout_add + (self.interval_milliseconds(), + self.start_checker)) # Also start a new checker *right now*. self.start_checker() - # Schedule a stop() when 'timeout' has passed - self.stop_initiator_tag = gobject.timeout_add\ - (self._timeout_milliseconds, - self.stop) - def stop(self): - """Stop this client. - The possibility that a client might be restarted is left open, - but not currently used.""" - # If this client doesn't have a secret, it is already stopped. - if hasattr(self, "secret") and self.secret: - logger.info(u"Stopping client %s", self.name) - self.secret = None - else: + # Schedule a disable() when 'timeout' has passed + self.disable_initiator_tag = (gobject.timeout_add + (self.timeout_milliseconds(), + self.disable)) + self.enabled = True + if self.use_dbus: + # Emit D-Bus signals + self.PropertyChanged(dbus.String(u"enabled"), + dbus.Boolean(True, variant_level=1)) + self.PropertyChanged(dbus.String(u"last_enabled"), + (_datetime_to_dbus(self.last_enabled, + variant_level=1))) + + def disable(self): + """Disable this client.""" + if not getattr(self, "enabled", False): return False - if getattr(self, "stop_initiator_tag", False): - gobject.source_remove(self.stop_initiator_tag) - self.stop_initiator_tag = None + logger.info(u"Disabling client %s", self.name) + if getattr(self, "disable_initiator_tag", False): + gobject.source_remove(self.disable_initiator_tag) + self.disable_initiator_tag = None if getattr(self, "checker_initiator_tag", False): gobject.source_remove(self.checker_initiator_tag) self.checker_initiator_tag = None self.stop_checker() - if self.stop_hook: - self.stop_hook(self) + if self.disable_hook: + self.disable_hook(self) + self.enabled = False + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"enabled"), + dbus.Boolean(False, variant_level=1)) # Do not run this again if called by a gobject.timeout_add return False + def __del__(self): - self.stop_hook = None - self.stop() - def checker_callback(self, pid, condition): + self.disable_hook = None + self.disable() + + def checker_callback(self, pid, condition, command): """The checker has completed, so take appropriate actions.""" self.checker_callback_tag = None self.checker = None - if os.WIFEXITED(condition) \ - and (os.WEXITSTATUS(condition) == 0): + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"checker_running"), + dbus.Boolean(False, variant_level=1)) + if (os.WIFEXITED(condition) + and (os.WEXITSTATUS(condition) == 0)): logger.info(u"Checker for %(name)s succeeded", vars(self)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(True), + dbus.UInt16(condition), + dbus.String(command)) self.bump_timeout() elif not os.WIFEXITED(condition): logger.warning(u"Checker for %(name)s crashed?", vars(self)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) else: logger.info(u"Checker for %(name)s failed", vars(self)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) + def bump_timeout(self): """Bump up the timeout for this client. This should only be called when the client has been seen, alive and well. """ - self.last_checked_ok = datetime.datetime.now() - gobject.source_remove(self.stop_initiator_tag) - self.stop_initiator_tag = gobject.timeout_add\ - (self._timeout_milliseconds, self.stop) + self.last_checked_ok = datetime.datetime.utcnow() + gobject.source_remove(self.disable_initiator_tag) + self.disable_initiator_tag = (gobject.timeout_add + (self.timeout_milliseconds(), + self.disable)) + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged( + dbus.String(u"last_checked_ok"), + (_datetime_to_dbus(self.last_checked_ok, + variant_level=1))) + def start_checker(self): """Start a new checker subprocess if one is not running. If a checker already exists, leave it running and do @@ -335,18 +377,18 @@ # is as it should be. if self.checker is None: try: - # In case check_command has exactly one % operator - command = self.check_command % self.host + # In case checker_command has exactly one % operator + command = self.checker_command % self.host except TypeError: # Escape attributes for the shell escaped_attrs = dict((key, re.escape(str(val))) for key, val in vars(self).iteritems()) try: - command = self.check_command % escaped_attrs + command = self.checker_command % escaped_attrs except TypeError, error: logger.error(u'Could not format string "%s":' - u' %s', self.check_command, error) + u' %s', self.checker_command, error) return True # Try again later try: logger.info(u"Starting checker %r for %s", @@ -358,14 +400,22 @@ self.checker = subprocess.Popen(command, close_fds=True, shell=True, cwd="/") - self.checker_callback_tag = gobject.child_watch_add\ - (self.checker.pid, - self.checker_callback) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerStarted(command) + self.PropertyChanged( + dbus.String("checker_running"), + dbus.Boolean(True, variant_level=1)) + self.checker_callback_tag = (gobject.child_watch_add + (self.checker.pid, + self.checker_callback, + data=command)) except OSError, error: logger.error(u"Failed to start subprocess: %s", error) # Re-run this periodically if run by gobject.timeout_add return True + def stop_checker(self): """Force the checker process, if any, to stop.""" if self.checker_callback_tag: @@ -383,26 +433,168 @@ if error.errno != errno.ESRCH: # No such process raise self.checker = None + if self.use_dbus: + self.PropertyChanged(dbus.String(u"checker_running"), + dbus.Boolean(False, variant_level=1)) + def still_valid(self): """Has the timeout not yet passed for this client?""" - now = datetime.datetime.now() + if not getattr(self, "enabled", False): + return False + now = datetime.datetime.utcnow() if self.last_checked_ok is None: return now < (self.created + self.timeout) else: return now < (self.last_checked_ok + self.timeout) + + ## D-Bus methods & signals + _interface = u"org.mandos_system.Mandos.Client" + + # BumpTimeout - method + BumpTimeout = dbus.service.method(_interface)(bump_timeout) + BumpTimeout.__name__ = "BumpTimeout" + + # CheckerCompleted - signal + @dbus.service.signal(_interface, signature="bqs") + def CheckerCompleted(self, success, condition, command): + "D-Bus signal" + pass + + # CheckerStarted - signal + @dbus.service.signal(_interface, signature="s") + def CheckerStarted(self, command): + "D-Bus signal" + pass + + # GetAllProperties - method + @dbus.service.method(_interface, out_signature="a{sv}") + def GetAllProperties(self): + "D-Bus method" + return dbus.Dictionary({ + dbus.String("name"): + dbus.String(self.name, variant_level=1), + dbus.String("fingerprint"): + dbus.String(self.fingerprint, variant_level=1), + dbus.String("host"): + dbus.String(self.host, variant_level=1), + dbus.String("created"): + _datetime_to_dbus(self.created, variant_level=1), + dbus.String("last_enabled"): + (_datetime_to_dbus(self.last_enabled, + variant_level=1) + if self.last_enabled is not None + else dbus.Boolean(False, variant_level=1)), + dbus.String("enabled"): + dbus.Boolean(self.enabled, variant_level=1), + dbus.String("last_checked_ok"): + (_datetime_to_dbus(self.last_checked_ok, + variant_level=1) + if self.last_checked_ok is not None + else dbus.Boolean (False, variant_level=1)), + dbus.String("timeout"): + dbus.UInt64(self.timeout_milliseconds(), + variant_level=1), + dbus.String("interval"): + dbus.UInt64(self.interval_milliseconds(), + variant_level=1), + dbus.String("checker"): + dbus.String(self.checker_command, + variant_level=1), + dbus.String("checker_running"): + dbus.Boolean(self.checker is not None, + variant_level=1), + }, signature="sv") + + # IsStillValid - method + IsStillValid = (dbus.service.method(_interface, out_signature="b") + (still_valid)) + IsStillValid.__name__ = "IsStillValid" + + # PropertyChanged - signal + @dbus.service.signal(_interface, signature="sv") + def PropertyChanged(self, property, value): + "D-Bus signal" + pass + + # SetChecker - method + @dbus.service.method(_interface, in_signature="s") + def SetChecker(self, checker): + "D-Bus setter method" + self.checker_command = checker + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"checker"), + dbus.String(self.checker_command, + variant_level=1)) + + # SetHost - method + @dbus.service.method(_interface, in_signature="s") + def SetHost(self, host): + "D-Bus setter method" + self.host = host + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"host"), + dbus.String(self.host, variant_level=1)) + + # SetInterval - method + @dbus.service.method(_interface, in_signature="t") + def SetInterval(self, milliseconds): + self.interval = datetime.timedelta(0, 0, 0, milliseconds) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"interval"), + (dbus.UInt64(self.interval_milliseconds(), + variant_level=1))) + + # SetSecret - method + @dbus.service.method(_interface, in_signature="ay", + byte_arrays=True) + def SetSecret(self, secret): + "D-Bus setter method" + self.secret = str(secret) + + # SetTimeout - method + @dbus.service.method(_interface, in_signature="t") + def SetTimeout(self, milliseconds): + self.timeout = datetime.timedelta(0, 0, 0, milliseconds) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"timeout"), + (dbus.UInt64(self.timeout_milliseconds(), + variant_level=1))) + + # Enable - method + Enable = dbus.service.method(_interface)(enable) + Enable.__name__ = "Enable" + + # StartChecker - method + @dbus.service.method(_interface) + def StartChecker(self): + "D-Bus method" + self.start_checker() + + # Disable - method + @dbus.service.method(_interface) + def Disable(self): + "D-Bus method" + self.disable() + + # StopChecker - method + StopChecker = dbus.service.method(_interface)(stop_checker) + StopChecker.__name__ = "StopChecker" + + del _interface def peer_certificate(session): "Return the peer's OpenPGP certificate as a bytestring" # If not an OpenPGP certificate... - if gnutls.library.functions.gnutls_certificate_type_get\ - (session._c_object) \ - != gnutls.library.constants.GNUTLS_CRT_OPENPGP: + if (gnutls.library.functions + .gnutls_certificate_type_get(session._c_object) + != gnutls.library.constants.GNUTLS_CRT_OPENPGP): # ...do the normal thing return session.peer_certificate list_size = ctypes.c_uint() - cert_list = gnutls.library.functions.gnutls_certificate_get_peers\ - (session._c_object, ctypes.byref(list_size)) + cert_list = (gnutls.library.functions + .gnutls_certificate_get_peers + (session._c_object, ctypes.byref(list_size))) if list_size.value == 0: return None cert = cert_list[0] @@ -412,22 +604,24 @@ def fingerprint(openpgp): "Convert an OpenPGP bytestring to a hexdigit fingerprint string" # New GnuTLS "datum" with the OpenPGP public key - datum = gnutls.library.types.gnutls_datum_t\ - (ctypes.cast(ctypes.c_char_p(openpgp), - ctypes.POINTER(ctypes.c_ubyte)), - ctypes.c_uint(len(openpgp))) + datum = (gnutls.library.types + .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp), + ctypes.POINTER + (ctypes.c_ubyte)), + ctypes.c_uint(len(openpgp)))) # New empty GnuTLS certificate crt = gnutls.library.types.gnutls_openpgp_crt_t() - gnutls.library.functions.gnutls_openpgp_crt_init\ - (ctypes.byref(crt)) + (gnutls.library.functions + .gnutls_openpgp_crt_init(ctypes.byref(crt))) # Import the OpenPGP public key into the certificate - gnutls.library.functions.gnutls_openpgp_crt_import\ - (crt, ctypes.byref(datum), - gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW) + (gnutls.library.functions + .gnutls_openpgp_crt_import(crt, ctypes.byref(datum), + gnutls.library.constants + .GNUTLS_OPENPGP_FMT_RAW)) # Verify the self signature in the key crtverify = ctypes.c_uint() - gnutls.library.functions.gnutls_openpgp_crt_verify_self\ - (crt, 0, ctypes.byref(crtverify)) + (gnutls.library.functions + .gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify))) if crtverify.value != 0: gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) raise gnutls.errors.CertificateSecurityError("Verify failed") @@ -435,8 +629,9 @@ buf = ctypes.create_string_buffer(20) buf_len = ctypes.c_size_t() # Get the fingerprint from the certificate into the buffer - gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\ - (crt, ctypes.byref(buf), ctypes.byref(buf_len)) + (gnutls.library.functions + .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), + ctypes.byref(buf_len))) # Deinit the certificate gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) # Convert the buffer to a Python bytestring @@ -454,8 +649,10 @@ def handle(self): logger.info(u"TCP connection from: %s", unicode(self.client_address)) - session = gnutls.connection.ClientSession\ - (self.request, gnutls.connection.X509Credentials()) + session = (gnutls.connection + .ClientSession(self.request, + gnutls.connection + .X509Credentials())) line = self.request.makefile().readline() logger.debug(u"Protocol version: %r", line) @@ -476,8 +673,9 @@ # "+DHE-DSS")) # Use a fallback default, since this MUST be set. priority = self.server.settings.get("priority", "NORMAL") - gnutls.library.functions.gnutls_priority_set_direct\ - (session._c_object, priority, None) + (gnutls.library.functions + .gnutls_priority_set_direct(session._c_object, + priority, None)) try: session.handshake() @@ -493,12 +691,11 @@ session.bye() return logger.debug(u"Fingerprint: %s", fpr) - client = None for c in self.server.clients: if c.fingerprint == fpr: client = c break - if not client: + else: logger.warning(u"Client not found for fingerprint: %s", fpr) session.bye() @@ -643,14 +840,15 @@ elif state == avahi.ENTRY_GROUP_FAILURE: logger.critical(u"Avahi: Error in group state changed %s", unicode(error)) - raise AvahiGroupError("State changed: %s", str(error)) + raise AvahiGroupError(u"State changed: %s" % unicode(error)) def if_nametoindex(interface): """Call the C function if_nametoindex(), or equivalent""" global if_nametoindex try: - if_nametoindex = ctypes.cdll.LoadLibrary\ - (ctypes.util.find_library("c")).if_nametoindex + if_nametoindex = (ctypes.cdll.LoadLibrary + (ctypes.util.find_library("c")) + .if_nametoindex) except (OSError, AttributeError): if "struct" not in sys.modules: import struct @@ -691,14 +889,14 @@ def main(): - parser = OptionParser(version = "%%prog %s" % version) + 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", default=False, + 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" @@ -711,6 +909,9 @@ default="/etc/mandos", metavar="DIR", help="Directory to search for configuration" " files") + parser.add_option("--no-dbus", action="store_false", + dest="use_dbus", + help=optparse.SUPPRESS_HELP) options = parser.parse_args()[0] if options.check: @@ -726,6 +927,7 @@ "priority": "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", "servicename": "Mandos", + "use_dbus": "True", } # Parse config file for server-global settings @@ -734,29 +936,35 @@ server_config.read(os.path.join(options.configdir, "mandos.conf")) # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() - # Use getboolean on the boolean config option - server_settings["debug"] = server_config.getboolean\ - ("DEFAULT", "debug") + # Use getboolean on the boolean config options + server_settings["debug"] = (server_config.getboolean + ("DEFAULT", "debug")) + server_settings["use_dbus"] = (server_config.getboolean + ("DEFAULT", "use_dbus")) del server_config # Override the settings from the config file with command line # options, if set. for option in ("interface", "address", "port", "debug", - "priority", "servicename", "configdir"): + "priority", "servicename", "configdir", + "use_dbus"): value = getattr(options, option) if value is not None: server_settings[option] = value del options # Now we have our good server settings in "server_settings" + # For convenience debug = server_settings["debug"] + use_dbus = server_settings["use_dbus"] + use_dbus = False if not debug: syslogger.setLevel(logging.WARNING) console.setLevel(logging.WARNING) if server_settings["servicename"] != "Mandos": - syslogger.setFormatter(logging.Formatter\ + syslogger.setFormatter(logging.Formatter ('Mandos (%s): %%(levelname)s:' ' %%(message)s' % server_settings["servicename"])) @@ -783,22 +991,26 @@ except IOError, error: logger.error("Could not open file %r", pidfilename) - uid = 65534 - gid = 65534 - try: - uid = pwd.getpwnam("mandos").pw_uid - except KeyError: - try: - uid = pwd.getpwnam("nobody").pw_uid - except KeyError: - pass - try: - gid = pwd.getpwnam("mandos").pw_gid - except KeyError: - try: - gid = pwd.getpwnam("nogroup").pw_gid - except KeyError: - pass + try: + uid = pwd.getpwnam("_mandos").pw_uid + except KeyError: + try: + uid = pwd.getpwnam("mandos").pw_uid + except KeyError: + try: + uid = pwd.getpwnam("nobody").pw_uid + except KeyError: + uid = 65534 + try: + gid = pwd.getpwnam("_mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("nogroup").pw_gid + except KeyError: + gid = 65534 try: os.setuid(uid) os.setgid(gid) @@ -810,8 +1022,8 @@ service = AvahiService(name = server_settings["servicename"], servicetype = "_mandos._tcp", ) if server_settings["interface"]: - service.interface = if_nametoindex\ - (server_settings["interface"]) + service.interface = (if_nametoindex + (server_settings["interface"])) global main_loop global bus @@ -824,21 +1036,17 @@ avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) # End of Avahi example code - - def remove_from_clients(client): - clients.remove(client) - if not clients: - logger.critical(u"No clients left, exiting") - sys.exit() + if use_dbus: + bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", + bus) clients.update(Set(Client(name = section, - stop_hook = remove_from_clients, config - = dict(client_config.items(section))) + = dict(client_config.items(section)), + use_dbus = use_dbus) for section in client_config.sections())) if not clients: - logger.critical(u"No clients defined") - sys.exit(1) + logger.warning(u"No clients defined") if debug: # Redirect stdin so all checkers get /dev/null @@ -876,8 +1084,8 @@ while clients: client = clients.pop() - client.stop_hook = None - client.stop() + client.disable_hook = None + client.disable() atexit.register(cleanup) @@ -886,8 +1094,61 @@ signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) + if use_dbus: + class MandosServer(dbus.service.Object): + """A D-Bus proxy object""" + def __init__(self): + dbus.service.Object.__init__(self, bus, + "/Mandos") + _interface = u"org.mandos_system.Mandos" + + @dbus.service.signal(_interface, signature="oa{sv}") + def ClientAdded(self, objpath, properties): + "D-Bus signal" + pass + + @dbus.service.signal(_interface, signature="o") + def ClientRemoved(self, objpath): + "D-Bus signal" + pass + + @dbus.service.method(_interface, out_signature="ao") + def GetAllClients(self): + return dbus.Array(c.dbus_object_path for c in clients) + + @dbus.service.method(_interface, out_signature="a{oa{sv}}") + def GetAllClientsWithProperties(self): + return dbus.Dictionary( + ((c.dbus_object_path, c.GetAllProperties()) + for c in clients), + signature="oa{sv}") + + @dbus.service.method(_interface, in_signature="o") + def RemoveClient(self, object_path): + for c in clients: + if c.dbus_object_path == object_path: + clients.remove(c) + # Don't signal anything except ClientRemoved + c.use_dbus = False + c.disable() + # Emit D-Bus signal + self.ClientRemoved(object_path) + return + raise KeyError + @dbus.service.method(_interface) + def Quit(self): + main_loop.quit() + + del _interface + + mandos_server = MandosServer() + for client in clients: - client.start() + if use_dbus: + # Emit D-Bus signal + mandos_server.ClientAdded(client.dbus_object_path, + client.GetAllProperties()) + client.enable() tcp_server.enable() tcp_server.server_activate() @@ -911,13 +1172,13 @@ gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN, lambda *args, **kwargs: - tcp_server.handle_request\ - (*args[2:], **kwargs) or True) + (tcp_server.handle_request + (*args[2:], **kwargs) or True)) logger.debug(u"Starting main loop") main_loop.run() except AvahiError, error: - logger.critical(u"AvahiError: %s" + unicode(error)) + logger.critical(u"AvahiError: %s", error) sys.exit(1) except KeyboardInterrupt: if debug: === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2008-10-07 21:31:09 +0000 +++ mandos-clients.conf.xml 2009-01-04 21:54:55 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/clients.conf"> - + %common; ]> @@ -33,6 +33,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'mandos-keygen' --- mandos-keygen 2008-10-17 18:56:25 +0000 +++ mandos-keygen 2009-01-04 21:54:55 +0000 @@ -2,7 +2,8 @@ # # Mandos key generator - create a new OpenPGP key for a Mandos client # -# Copyright © 2008 Teddy Hogeborn & Björn Påhlsson +# Copyright © 2008,2009 Teddy Hogeborn +# Copyright © 2008,2009 Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2008-10-04 01:55:56 +0000 +++ mandos-keygen.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'mandos-options.xml' --- mandos-options.xml 2008-09-30 03:19:39 +0000 +++ mandos-options.xml 2008-12-29 02:44:54 +0000 @@ -65,5 +65,11 @@ rename itself to Mandos #2, and so on; therefore, this option is not needed in that case. + + + This option controls whether the server will provide a D-Bus + system bus interface. The default is to provide such an + interface. + === modified file 'mandos.conf' --- mandos.conf 2008-08-18 23:55:28 +0000 +++ mandos.conf 2008-12-29 02:44:54 +0000 @@ -36,3 +36,6 @@ # If there are name collisions on the same *network*, the server will # rename itself to "Mandos #2", etc. ;servicename = Mandos + +# Whether to provide a D-Bus system bus interface or not +;use_dbus = True === modified file 'mandos.conf.xml' --- mandos.conf.xml 2008-09-30 07:23:39 +0000 +++ mandos.conf.xml 2009-01-04 21:54:55 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/mandos.conf"> - + %common; ]> @@ -33,6 +33,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson @@ -130,6 +131,17 @@ + + + + + + + @@ -172,6 +184,7 @@ debug = true priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP servicename = Daena +use_dbus = False === modified file 'mandos.lsm' --- mandos.lsm 2008-10-18 10:58:29 +0000 +++ mandos.lsm 2008-12-10 01:26:02 +0000 @@ -1,7 +1,7 @@ Begin4 Title: Mandos Version: 1.0.2 -Entered-date: 2008-10-18 +Entered-date: 2008-10-28 Description: The Mandos system allows computers to have encrypted root file systems and at the same time be capable of remote and/or unattended reboots. === modified file 'mandos.xml' --- mandos.xml 2008-10-04 01:55:56 +0000 +++ mandos.xml 2009-01-05 23:26:06 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugin-runner.c' --- plugin-runner.c 2008-11-01 02:26:00 +0000 +++ plugin-runner.c 2009-01-04 21:54:55 +0000 @@ -2,7 +2,8 @@ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008 Teddy Hogeborn & Björn Påhlsson + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugin-runner.xml' --- plugin-runner.xml 2008-09-30 07:23:39 +0000 +++ plugin-runner.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2008-09-26 19:47:21 +0000 +++ plugins.d/askpass-fifo.c 2009-01-04 21:54:55 +0000 @@ -1,3 +1,28 @@ +/* -*- coding: utf-8 -*- */ +/* + * Passprompt - Read a password from a FIFO and output it + * + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * . + * + * Contact the authors at and + * . + */ + #define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ #include /* ssize_t */ #include /* mkfifo(), S_IRUSR, S_IWUSR */ === modified file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2008-10-04 20:09:53 +0000 +++ plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2008-09-30 07:23:39 +0000 +++ plugins.d/mandos-client.c 2009-01-04 21:54:55 +0000 @@ -9,7 +9,8 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008 Teddy Hogeborn & Björn Påhlsson + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2008-10-04 01:55:56 +0000 +++ plugins.d/mandos-client.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2008-09-30 07:23:39 +0000 +++ plugins.d/password-prompt.c 2009-01-04 21:54:55 +0000 @@ -1,8 +1,9 @@ /* -*- coding: utf-8 -*- */ /* * Passprompt - Read a password from the terminal and print it - * - * Copyright © 2008 Teddy Hogeborn & Björn Påhlsson + * + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2008-10-04 01:55:56 +0000 +++ plugins.d/password-prompt.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2008-10-03 09:32:30 +0000 +++ plugins.d/splashy.c 2009-01-04 21:54:55 +0000 @@ -1,3 +1,28 @@ +/* -*- coding: utf-8 -*- */ +/* + * Passprompt - Read a password from splashy and output it + * + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * . + * + * Contact the authors at and + * . + */ + #define _GNU_SOURCE /* asprintf() */ #include /* sig_atomic_t, struct sigaction, sigemptyset(), sigaddset(), SIGINT, === modified file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2008-10-04 03:11:39 +0000 +++ plugins.d/splashy.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2008-10-03 09:32:30 +0000 +++ plugins.d/usplash.c 2009-01-04 21:54:55 +0000 @@ -1,3 +1,28 @@ +/* -*- coding: utf-8 -*- */ +/* + * Passprompt - Read a password from usplash and output it + * + * Copyright © 2008,2009 Teddy Hogeborn + * Copyright © 2008,2009 Björn Påhlsson + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * . + * + * Contact the authors at and + * . + */ + #define _GNU_SOURCE /* asprintf() */ #include /* sig_atomic_t, struct sigaction, sigemptyset(), sigaddset(), SIGINT, === modified file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2008-10-04 03:11:39 +0000 +++ plugins.d/usplash.xml 2009-01-04 21:54:55 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2008 + 2009 Teddy Hogeborn Björn Påhlsson