=== modified file 'TODO'
--- TODO 2009-05-21 20:20:20 +0000
+++ TODO 2009-09-17 11:47:22 +0000
@@ -1,7 +1,27 @@
-*- org -*-
* mandos-client
-** TODO [#A] Clean up /tmp directory on signal
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with argv[0]
+** TODO [#B] Retry a server which has a non-definite reply:
+*** A closed connection during the TLS handshake
+*** A TCP timeout
+** TODO [#B] Use capabilities instead of seteuid().
+
+* splashy
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+
+* usplash
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+
+* askpass-fifo
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+** TODO [#B] Drop privileges after opening FIFO.
+
+* password-prompt
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
* plugin-runner
** TODO [#B] use scandir(3) instead of readdir(3)
@@ -25,6 +45,12 @@
[[info:standards:Option%20Table][Table of Long Options]]
** TODO Date+time on console log messages :BUGS:
Is this the default?
+** TODO DBusServiceObjectUsingSuper
+** Global enable/disable flag
+** By-client countdown on secrets given
+** Fix problem with fsck taking a really long time
+ Whenever a client successfully gets a secret it could get a
+ one-time timeout boost to allow for an fsck-incurred delay
* mandos.xml
** [[file:mandos.xml::XXX][Document D-Bus interface]]
@@ -35,7 +61,11 @@
*** Handle "no D-Bus server" and/or "no Mandos server found" better
*** [#B] --dump option
-* Curses interface
+* mandos-monitor
+** D-Bus mail loop w/ signal receiver
+** Snack/Newt client data displayer
+*** Client Widgets
+*** Properties popup
* mandos-keygen
** TODO Loop until passwords match when run interactively
@@ -44,6 +74,10 @@
** TODO [#B] "--test" option
For testing decryption before rebooting.
+* Makefile
+** Implement DEB_BUILD_OPTIONS
+ http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options
+
* Package
** /usr/share/initramfs-tools/hooks/mandos
*** TODO [#C] use same file name rules as run-parts(8)
=== modified file 'debian/control'
--- debian/control 2009-05-21 20:20:20 +0000
+++ debian/control 2009-09-17 11:47:22 +0000
@@ -7,7 +7,7 @@
Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl,
libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc,
pkg-config
-Standards-Version: 3.8.1
+Standards-Version: 3.8.3
Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk
Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files
Homepage: http://www.fukt.bsnet.se/mandos
=== modified file 'debian/mandos-client.README.Debian'
--- debian/mandos-client.README.Debian 2009-02-09 02:01:13 +0000
+++ debian/mandos-client.README.Debian 2009-09-08 06:28:20 +0000
@@ -1,27 +1,42 @@
-* Configure The Server
-
- A client key has been automatically created in /etc/keys/mandos.
- The next step is to run "mandos-keygen --password" to get a config
- file section. This should be appended to /etc/mandos/clients.conf
- on the Mandos server.
-
-* Use the Correct Network Interface
-
- Make sure that the correct network interface is specified in the
- DEVICE setting in the "/etc/initramfs-tools/initramfs.conf" file.
- If this is changed, it will be necessary to update the initrd image
- by doing "update-initramfs -k all -u". This setting can be
- overridden at boot time on the Linux kernel command line using the
- sixth colon-separated field of the "ip=" option; for exact syntax,
- see the file "Documentation/nfsroot.txt" in the Linux source tree.
-
-* Test the Server
-
- After the server has been started and this client's key added, it is
- possible to verify that the correct password will be received by
+* Choose the Client Network Interface
+
+ You MUST make sure that the correct network interface is specified
+ in the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf"
+ file. *If* this is changed, it will be necessary to update the
+ initrd image by running the command
+
+ update-initramfs -k all -u
+
+ The device can be overridden at boot time on the Linux kernel
+ command line using the sixth colon-separated field of the "ip="
+ option; for exact syntax, read the documentation in the file
+ "/usr/share/doc/linux-doc-*/Documentation/filesystems/nfsroot.txt",
+ available in the "linux-doc-*" package.
+
+ Note that since this network interface is used in the initial RAM
+ disk environment, the network interface *must* exist at that stage.
+ Thus, the interface can *not* be a pseudo-interface such as "br0" or
+ "tun0"; instead, a real interface (such as "eth0") must be used.
+
+* Adding a Client Password to the Server
+
+ The server must be given a password to give back to the client on
+ boot time. This password must be a one which can be used to unlock
+ the root file system device. On the *client*, run this command:
+
+ mandos-keygen --password
+
+ It will prompt for a password and output a config file section.
+ This output should be copied to the Mandos server and added to the
+ file "/etc/mandos/clients.conf" there.
+
+* Testing that it Works (Without Rebooting)
+
+ After the server has been started with this client's key added, it
+ is possible to verify that the correct password will be received by
this client by running the command, on the client:
- # /usr/lib/mandos/plugins.d/mandos-client \
+ /usr/lib/mandos/plugins.d/mandos-client \
--pubkey=/etc/keys/mandos/pubkey.txt \
--seckey=/etc/keys/mandos/seckey.txt; echo
@@ -31,16 +46,16 @@
* User-Supplied Plugins
- Any plugins found in /etc/mandos/plugins.d will override and add to
- the normal Mandos plugins. When adding or changing plugins, do not
- forget to update the initital RAM disk image:
+ Any plugins found in "/etc/mandos/plugins.d" will override and add
+ to the normal Mandos plugins. When adding or changing plugins, do
+ not forget to update the initital RAM disk image:
- # update-initramfs -k all -u
+ update-initramfs -k all -u
-* Do *NOT* Edit /etc/crypttab
+* Do *NOT* Edit "/etc/crypttab"
- It is NOT necessary to edit /etc/crypttab to specify
- /usr/lib/mandos/plugin-runner as a keyscript for the root file
+ It is NOT necessary to edit "/etc/crypttab" to specify
+ "/usr/lib/mandos/plugin-runner" as a keyscript for the root file
system; if no keyscript is given for the root file system, the
Mandos client will be the new default way for getting a password for
the root file system when booting.
@@ -69,4 +84,4 @@
work, "--options-for=mandos-client:--connect=
:" needs
to be manually added to the file "/etc/mandos/plugin-runner.conf".
- -- Teddy Hogeborn , Mon, 9 Feb 2009 00:36:55 +0100
+ -- Teddy Hogeborn , Tue, 8 Sep 2009 08:25:58 +0200
=== modified file 'debian/mandos-client.postinst'
--- debian/mandos-client.postinst 2009-05-17 03:13:49 +0000
+++ debian/mandos-client.postinst 2009-05-24 23:36:15 +0000
@@ -33,13 +33,15 @@
# Add user and group
add_mandos_user(){
# 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
+ if dpkg --compare-versions "$2" lt "1.0.3-1"; then
+ case "`getent passwd mandos`" in
+ *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
+ usermod --login _mandos mandos
+ groupmod --new-name _mandos mandos
+ return
+ ;;
+ esac
+ fi
# Create new user and group
if ! getent passwd _mandos >/dev/null; then
adduser --system --force-badname --quiet --home /nonexistent \
=== modified file 'debian/mandos.README.Debian'
--- debian/mandos.README.Debian 2009-01-04 22:15:01 +0000
+++ debian/mandos.README.Debian 2009-09-08 06:28:20 +0000
@@ -1,7 +1,10 @@
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 , Sun, 4 Jan 2009 22:59:22 +0100
+package on a client computer, and run the command
+
+ # mandos-keygen --password
+
+there to get a config file stanza. Append the output of that command
+to the file "/etc/mandos/clients.conf" on the Mandos server.
+
+ -- Teddy Hogeborn , Tue, 8 Sep 2009 06:57:45 +0200
=== modified file 'debian/mandos.postinst'
--- debian/mandos.postinst 2009-01-18 00:16:57 +0000
+++ debian/mandos.postinst 2009-05-24 23:28:04 +0000
@@ -18,12 +18,14 @@
case "$1" in
configure)
# 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
+ if dpkg --compare-versions "$2" lt "1.0.3-1"; then
+ case "`getent passwd mandos`" in
+ *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
+ usermod --login _mandos mandos
+ groupmod --new-name _mandos mandos
+ ;;
+ esac
+ fi
# Create new user and group
if ! getent passwd _mandos >/dev/null; then
adduser --system --force-badname --quiet \
=== modified file 'init.d-mandos'
--- init.d-mandos 2008-09-21 12:04:02 +0000
+++ init.d-mandos 2009-09-16 23:28:39 +0000
@@ -1,8 +1,8 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: mandos
-# Required-Start: $remote_fs avahi-daemon
-# Required-Stop: $remote_fs
+# Required-Start: $remote_fs $syslog avahi
+# Required-Stop: $remote_fs $syslog avahi
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Mandos server
=== modified file 'initramfs-tools-hook'
--- initramfs-tools-hook 2009-02-25 01:14:29 +0000
+++ initramfs-tools-hook 2009-09-07 23:48:17 +0000
@@ -51,16 +51,13 @@
exit 1
fi
-mandos_user="`{ getent passwd _mandos \
- || getent passwd mandos \
- || getent passwd nobody \
- || echo ::65534::::; } \
- | cut --delimiter=: --fields=3 --only-delimited`"
-mandos_group="`{ getent group _mandos \
- || getent group mandos \
- || getent group nogroup \
- || echo ::65534:; } \
- | cut --delimiter=: --fields=3 --only-delimited`"
+set `{ getent passwd _mandos \
+ || getent passwd nobody \
+ || echo ::65534:65534:::; } \
+ | cut --delimiter=: --fields=3,4 --only-delimited \
+ --output-delimiter=" "`
+mandos_user="$1"
+mandos_group="$2"
# The Mandos network client uses the network
auto_add_modules net
@@ -91,8 +88,9 @@
continue
fi
case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;;
- "*") : ;;
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
+ "*") echo "W: Mandos client plugin directory is empty." >&2 ;;
*) copy_exec "$file" "${PLUGINDIR}" ;;
esac
done
@@ -101,7 +99,8 @@
for file in /etc/mandos/plugins.d/*; do
base="`basename \"$file\"`"
case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;;
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
"*") : ;;
*) copy_exec "$file" "${PLUGINDIR}" ;;
esac
@@ -123,19 +122,13 @@
done
if [ ${mandos_user} != 65534 ]; then
- PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf"
- cat <<-EOF >> "$PLUGINRUNNERCONF"
-
- --userid=${mandos_user}
-EOF
+ sed --in-place --expression="1i--userid=${mandos_user}" \
+ "${DESTDIR}${CONFDIR}/plugin-runner.conf"
fi
if [ ${mandos_group} != 65534 ]; then
- PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf"
- cat <<-EOF >> "$PLUGINRUNNERCONF"
-
- --groupid=${mandos_group}
-EOF
+ sed --in-place --expression="1i--groupid=${mandos_group}" \
+ "${DESTDIR}${CONFDIR}/plugin-runner.conf"
fi
# Key files
=== modified file 'initramfs-tools-script'
--- initramfs-tools-script 2009-02-09 02:01:13 +0000
+++ initramfs-tools-script 2009-09-16 23:28:39 +0000
@@ -10,7 +10,6 @@
# eventually be "/scripts/init-premount/mandos" in the initrd.img
# file.
-# No initramfs pre-requirements.
PREREQ="udev"
prereqs()
{
=== modified file 'mandos'
--- mandos 2009-05-23 05:22:05 +0000
+++ mandos 2009-09-17 11:47:22 +0000
@@ -6,9 +6,9 @@
# This program is partly derived from an example program for an Avahi
# service publisher, downloaded from
# . This includes the
-# methods "add" and "remove" in the "AvahiService" class, the
-# "server_state_changed" and "entry_group_state_changed" functions,
-# and some lines in "main".
+# methods "add", "remove", "server_state_changed",
+# "entry_group_state_changed", "cleanup", and "activate" in the
+# "AvahiService" class, and some lines in "main".
#
# Everything else is
# Copyright © 2008,2009 Teddy Hogeborn
@@ -33,7 +33,7 @@
from __future__ import division, with_statement, absolute_import
-import SocketServer
+import SocketServer as socketserver
import socket
import optparse
import datetime
@@ -44,12 +44,11 @@
import gnutls.library.functions
import gnutls.library.constants
import gnutls.library.types
-import ConfigParser
+import ConfigParser as configparser
import sys
import re
import os
import signal
-from sets import Set
import subprocess
import atexit
import stat
@@ -57,6 +56,9 @@
import logging.handlers
import pwd
from contextlib import closing
+import struct
+import fcntl
+import functools
import dbus
import dbus.service
@@ -66,20 +68,30 @@
import ctypes
import ctypes.util
+try:
+ SO_BINDTODEVICE = socket.SO_BINDTODEVICE
+except AttributeError:
+ try:
+ from IN import SO_BINDTODEVICE
+ except ImportError:
+ SO_BINDTODEVICE = None
+
+
version = "1.0.11"
-logger = logging.Logger('mandos')
+logger = logging.Logger(u'mandos')
syslogger = (logging.handlers.SysLogHandler
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
address = "/dev/log"))
syslogger.setFormatter(logging.Formatter
- ('Mandos [%(process)d]: %(levelname)s:'
- ' %(message)s'))
+ (u'Mandos [%(process)d]: %(levelname)s:'
+ u' %(message)s'))
logger.addHandler(syslogger)
console = logging.StreamHandler()
-console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
- ' %(levelname)s: %(message)s'))
+console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
+ u' %(levelname)s:'
+ u' %(message)s'))
logger.addHandler(console)
class AvahiError(Exception):
@@ -98,11 +110,12 @@
class AvahiService(object):
"""An Avahi (Zeroconf) service.
+
Attributes:
interface: integer; avahi.IF_UNSPEC or an interface index.
Used to optionally bind to the specified interface.
- name: string; Example: 'Mandos'
- type: string; Example: '_mandos._tcp'.
+ name: string; Example: u'Mandos'
+ type: string; Example: u'_mandos._tcp'.
See
port: integer; what port to announce
TXT: list of strings; TXT record for the service
@@ -111,11 +124,14 @@
max_renames: integer; maximum number of renames
rename_count: integer; counter so we only rename after collisions
a sensible number of times
+ group: D-Bus Entry Group
+ server: D-Bus Server
+ bus: dbus.SystemBus()
"""
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
servicetype = None, port = None, TXT = None,
- domain = "", host = "", max_renames = 32768,
- protocol = avahi.PROTO_UNSPEC):
+ domain = u"", host = u"", max_renames = 32768,
+ protocol = avahi.PROTO_UNSPEC, bus = None):
self.interface = interface
self.name = name
self.type = servicetype
@@ -126,6 +142,9 @@
self.rename_count = 0
self.max_renames = max_renames
self.protocol = protocol
+ self.group = None # our entry group
+ self.server = None
+ self.bus = bus
def rename(self):
"""Derived from the Avahi example code"""
if self.rename_count >= self.max_renames:
@@ -133,53 +152,81 @@
u" after %i retries, exiting.",
self.rename_count)
raise AvahiServiceError(u"Too many renames")
- self.name = server.GetAlternativeServiceName(self.name)
+ self.name = self.server.GetAlternativeServiceName(self.name)
logger.info(u"Changing Zeroconf service name to %r ...",
- str(self.name))
+ unicode(self.name))
syslogger.setFormatter(logging.Formatter
- ('Mandos (%s): %%(levelname)s:'
- ' %%(message)s' % self.name))
+ (u'Mandos (%s) [%%(process)d]:'
+ u' %%(levelname)s: %%(message)s'
+ % self.name))
self.remove()
self.add()
self.rename_count += 1
def remove(self):
"""Derived from the Avahi example code"""
- if group is not None:
- group.Reset()
+ if self.group is not None:
+ self.group.Reset()
def add(self):
"""Derived from the Avahi example code"""
- global group
- if group is None:
- group = dbus.Interface(bus.get_object
- (avahi.DBUS_NAME,
- server.EntryGroupNew()),
- avahi.DBUS_INTERFACE_ENTRY_GROUP)
- group.connect_to_signal('StateChanged',
- entry_group_state_changed)
+ if self.group is None:
+ self.group = dbus.Interface(
+ self.bus.get_object(avahi.DBUS_NAME,
+ self.server.EntryGroupNew()),
+ avahi.DBUS_INTERFACE_ENTRY_GROUP)
+ self.group.connect_to_signal('StateChanged',
+ self.entry_group_state_changed)
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
- service.name, service.type)
- group.AddService(
- self.interface, # interface
- self.protocol, # protocol
- dbus.UInt32(0), # flags
- self.name, self.type,
- self.domain, self.host,
- dbus.UInt16(self.port),
- avahi.string_array_to_txt_array(self.TXT))
- group.Commit()
-
-# From the Avahi example code:
-group = None # our entry group
-# End of Avahi example code
-
-
-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):
+ self.name, self.type)
+ self.group.AddService(
+ self.interface,
+ self.protocol,
+ dbus.UInt32(0), # flags
+ self.name, self.type,
+ self.domain, self.host,
+ dbus.UInt16(self.port),
+ avahi.string_array_to_txt_array(self.TXT))
+ self.group.Commit()
+ def entry_group_state_changed(self, state, error):
+ """Derived from the Avahi example code"""
+ logger.debug(u"Avahi state change: %i", state)
+
+ if state == avahi.ENTRY_GROUP_ESTABLISHED:
+ logger.debug(u"Zeroconf service established.")
+ elif state == avahi.ENTRY_GROUP_COLLISION:
+ logger.warning(u"Zeroconf service name collision.")
+ self.rename()
+ elif state == avahi.ENTRY_GROUP_FAILURE:
+ logger.critical(u"Avahi: Error in group state changed %s",
+ unicode(error))
+ raise AvahiGroupError(u"State changed: %s"
+ % unicode(error))
+ def cleanup(self):
+ """Derived from the Avahi example code"""
+ if self.group is not None:
+ self.group.Free()
+ self.group = None
+ def server_state_changed(self, state):
+ """Derived from the Avahi example code"""
+ if state == avahi.SERVER_COLLISION:
+ logger.error(u"Zeroconf server name collision")
+ self.remove()
+ elif state == avahi.SERVER_RUNNING:
+ self.add()
+ def activate(self):
+ """Derived from the Avahi example code"""
+ if self.server is None:
+ self.server = dbus.Interface(
+ self.bus.get_object(avahi.DBUS_NAME,
+ avahi.DBUS_PATH_SERVER),
+ avahi.DBUS_INTERFACE_SERVER)
+ self.server.connect_to_signal(u"StateChanged",
+ self.server_state_changed)
+ self.server_state_changed(self.server.GetState())
+
+
+class Client(object):
"""A representation of a client host served by this server.
+
Attributes:
name: string; from the config file, used in log messages and
D-Bus identifiers
@@ -206,23 +253,24 @@
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
- use_dbus: bool(); Whether to provide D-Bus interface and signals
- dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
"""
+
+ @staticmethod
+ def _datetime_to_milliseconds(dt):
+ "Convert a datetime.datetime() to milliseconds"
+ return ((dt.days * 24 * 60 * 60 * 1000)
+ + (dt.seconds * 1000)
+ + (dt.microseconds // 1000))
+
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))
+ return self._datetime_to_milliseconds(self.timeout)
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))
+ return self._datetime_to_milliseconds(self.interval)
- def __init__(self, name = None, disable_hook=None, config=None,
- use_dbus=True):
+ def __init__(self, name = None, disable_hook=None, config=None):
"""Note: the 'checker' key in 'config' sets the
'checker_command' attribute and *not* the 'checker'
attribute."""
@@ -230,50 +278,43 @@
if config is None:
config = {}
logger.debug(u"Creating client %r", self.name)
- self.use_dbus = False # During __init__
# Uppercase and remove spaces from fingerprint for later
# comparison purposes with return value from the fingerprint()
# function
- self.fingerprint = (config["fingerprint"].upper()
+ self.fingerprint = (config[u"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:
+ if u"secret" in config:
+ self.secret = config[u"secret"].decode(u"base64")
+ elif u"secfile" in config:
with closing(open(os.path.expanduser
(os.path.expandvars
- (config["secfile"])))) as secfile:
+ (config[u"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.host = config.get(u"host", u"")
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.timeout = string_to_delta(config[u"timeout"])
+ self.interval = string_to_delta(config[u"interval"])
self.disable_hook = disable_hook
self.checker = None
self.checker_initiator_tag = None
self.disable_initiator_tag = None
self.checker_callback_tag = None
- self.checker_command = config["checker"]
+ self.checker_command = config[u"checker"]
self.current_checker_command = None
self.last_connect = None
- # Only now, when this client is initialized, can it show up on
- # the D-Bus
- self.use_dbus = use_dbus
- if self.use_dbus:
- self.dbus_object_path = (dbus.ObjectPath
- ("/clients/"
- + self.name.replace(".", "_")))
- dbus.service.Object.__init__(self, bus,
- self.dbus_object_path)
def enable(self):
"""Start this client's checker and timeout hooks"""
+ if getattr(self, u"enabled", False):
+ # Already enabled
+ return
self.last_enabled = datetime.datetime.utcnow()
# Schedule a new checker to be started an 'interval' from now,
# and every interval from then on.
@@ -287,33 +328,22 @@
(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
logger.info(u"Disabling client %s", self.name)
- if getattr(self, "disable_initiator_tag", False):
+ if getattr(self, u"disable_initiator_tag", False):
gobject.source_remove(self.disable_initiator_tag)
self.disable_initiator_tag = None
- if getattr(self, "checker_initiator_tag", False):
+ if getattr(self, u"checker_initiator_tag", False):
gobject.source_remove(self.checker_initiator_tag)
self.checker_initiator_tag = None
self.stop_checker()
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
@@ -325,10 +355,6 @@
"""The checker has completed, so take appropriate actions."""
self.checker_callback_tag = None
self.checker = None
- 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):
exitstatus = os.WEXITSTATUS(condition)
if exitstatus == 0:
@@ -338,22 +364,13 @@
else:
logger.info(u"Checker for %(name)s failed",
vars(self))
- if self.use_dbus:
- # Emit D-Bus signal
- self.CheckerCompleted(dbus.Int16(exitstatus),
- dbus.Int64(condition),
- dbus.String(command))
else:
logger.warning(u"Checker for %(name)s crashed?",
vars(self))
- if self.use_dbus:
- # Emit D-Bus signal
- self.CheckerCompleted(dbus.Int16(-1),
- dbus.Int64(condition),
- dbus.String(command))
def checked_ok(self):
"""Bump up the timeout for this client.
+
This should only be called when the client has been seen,
alive and well.
"""
@@ -362,15 +379,10 @@
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
nothing."""
# The reason for not killing a running checker is that if we
@@ -386,7 +398,7 @@
if self.checker is not None:
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
if pid:
- logger.warning("Checker was a zombie")
+ logger.warning(u"Checker was a zombie")
gobject.source_remove(self.checker_callback_tag)
self.checker_callback(pid, status,
self.current_checker_command)
@@ -397,7 +409,10 @@
command = self.checker_command % self.host
except TypeError:
# Escape attributes for the shell
- escaped_attrs = dict((key, re.escape(str(val)))
+ escaped_attrs = dict((key,
+ re.escape(unicode(str(val),
+ errors=
+ u'replace')))
for key, val in
vars(self).iteritems())
try:
@@ -406,7 +421,7 @@
logger.error(u'Could not format string "%s":'
u' %s', self.checker_command, error)
return True # Try again later
- self.current_checker_command = command
+ self.current_checker_command = command
try:
logger.info(u"Starting checker %r for %s",
command, self.name)
@@ -416,13 +431,7 @@
# always replaced by /dev/null.)
self.checker = subprocess.Popen(command,
close_fds=True,
- shell=True, cwd="/")
- if self.use_dbus:
- # Emit D-Bus signal
- self.CheckerStarted(command)
- self.PropertyChanged(
- dbus.String("checker_running"),
- dbus.Boolean(True, variant_level=1))
+ shell=True, cwd=u"/")
self.checker_callback_tag = (gobject.child_watch_add
(self.checker.pid,
self.checker_callback,
@@ -444,7 +453,7 @@
if self.checker_callback_tag:
gobject.source_remove(self.checker_callback_tag)
self.checker_callback_tag = None
- if getattr(self, "checker", None) is None:
+ if getattr(self, u"checker", None) is None:
return
logger.debug(u"Stopping checker for %(name)s", vars(self))
try:
@@ -456,94 +465,220 @@
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?"""
- if not getattr(self, "enabled", False):
+ if not getattr(self, u"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)
+
+
+class ClientDBus(Client, dbus.service.Object):
+ """A Client class using D-Bus
+
+ Attributes:
+ dbus_object_path: dbus.ObjectPath
+ bus: dbus.SystemBus()
+ """
+ # dbus.service.Object doesn't use super(), so we can't either.
+
+ def __init__(self, bus = None, *args, **kwargs):
+ self.bus = bus
+ Client.__init__(self, *args, **kwargs)
+ # Only now, when this client is initialized, can it show up on
+ # the D-Bus
+ self.dbus_object_path = (dbus.ObjectPath
+ (u"/clients/"
+ + self.name.replace(u".", u"_")))
+ dbus.service.Object.__init__(self, self.bus,
+ self.dbus_object_path)
+
+ @staticmethod
+ 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)
+
+ def enable(self):
+ oldstate = getattr(self, u"enabled", False)
+ r = Client.enable(self)
+ if oldstate != self.enabled:
+ # Emit D-Bus signals
+ self.PropertyChanged(dbus.String(u"enabled"),
+ dbus.Boolean(True, variant_level=1))
+ self.PropertyChanged(
+ dbus.String(u"last_enabled"),
+ self._datetime_to_dbus(self.last_enabled,
+ variant_level=1))
+ return r
+
+ def disable(self, signal = True):
+ oldstate = getattr(self, u"enabled", False)
+ r = Client.disable(self)
+ if signal and oldstate != self.enabled:
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"enabled"),
+ dbus.Boolean(False, variant_level=1))
+ return r
+
+ def __del__(self, *args, **kwargs):
+ try:
+ self.remove_from_connection()
+ except LookupError:
+ pass
+ if hasattr(dbus.service.Object, u"__del__"):
+ dbus.service.Object.__del__(self, *args, **kwargs)
+ Client.__del__(self, *args, **kwargs)
+
+ def checker_callback(self, pid, condition, command,
+ *args, **kwargs):
+ self.checker_callback_tag = None
+ self.checker = None
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"checker_running"),
+ dbus.Boolean(False, variant_level=1))
+ if os.WIFEXITED(condition):
+ exitstatus = os.WEXITSTATUS(condition)
+ # Emit D-Bus signal
+ self.CheckerCompleted(dbus.Int16(exitstatus),
+ dbus.Int64(condition),
+ dbus.String(command))
+ else:
+ # Emit D-Bus signal
+ self.CheckerCompleted(dbus.Int16(-1),
+ dbus.Int64(condition),
+ dbus.String(command))
+
+ return Client.checker_callback(self, pid, condition, command,
+ *args, **kwargs)
+
+ def checked_ok(self, *args, **kwargs):
+ r = Client.checked_ok(self, *args, **kwargs)
+ # Emit D-Bus signal
+ self.PropertyChanged(
+ dbus.String(u"last_checked_ok"),
+ (self._datetime_to_dbus(self.last_checked_ok,
+ variant_level=1)))
+ return r
+
+ def start_checker(self, *args, **kwargs):
+ old_checker = self.checker
+ if self.checker is not None:
+ old_checker_pid = self.checker.pid
+ else:
+ old_checker_pid = None
+ r = Client.start_checker(self, *args, **kwargs)
+ # Only if new checker process was started
+ if (self.checker is not None
+ and old_checker_pid != self.checker.pid):
+ # Emit D-Bus signal
+ self.CheckerStarted(self.current_checker_command)
+ self.PropertyChanged(
+ dbus.String(u"checker_running"),
+ dbus.Boolean(True, variant_level=1))
+ return r
+
+ def stop_checker(self, *args, **kwargs):
+ old_checker = getattr(self, u"checker", None)
+ r = Client.stop_checker(self, *args, **kwargs)
+ if (old_checker is not None
+ and getattr(self, u"checker", None) is None):
+ self.PropertyChanged(dbus.String(u"checker_running"),
+ dbus.Boolean(False, variant_level=1))
+ return r
## D-Bus methods & signals
_interface = u"se.bsnet.fukt.Mandos.Client"
# CheckedOK - method
- CheckedOK = dbus.service.method(_interface)(checked_ok)
- CheckedOK.__name__ = "CheckedOK"
+ @dbus.service.method(_interface)
+ def CheckedOK(self):
+ return self.checked_ok()
# CheckerCompleted - signal
- @dbus.service.signal(_interface, signature="nxs")
+ @dbus.service.signal(_interface, signature=u"nxs")
def CheckerCompleted(self, exitcode, waitstatus, command):
"D-Bus signal"
pass
# CheckerStarted - signal
- @dbus.service.signal(_interface, signature="s")
+ @dbus.service.signal(_interface, signature=u"s")
def CheckerStarted(self, command):
"D-Bus signal"
pass
# GetAllProperties - method
- @dbus.service.method(_interface, out_signature="a{sv}")
+ @dbus.service.method(_interface, out_signature=u"a{sv}")
def GetAllProperties(self):
"D-Bus method"
return dbus.Dictionary({
- dbus.String("name"):
+ dbus.String(u"name"):
dbus.String(self.name, variant_level=1),
- dbus.String("fingerprint"):
+ dbus.String(u"fingerprint"):
dbus.String(self.fingerprint, variant_level=1),
- dbus.String("host"):
+ dbus.String(u"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)
+ dbus.String(u"created"):
+ self._datetime_to_dbus(self.created,
+ variant_level=1),
+ dbus.String(u"last_enabled"):
+ (self._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.String(u"enabled"):
dbus.Boolean(self.enabled, variant_level=1),
- dbus.String("last_checked_ok"):
- (_datetime_to_dbus(self.last_checked_ok,
- variant_level=1)
+ dbus.String(u"last_checked_ok"):
+ (self._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.String(u"timeout"):
dbus.UInt64(self.timeout_milliseconds(),
variant_level=1),
- dbus.String("interval"):
+ dbus.String(u"interval"):
dbus.UInt64(self.interval_milliseconds(),
variant_level=1),
- dbus.String("checker"):
+ dbus.String(u"checker"):
dbus.String(self.checker_command,
variant_level=1),
- dbus.String("checker_running"):
+ dbus.String(u"checker_running"):
dbus.Boolean(self.checker is not None,
variant_level=1),
- dbus.String("object_path"):
+ dbus.String(u"object_path"):
dbus.ObjectPath(self.dbus_object_path,
variant_level=1)
- }, signature="sv")
+ }, signature=u"sv")
# IsStillValid - method
- IsStillValid = (dbus.service.method(_interface, out_signature="b")
- (still_valid))
- IsStillValid.__name__ = "IsStillValid"
+ @dbus.service.method(_interface, out_signature=u"b")
+ def IsStillValid(self):
+ return self.still_valid()
# PropertyChanged - signal
- @dbus.service.signal(_interface, signature="sv")
+ @dbus.service.signal(_interface, signature=u"sv")
def PropertyChanged(self, property, value):
"D-Bus signal"
pass
+ # ReceivedSecret - signal
+ @dbus.service.signal(_interface)
+ def ReceivedSecret(self):
+ "D-Bus signal"
+ pass
+
+ # Rejected - signal
+ @dbus.service.signal(_interface)
+ def Rejected(self):
+ "D-Bus signal"
+ pass
+
# SetChecker - method
- @dbus.service.method(_interface, in_signature="s")
+ @dbus.service.method(_interface, in_signature=u"s")
def SetChecker(self, checker):
"D-Bus setter method"
self.checker_command = checker
@@ -553,7 +688,7 @@
variant_level=1))
# SetHost - method
- @dbus.service.method(_interface, in_signature="s")
+ @dbus.service.method(_interface, in_signature=u"s")
def SetHost(self, host):
"D-Bus setter method"
self.host = host
@@ -562,7 +697,7 @@
dbus.String(self.host, variant_level=1))
# SetInterval - method
- @dbus.service.method(_interface, in_signature="t")
+ @dbus.service.method(_interface, in_signature=u"t")
def SetInterval(self, milliseconds):
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
# Emit D-Bus signal
@@ -571,14 +706,14 @@
variant_level=1)))
# SetSecret - method
- @dbus.service.method(_interface, in_signature="ay",
+ @dbus.service.method(_interface, in_signature=u"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")
+ @dbus.service.method(_interface, in_signature=u"t")
def SetTimeout(self, milliseconds):
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
# Emit D-Bus signal
@@ -587,8 +722,10 @@
variant_level=1)))
# Enable - method
- Enable = dbus.service.method(_interface)(enable)
- Enable.__name__ = "Enable"
+ @dbus.service.method(_interface)
+ def Enable(self):
+ "D-Bus method"
+ self.enable()
# StartChecker - method
@dbus.service.method(_interface)
@@ -603,199 +740,226 @@
self.disable()
# StopChecker - method
- StopChecker = dbus.service.method(_interface)(stop_checker)
- StopChecker.__name__ = "StopChecker"
+ @dbus.service.method(_interface)
+ def StopChecker(self):
+ self.stop_checker()
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):
- # ...do the normal thing
- return session.peer_certificate
- list_size = ctypes.c_uint(1)
- cert_list = (gnutls.library.functions
- .gnutls_certificate_get_peers
- (session._c_object, ctypes.byref(list_size)))
- if not bool(cert_list) and list_size.value != 0:
- raise gnutls.errors.GNUTLSError("error getting peer"
- " certificate")
- if list_size.value == 0:
- return None
- cert = cert_list[0]
- return ctypes.string_at(cert.data, cert.size)
-
-
-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))))
- # New empty GnuTLS certificate
- crt = gnutls.library.types.gnutls_openpgp_crt_t()
- (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))
- # Verify the self signature in the key
- crtverify = ctypes.c_uint()
- (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")
- # New buffer for the fingerprint
- 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)))
- # Deinit the certificate
- gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
- # Convert the buffer to a Python bytestring
- fpr = ctypes.string_at(buf, buf_len.value)
- # Convert the bytestring to hexadecimal notation
- hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
- return hex_fpr
-
-
-class TCP_handler(SocketServer.BaseRequestHandler, object):
- """A TCP request handler class.
- Instantiated by IPv6_TCPServer for each request to handle it.
+class ClientHandler(socketserver.BaseRequestHandler, object):
+ """A class to handle client connections.
+
+ Instantiated once for each connection to handle it.
Note: This will run in its own forked process."""
def handle(self):
logger.info(u"TCP connection from: %s",
unicode(self.client_address))
- session = (gnutls.connection
- .ClientSession(self.request,
- gnutls.connection
- .X509Credentials()))
-
- line = self.request.makefile().readline()
- logger.debug(u"Protocol version: %r", line)
- try:
- if int(line.strip().split()[0]) > 1:
- raise RuntimeError
- except (ValueError, IndexError, RuntimeError), error:
- logger.error(u"Unknown protocol version: %s", error)
- return
-
- # Note: gnutls.connection.X509Credentials is really a generic
- # GnuTLS certificate credentials object so long as no X.509
- # keys are added to it. Therefore, we can use it here despite
- # using OpenPGP certificates.
-
- #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
- # "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
- # "+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))
-
- try:
- session.handshake()
- except gnutls.errors.GNUTLSError, error:
- logger.warning(u"Handshake failed: %s", error)
- # Do not run session.bye() here: the session is not
- # established. Just abandon the request.
- return
- logger.debug(u"Handshake succeeded")
- try:
- fpr = fingerprint(peer_certificate(session))
- except (TypeError, gnutls.errors.GNUTLSError), error:
- logger.warning(u"Bad certificate: %s", error)
- session.bye()
- return
- logger.debug(u"Fingerprint: %s", fpr)
-
- for c in self.server.clients:
- if c.fingerprint == fpr:
- client = c
- break
- else:
- logger.warning(u"Client not found for fingerprint: %s",
- fpr)
- session.bye()
- return
- # Have to check if client.still_valid(), since it is possible
- # that the client timed out while establishing the GnuTLS
- # session.
- if not client.still_valid():
- logger.warning(u"Client %(name)s is invalid",
- vars(client))
- session.bye()
- return
- ## This won't work here, since we're in a fork.
- # client.checked_ok()
- sent_size = 0
- while sent_size < len(client.secret):
- sent = session.send(client.secret[sent_size:])
- logger.debug(u"Sent: %d, remaining: %d",
- sent, len(client.secret)
- - (sent_size + sent))
- sent_size += sent
- session.bye()
-
-
-class IPv6_TCPServer(SocketServer.ForkingMixIn,
- SocketServer.TCPServer, object):
+ logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
+ # Open IPC pipe to parent process
+ with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
+ session = (gnutls.connection
+ .ClientSession(self.request,
+ gnutls.connection
+ .X509Credentials()))
+
+ line = self.request.makefile().readline()
+ logger.debug(u"Protocol version: %r", line)
+ try:
+ if int(line.strip().split()[0]) > 1:
+ raise RuntimeError
+ except (ValueError, IndexError, RuntimeError), error:
+ logger.error(u"Unknown protocol version: %s", error)
+ return
+
+ # 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",
+ # u"+DHE-DSS"))
+ # Use a fallback default, since this MUST be set.
+ priority = self.server.gnutls_priority
+ if priority is None:
+ priority = u"NORMAL"
+ (gnutls.library.functions
+ .gnutls_priority_set_direct(session._c_object,
+ priority, None))
+
+ try:
+ session.handshake()
+ except gnutls.errors.GNUTLSError, error:
+ logger.warning(u"Handshake failed: %s", error)
+ # Do not run session.bye() here: the session is not
+ # established. Just abandon the request.
+ return
+ logger.debug(u"Handshake succeeded")
+ try:
+ fpr = self.fingerprint(self.peer_certificate(session))
+ except (TypeError, gnutls.errors.GNUTLSError), error:
+ logger.warning(u"Bad certificate: %s", error)
+ session.bye()
+ return
+ logger.debug(u"Fingerprint: %s", fpr)
+
+ for c in self.server.clients:
+ if c.fingerprint == fpr:
+ client = c
+ break
+ else:
+ ipc.write(u"NOTFOUND %s %s\n"
+ % (fpr, unicode(self.client_address)))
+ session.bye()
+ return
+ # Have to check if client.still_valid(), since it is
+ # possible that the client timed out while establishing
+ # the GnuTLS session.
+ if not client.still_valid():
+ ipc.write(u"INVALID %s\n" % client.name)
+ session.bye()
+ return
+ ipc.write(u"SENDING %s\n" % client.name)
+ sent_size = 0
+ while sent_size < len(client.secret):
+ sent = session.send(client.secret[sent_size:])
+ logger.debug(u"Sent: %d, remaining: %d",
+ sent, len(client.secret)
+ - (sent_size + sent))
+ sent_size += sent
+ session.bye()
+
+ @staticmethod
+ 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):
+ # ...do the normal thing
+ return session.peer_certificate
+ list_size = ctypes.c_uint(1)
+ cert_list = (gnutls.library.functions
+ .gnutls_certificate_get_peers
+ (session._c_object, ctypes.byref(list_size)))
+ if not bool(cert_list) and list_size.value != 0:
+ raise gnutls.errors.GNUTLSError(u"error getting peer"
+ u" certificate")
+ if list_size.value == 0:
+ return None
+ cert = cert_list[0]
+ return ctypes.string_at(cert.data, cert.size)
+
+ @staticmethod
+ def fingerprint(openpgp):
+ "Convert an OpenPGP bytestring to a hexdigit fingerprint"
+ # 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))))
+ # New empty GnuTLS certificate
+ crt = gnutls.library.types.gnutls_openpgp_crt_t()
+ (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))
+ # Verify the self signature in the key
+ crtverify = ctypes.c_uint()
+ (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
+ (u"Verify failed"))
+ # New buffer for the fingerprint
+ 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)))
+ # Deinit the certificate
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ # Convert the buffer to a Python bytestring
+ fpr = ctypes.string_at(buf, buf_len.value)
+ # Convert the bytestring to hexadecimal notation
+ hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
+ return hex_fpr
+
+
+class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
+ """Like socketserver.ForkingMixIn, but also pass a pipe."""
+ def process_request(self, request, client_address):
+ """Overrides and wraps the original process_request().
+
+ This function creates a new pipe in self.pipe
+ """
+ self.pipe = os.pipe()
+ super(ForkingMixInWithPipe,
+ self).process_request(request, client_address)
+ os.close(self.pipe[1]) # close write end
+ self.add_pipe(self.pipe[0])
+ def add_pipe(self, pipe):
+ """Dummy function; override as necessary"""
+ os.close(pipe)
+
+
+class IPv6_TCPServer(ForkingMixInWithPipe,
+ socketserver.TCPServer, object):
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
+
Attributes:
- settings: Server settings
- clients: Set() of Client objects
enabled: Boolean; whether this server is activated yet
+ interface: None or a network interface name (string)
+ use_ipv6: Boolean; to use IPv6 or not
"""
- address_family = socket.AF_INET6
- def __init__(self, *args, **kwargs):
- if "settings" in kwargs:
- self.settings = kwargs["settings"]
- del kwargs["settings"]
- if "clients" in kwargs:
- self.clients = kwargs["clients"]
- del kwargs["clients"]
- if "use_ipv6" in kwargs:
- if not kwargs["use_ipv6"]:
- self.address_family = socket.AF_INET
- del kwargs["use_ipv6"]
- self.enabled = False
- super(IPv6_TCPServer, self).__init__(*args, **kwargs)
+ def __init__(self, server_address, RequestHandlerClass,
+ interface=None, use_ipv6=True):
+ self.interface = interface
+ if use_ipv6:
+ self.address_family = socket.AF_INET6
+ socketserver.TCPServer.__init__(self, server_address,
+ RequestHandlerClass)
def server_bind(self):
"""This overrides the normal server_bind() function
to bind to an interface if one was specified, and also NOT to
bind to an address or port if they were not specified."""
- if self.settings["interface"]:
- # 25 is from /usr/include/asm-i486/socket.h
- SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
- try:
- self.socket.setsockopt(socket.SOL_SOCKET,
- SO_BINDTODEVICE,
- self.settings["interface"])
- except socket.error, error:
- if error[0] == errno.EPERM:
- logger.error(u"No permission to"
- u" bind to interface %s",
- self.settings["interface"])
- else:
- raise
+ if self.interface is not None:
+ if SO_BINDTODEVICE is None:
+ logger.error(u"SO_BINDTODEVICE does not exist;"
+ u" cannot bind to interface %s",
+ self.interface)
+ else:
+ try:
+ self.socket.setsockopt(socket.SOL_SOCKET,
+ SO_BINDTODEVICE,
+ str(self.interface
+ + u'\0'))
+ except socket.error, error:
+ if error[0] == errno.EPERM:
+ logger.error(u"No permission to"
+ u" bind to interface %s",
+ self.interface)
+ elif error[0] == errno.ENOPROTOOPT:
+ logger.error(u"SO_BINDTODEVICE not available;"
+ u" cannot bind to interface %s",
+ self.interface)
+ else:
+ raise
# Only bind(2) the socket if we really need to.
if self.server_address[0] or self.server_address[1]:
if not self.server_address[0]:
if self.address_family == socket.AF_INET6:
- any_address = "::" # in6addr_any
+ any_address = u"::" # in6addr_any
else:
any_address = socket.INADDR_ANY
self.server_address = (any_address,
@@ -803,35 +967,136 @@
elif not self.server_address[1]:
self.server_address = (self.server_address[0],
0)
-# if self.settings["interface"]:
+# if self.interface:
# self.server_address = (self.server_address[0],
# 0, # port
# 0, # flowinfo
# if_nametoindex
-# (self.settings
-# ["interface"]))
- return super(IPv6_TCPServer, self).server_bind()
+# (self.interface))
+ return socketserver.TCPServer.server_bind(self)
+
+
+class MandosServer(IPv6_TCPServer):
+ """Mandos server.
+
+ Attributes:
+ clients: set of Client objects
+ gnutls_priority GnuTLS priority string
+ use_dbus: Boolean; to emit D-Bus signals or not
+ clients: set of Client objects
+ gnutls_priority GnuTLS priority string
+ use_dbus: Boolean; to emit D-Bus signals or not
+
+ Assumes a gobject.MainLoop event loop.
+ """
+ def __init__(self, server_address, RequestHandlerClass,
+ interface=None, use_ipv6=True, clients=None,
+ gnutls_priority=None, use_dbus=True):
+ self.enabled = False
+ self.clients = clients
+ if self.clients is None:
+ self.clients = set()
+ self.use_dbus = use_dbus
+ self.gnutls_priority = gnutls_priority
+ IPv6_TCPServer.__init__(self, server_address,
+ RequestHandlerClass,
+ interface = interface,
+ use_ipv6 = use_ipv6)
def server_activate(self):
if self.enabled:
- return super(IPv6_TCPServer, self).server_activate()
+ return socketserver.TCPServer.server_activate(self)
def enable(self):
self.enabled = True
+ def add_pipe(self, pipe):
+ # Call "handle_ipc" for both data and EOF events
+ gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
+ self.handle_ipc)
+ def handle_ipc(self, source, condition, file_objects={}):
+ condition_names = {
+ gobject.IO_IN: u"IN", # There is data to read.
+ gobject.IO_OUT: u"OUT", # Data can be written (without
+ # blocking).
+ gobject.IO_PRI: u"PRI", # There is urgent data to read.
+ gobject.IO_ERR: u"ERR", # Error condition.
+ gobject.IO_HUP: u"HUP" # Hung up (the connection has been
+ # broken, usually for pipes and
+ # sockets).
+ }
+ conditions_string = ' | '.join(name
+ for cond, name in
+ condition_names.iteritems()
+ if cond & condition)
+ logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
+ conditions_string)
+
+ # Turn the pipe file descriptor into a Python file object
+ if source not in file_objects:
+ file_objects[source] = os.fdopen(source, u"r", 1)
+
+ # Read a line from the file object
+ cmdline = file_objects[source].readline()
+ if not cmdline: # Empty line means end of file
+ # close the IPC pipe
+ file_objects[source].close()
+ del file_objects[source]
+
+ # 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":
+ logger.warning(u"Client not found for fingerprint: %s",
+ args)
+ if self.use_dbus:
+ # Emit D-Bus signal
+ mandos_dbus_service.ClientNotFound(args)
+ elif cmd == u"INVALID":
+ for client in self.clients:
+ if client.name == args:
+ logger.warning(u"Client %s is invalid", args)
+ if self.use_dbus:
+ # Emit D-Bus signal
+ client.Rejected()
+ break
+ else:
+ logger.error(u"Unknown client %s is invalid", 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.ReceivedSecret()
+ break
+ else:
+ logger.error(u"Sending secret to unknown client %s",
+ args)
+ else:
+ logger.error(u"Unknown IPC command: %r", cmdline)
+
+ # Keep calling this function
+ return True
def string_to_delta(interval):
"""Parse a string and return a datetime.timedelta
- >>> string_to_delta('7d')
+ >>> string_to_delta(u'7d')
datetime.timedelta(7)
- >>> string_to_delta('60s')
+ >>> string_to_delta(u'60s')
datetime.timedelta(0, 60)
- >>> string_to_delta('60m')
+ >>> string_to_delta(u'60m')
datetime.timedelta(0, 3600)
- >>> string_to_delta('24h')
+ >>> string_to_delta(u'24h')
datetime.timedelta(1)
>>> string_to_delta(u'1w')
datetime.timedelta(7)
- >>> string_to_delta('5m 30s')
+ >>> string_to_delta(u'5m 30s')
datetime.timedelta(0, 330)
"""
timevalue = datetime.timedelta(0)
@@ -857,60 +1122,39 @@
return timevalue
-def server_state_changed(state):
- """Derived from the Avahi example code"""
- if state == avahi.SERVER_COLLISION:
- logger.error(u"Zeroconf server name collision")
- service.remove()
- elif state == avahi.SERVER_RUNNING:
- service.add()
-
-
-def entry_group_state_changed(state, error):
- """Derived from the Avahi example code"""
- logger.debug(u"Avahi state change: %i", state)
-
- if state == avahi.ENTRY_GROUP_ESTABLISHED:
- logger.debug(u"Zeroconf service established.")
- elif state == avahi.ENTRY_GROUP_COLLISION:
- logger.warning(u"Zeroconf service name collision.")
- service.rename()
- elif state == avahi.ENTRY_GROUP_FAILURE:
- logger.critical(u"Avahi: Error in group state changed %s",
- unicode(error))
- raise AvahiGroupError(u"State changed: %s" % unicode(error))
-
def if_nametoindex(interface):
- """Call the C function if_nametoindex(), or equivalent"""
+ """Call the C function if_nametoindex(), or equivalent
+
+ Note: This function cannot accept a unicode string."""
global if_nametoindex
try:
if_nametoindex = (ctypes.cdll.LoadLibrary
- (ctypes.util.find_library("c"))
+ (ctypes.util.find_library(u"c"))
.if_nametoindex)
except (OSError, AttributeError):
- if "struct" not in sys.modules:
- import struct
- if "fcntl" not in sys.modules:
- import fcntl
+ logger.warning(u"Doing if_nametoindex the hard way")
def if_nametoindex(interface):
"Get an interface index the hard way, i.e. using fcntl()"
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
with closing(socket.socket()) as s:
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
- struct.pack("16s16x", interface))
- interface_index = struct.unpack("I", ifreq[16:20])[0]
+ struct.pack(str(u"16s16x"),
+ interface))
+ interface_index = struct.unpack(str(u"I"),
+ ifreq[16:20])[0]
return interface_index
return if_nametoindex(interface)
def daemon(nochdir = False, noclose = False):
"""See daemon(3). Standard BSD Unix function.
+
This should really exist as os.daemon, but it doesn't (yet)."""
if os.fork():
sys.exit()
os.setsid()
if not nochdir:
- os.chdir("/")
+ os.chdir(u"/")
if os.fork():
sys.exit()
if not noclose:
@@ -918,7 +1162,7 @@
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
if not stat.S_ISCHR(os.fstat(null).st_mode):
raise OSError(errno.ENODEV,
- "/dev/null not a character device")
+ u"/dev/null not a character device")
os.dup2(null, sys.stdin.fileno())
os.dup2(null, sys.stdout.fileno())
os.dup2(null, sys.stderr.fileno())
@@ -927,31 +1171,35 @@
def main():
+
+ ######################################################################
+ # Parsing of options, both command line and config file
+
parser = optparse.OptionParser(version = "%%prog %s" % version)
- parser.add_option("-i", "--interface", type="string",
- metavar="IF", help="Bind to interface IF")
- parser.add_option("-a", "--address", type="string",
- help="Address to listen for requests on")
- parser.add_option("-p", "--port", type="int",
- help="Port number to receive requests on")
- parser.add_option("--check", action="store_true",
- help="Run self-test")
- parser.add_option("--debug", action="store_true",
- help="Debug mode; run in foreground and log to"
- " terminal")
- parser.add_option("--priority", type="string", help="GnuTLS"
- " priority string (see GnuTLS documentation)")
- parser.add_option("--servicename", type="string", metavar="NAME",
- help="Zeroconf service name")
- parser.add_option("--configdir", type="string",
- default="/etc/mandos", metavar="DIR",
- help="Directory to search for configuration"
- " files")
- parser.add_option("--no-dbus", action="store_false",
- dest="use_dbus",
+ parser.add_option("-i", u"--interface", type=u"string",
+ metavar="IF", help=u"Bind to interface IF")
+ parser.add_option("-a", u"--address", type=u"string",
+ help=u"Address to listen for requests on")
+ parser.add_option("-p", u"--port", type=u"int",
+ help=u"Port number to receive requests on")
+ parser.add_option("--check", action=u"store_true",
+ help=u"Run self-test")
+ parser.add_option("--debug", action=u"store_true",
+ help=u"Debug mode; run in foreground and log to"
+ u" terminal")
+ parser.add_option("--priority", type=u"string", help=u"GnuTLS"
+ u" priority string (see GnuTLS documentation)")
+ parser.add_option("--servicename", type=u"string",
+ metavar=u"NAME", help=u"Zeroconf service name")
+ parser.add_option("--configdir", type=u"string",
+ default=u"/etc/mandos", metavar=u"DIR",
+ help=u"Directory to search for configuration"
+ u" files")
+ parser.add_option("--no-dbus", action=u"store_false",
+ dest=u"use_dbus",
help=optparse.SUPPRESS_HELP) # XXX: Not done yet
- parser.add_option("--no-ipv6", action="store_false",
- dest="use_ipv6", help="Do not use IPv6")
+ parser.add_option("--no-ipv6", action=u"store_false",
+ dest=u"use_ipv6", help=u"Do not use IPv6")
options = parser.parse_args()[0]
if options.check:
@@ -960,95 +1208,104 @@
sys.exit()
# Default values for config file for server-global settings
- server_defaults = { "interface": "",
- "address": "",
- "port": "",
- "debug": "False",
- "priority":
- "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
- "servicename": "Mandos",
- "use_dbus": "True",
- "use_ipv6": "True",
+ server_defaults = { u"interface": u"",
+ u"address": u"",
+ u"port": u"",
+ u"debug": u"False",
+ u"priority":
+ u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
+ u"servicename": u"Mandos",
+ u"use_dbus": u"True",
+ u"use_ipv6": u"True",
}
# Parse config file for server-global settings
- server_config = ConfigParser.SafeConfigParser(server_defaults)
+ server_config = configparser.SafeConfigParser(server_defaults)
del server_defaults
- server_config.read(os.path.join(options.configdir, "mandos.conf"))
+ server_config.read(os.path.join(options.configdir,
+ u"mandos.conf"))
# Convert the SafeConfigParser object to a dict
server_settings = server_config.defaults()
# Use the appropriate methods on the non-string config options
- server_settings["debug"] = server_config.getboolean("DEFAULT",
- "debug")
- server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
- "use_dbus")
- server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
- "use_ipv6")
+ for option in (u"debug", u"use_dbus", u"use_ipv6"):
+ server_settings[option] = server_config.getboolean(u"DEFAULT",
+ option)
if server_settings["port"]:
- server_settings["port"] = server_config.getint("DEFAULT",
- "port")
+ server_settings["port"] = server_config.getint(u"DEFAULT",
+ u"port")
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",
- "use_dbus", "use_ipv6"):
+ for option in (u"interface", u"address", u"port", u"debug",
+ u"priority", u"servicename", u"configdir",
+ u"use_dbus", u"use_ipv6"):
value = getattr(options, option)
if value is not None:
server_settings[option] = value
del options
+ # Force all strings to be unicode
+ for option in server_settings.keys():
+ if type(server_settings[option]) is str:
+ server_settings[option] = unicode(server_settings[option])
# Now we have our good server settings in "server_settings"
+ ##################################################################
+
# For convenience
- debug = server_settings["debug"]
- use_dbus = server_settings["use_dbus"]
+ debug = server_settings[u"debug"]
+ use_dbus = server_settings[u"use_dbus"]
use_dbus = False # XXX: Not done yet
- use_ipv6 = server_settings["use_ipv6"]
+ use_ipv6 = server_settings[u"use_ipv6"]
if not debug:
syslogger.setLevel(logging.WARNING)
console.setLevel(logging.WARNING)
- if server_settings["servicename"] != "Mandos":
+ if server_settings[u"servicename"] != u"Mandos":
syslogger.setFormatter(logging.Formatter
- ('Mandos (%s): %%(levelname)s:'
- ' %%(message)s'
- % server_settings["servicename"]))
+ (u'Mandos (%s) [%%(process)d]:'
+ u' %%(levelname)s: %%(message)s'
+ % server_settings[u"servicename"]))
# Parse config file with clients
- client_defaults = { "timeout": "1h",
- "interval": "5m",
- "checker": "fping -q -- %%(host)s",
- "host": "",
+ client_defaults = { u"timeout": u"1h",
+ u"interval": u"5m",
+ u"checker": u"fping -q -- %%(host)s",
+ u"host": u"",
}
- client_config = ConfigParser.SafeConfigParser(client_defaults)
- client_config.read(os.path.join(server_settings["configdir"],
- "clients.conf"))
-
- clients = Set()
- tcp_server = IPv6_TCPServer((server_settings["address"],
- server_settings["port"]),
- TCP_handler,
- settings=server_settings,
- clients=clients, use_ipv6=use_ipv6)
- pidfilename = "/var/run/mandos.pid"
+ client_config = configparser.SafeConfigParser(client_defaults)
+ client_config.read(os.path.join(server_settings[u"configdir"],
+ u"clients.conf"))
+
+ global mandos_dbus_service
+ mandos_dbus_service = None
+
+ tcp_server = MandosServer((server_settings[u"address"],
+ server_settings[u"port"]),
+ ClientHandler,
+ interface=server_settings[u"interface"],
+ use_ipv6=use_ipv6,
+ gnutls_priority=
+ server_settings[u"priority"],
+ use_dbus=use_dbus)
+ pidfilename = u"/var/run/mandos.pid"
try:
- pidfile = open(pidfilename, "w")
+ pidfile = open(pidfilename, u"w")
except IOError:
- logger.error("Could not open file %r", pidfilename)
+ logger.error(u"Could not open file %r", pidfilename)
try:
- uid = pwd.getpwnam("_mandos").pw_uid
- gid = pwd.getpwnam("_mandos").pw_gid
+ uid = pwd.getpwnam(u"_mandos").pw_uid
+ gid = pwd.getpwnam(u"_mandos").pw_gid
except KeyError:
try:
- uid = pwd.getpwnam("mandos").pw_uid
- gid = pwd.getpwnam("mandos").pw_gid
+ uid = pwd.getpwnam(u"mandos").pw_uid
+ gid = pwd.getpwnam(u"mandos").pw_gid
except KeyError:
try:
- uid = pwd.getpwnam("nobody").pw_uid
- gid = pwd.getpwnam("nogroup").pw_gid
+ uid = pwd.getpwnam(u"nobody").pw_uid
+ gid = pwd.getpwnam(u"nobody").pw_gid
except KeyError:
uid = 65534
gid = 65534
@@ -1067,40 +1324,35 @@
@gnutls.library.types.gnutls_log_func
def debug_gnutls(level, string):
- logger.debug("GnuTLS: %s", string[:-1])
+ logger.debug(u"GnuTLS: %s", string[:-1])
(gnutls.library.functions
.gnutls_global_set_log_function(debug_gnutls))
- global service
- protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
- service = AvahiService(name = server_settings["servicename"],
- servicetype = "_mandos._tcp",
- protocol = protocol)
- if server_settings["interface"]:
- service.interface = (if_nametoindex
- (server_settings["interface"]))
-
global main_loop
- global bus
- global server
# From the Avahi example code
DBusGMainLoop(set_as_default=True )
main_loop = gobject.MainLoop()
bus = dbus.SystemBus()
- server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
- avahi.DBUS_PATH_SERVER),
- avahi.DBUS_INTERFACE_SERVER)
# End of Avahi example code
if use_dbus:
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
+ protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
+ service = AvahiService(name = server_settings[u"servicename"],
+ servicetype = u"_mandos._tcp",
+ protocol = protocol, bus = bus)
+ if server_settings["interface"]:
+ service.interface = (if_nametoindex
+ (str(server_settings[u"interface"])))
- clients.update(Set(Client(name = section,
- config
- = dict(client_config.items(section)),
- use_dbus = use_dbus)
- for section in client_config.sections()))
- if not clients:
+ client_class = Client
+ if use_dbus:
+ client_class = functools.partial(ClientDBus, bus = bus)
+ tcp_server.clients.update(set(
+ client_class(name = section,
+ config= dict(client_config.items(section)))
+ for section in client_config.sections()))
+ if not tcp_server.clients:
logger.warning(u"No clients defined")
if debug:
@@ -1116,9 +1368,9 @@
daemon()
try:
- pid = os.getpid()
- pidfile.write(str(pid) + "\n")
- pidfile.close()
+ with closing(pidfile):
+ pid = os.getpid()
+ pidfile.write(str(pid) + "\n")
del pidfile
except IOError:
logger.error(u"Could not write to file %r with PID %d",
@@ -1130,15 +1382,10 @@
def cleanup():
"Cleanup function; run on exit"
- global group
- # From the Avahi example code
- if not group is None:
- group.Free()
- group = None
- # End of Avahi example code
+ service.cleanup()
- while clients:
- client = clients.pop()
+ while tcp_server.clients:
+ client = tcp_server.clients.pop()
client.disable_hook = None
client.disable()
@@ -1150,44 +1397,51 @@
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
if use_dbus:
- class MandosServer(dbus.service.Object):
+ class MandosDBusService(dbus.service.Object):
"""A D-Bus proxy object"""
def __init__(self):
- dbus.service.Object.__init__(self, bus, "/")
+ dbus.service.Object.__init__(self, bus, u"/")
_interface = u"se.bsnet.fukt.Mandos"
- @dbus.service.signal(_interface, signature="oa{sv}")
+ @dbus.service.signal(_interface, signature=u"oa{sv}")
def ClientAdded(self, objpath, properties):
"D-Bus signal"
pass
- @dbus.service.signal(_interface, signature="os")
+ @dbus.service.signal(_interface, signature=u"s")
+ def ClientNotFound(self, fingerprint):
+ "D-Bus signal"
+ pass
+
+ @dbus.service.signal(_interface, signature=u"os")
def ClientRemoved(self, objpath, name):
"D-Bus signal"
pass
- @dbus.service.method(_interface, out_signature="ao")
+ @dbus.service.method(_interface, out_signature=u"ao")
def GetAllClients(self):
"D-Bus method"
- return dbus.Array(c.dbus_object_path for c in clients)
+ return dbus.Array(c.dbus_object_path
+ for c in tcp_server.clients)
- @dbus.service.method(_interface, out_signature="a{oa{sv}}")
+ @dbus.service.method(_interface,
+ out_signature=u"a{oa{sv}}")
def GetAllClientsWithProperties(self):
"D-Bus method"
return dbus.Dictionary(
((c.dbus_object_path, c.GetAllProperties())
- for c in clients),
- signature="oa{sv}")
+ for c in tcp_server.clients),
+ signature=u"oa{sv}")
- @dbus.service.method(_interface, in_signature="o")
+ @dbus.service.method(_interface, in_signature=u"o")
def RemoveClient(self, object_path):
"D-Bus method"
- for c in clients:
+ for c in tcp_server.clients:
if c.dbus_object_path == object_path:
- clients.remove(c)
+ tcp_server.clients.remove(c)
+ c.remove_from_connection()
# Don't signal anything except ClientRemoved
- c.use_dbus = False
- c.disable()
+ c.disable(signal=False)
# Emit D-Bus signal
self.ClientRemoved(object_path, c.name)
return
@@ -1195,13 +1449,13 @@
del _interface
- mandos_server = MandosServer()
+ mandos_dbus_service = MandosDBusService()
- for client in clients:
+ for client in tcp_server.clients:
if use_dbus:
# Emit D-Bus signal
- mandos_server.ClientAdded(client.dbus_object_path,
- client.GetAllProperties())
+ mandos_dbus_service.ClientAdded(client.dbus_object_path,
+ client.GetAllProperties())
client.enable()
tcp_server.enable()
@@ -1221,9 +1475,8 @@
try:
# From the Avahi example code
- server.connect_to_signal("StateChanged", server_state_changed)
try:
- server_state_changed(server.GetState())
+ service.activate()
except dbus.exceptions.DBusException, error:
logger.critical(u"DBusException: %s", error)
sys.exit(1)
@@ -1242,8 +1495,8 @@
except KeyboardInterrupt:
if debug:
print >> sys.stderr
- logger.debug("Server received KeyboardInterrupt")
- logger.debug("Server exiting")
+ logger.debug(u"Server received KeyboardInterrupt")
+ logger.debug(u"Server exiting")
if __name__ == '__main__':
main()
=== modified file 'mandos-clients.conf.xml'
--- mandos-clients.conf.xml 2009-02-15 09:09:27 +0000
+++ mandos-clients.conf.xml 2009-09-17 01:21:27 +0000
@@ -3,7 +3,7 @@
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
/etc/mandos/clients.conf">
-
+
%common;
]>
@@ -108,10 +108,11 @@
This option is optional.
- The timeout is how long the server will wait for a
- successful checker run until a client is considered
- invalid - that is, ineligible to get the data this server
- holds. By default Mandos will use 1 hour.
+ The timeout is how long the server will wait (for either a
+ successful checker run or a client receiving its secret)
+ until a client is considered invalid - that is, ineligible
+ to get the data this server holds. By default Mandos will
+ use 1 hour.
The TIME is specified as a
=== modified file 'mandos-ctl'
--- mandos-ctl 2009-05-23 05:22:05 +0000
+++ mandos-ctl 2009-09-17 11:47:22 +0000
@@ -106,7 +106,7 @@
for client in
clients))
for key in keywords)
- print format_string % tuple(tablewords[key] for key in keywords)
+ print format_string % tuple(tablewords[key] for key in keywords)
for client in clients:
print format_string % tuple(valuetostring(client[key], key)
for key in keywords)
=== modified file 'mandos.xml'
--- mandos.xml 2009-02-25 01:21:37 +0000
+++ mandos.xml 2009-09-17 11:47:22 +0000
@@ -2,7 +2,7 @@
-
+
%common;
]>
@@ -315,11 +315,14 @@
The server will, by default, continually check that the clients
are still up. If a client has not been confirmed as being up
for some time, the client is assumed to be compromised and is no
- longer eligible to receive the encrypted password. The timeout,
+ longer eligible to receive the encrypted password. (Manual
+ intervention is required to re-enable a client.) The timeout,
checker program, and interval between checks can be configured
both globally and per client; see
mandos-clients.conf
- 5.
+ 5. A client successfully
+ receiving its password will also be treated as a successful
+ checker run.
=== modified file 'plugin-runner.c'
--- plugin-runner.c 2009-02-12 19:08:35 +0000
+++ plugin-runner.c 2009-09-10 06:28:14 +0000
@@ -1,4 +1,4 @@
-/* -*- coding: utf-8 -*- */
+/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
/*
* Mandos plugin runner - Run Mandos plugins
*
@@ -38,7 +38,8 @@
#include /* fd_set, select(), FD_ZERO(),
FD_SET(), FD_ISSET(), FD_CLR */
#include /* wait(), waitpid(), WIFEXITED(),
- WEXITSTATUS() */
+ WEXITSTATUS(), WTERMSIG(),
+ WCOREDUMP() */
#include /* struct stat, stat(), S_ISREG() */
#include /* and, or, not */
#include /* DIR, struct dirent, opendir(),
@@ -52,7 +53,8 @@
close() */
#include /* fcntl(), F_GETFD, F_SETFD,
FD_CLOEXEC */
-#include /* strsep, strlen(), asprintf() */
+#include /* strsep, strlen(), asprintf(),
+ strsignal() */
#include /* errno */
#include /* struct argp_option, struct
argp_state, struct argp,
@@ -108,13 +110,18 @@
}
}
/* Create a new plugin */
- plugin *new_plugin = malloc(sizeof(plugin));
+ plugin *new_plugin = NULL;
+ do {
+ new_plugin = malloc(sizeof(plugin));
+ } while(new_plugin == NULL and errno == EINTR);
if(new_plugin == NULL){
return NULL;
}
char *copy_name = NULL;
if(name != NULL){
- copy_name = strdup(name);
+ do {
+ copy_name = strdup(name);
+ } while(copy_name == NULL and errno == EINTR);
if(copy_name == NULL){
free(new_plugin);
return NULL;
@@ -126,7 +133,9 @@
.disabled = false,
.next = plugin_list };
- new_plugin->argv = malloc(sizeof(char *) * 2);
+ do {
+ new_plugin->argv = malloc(sizeof(char *) * 2);
+ } while(new_plugin->argv == NULL and errno == EINTR);
if(new_plugin->argv == NULL){
free(copy_name);
free(new_plugin);
@@ -135,7 +144,9 @@
new_plugin->argv[0] = copy_name;
new_plugin->argv[1] = NULL;
- new_plugin->environ = malloc(sizeof(char *));
+ do {
+ new_plugin->environ = malloc(sizeof(char *));
+ } while(new_plugin->environ == NULL and errno == EINTR);
if(new_plugin->environ == NULL){
free(copy_name);
free(new_plugin->argv);
@@ -153,14 +164,19 @@
static bool add_to_char_array(const char *new, char ***array,
int *len){
/* Resize the pointed-to array to hold one more pointer */
- *array = realloc(*array, sizeof(char *)
- * (size_t) ((*len) + 2));
+ do {
+ *array = realloc(*array, sizeof(char *)
+ * (size_t) ((*len) + 2));
+ } while(*array == NULL and errno == EINTR);
/* Malloc check */
if(*array == NULL){
return false;
}
/* Make a copy of the new string */
- char *copy = strdup(new);
+ char *copy;
+ do {
+ copy = strdup(new);
+ } while(copy == NULL and errno == EINTR);
if(copy == NULL){
return false;
}
@@ -192,7 +208,10 @@
if(strncmp(*e, def, namelen + 1) == 0){
/* It already exists */
if(replace){
- char *new = realloc(*e, strlen(def) + 1);
+ char *new;
+ do {
+ new = realloc(*e, strlen(def) + 1);
+ } while(new == NULL and errno == EINTR);
if(new == NULL){
return false;
}
@@ -208,16 +227,17 @@
/*
* Based on the example in the GNU LibC manual chapter 13.13 "File
* Descriptor Flags".
- * *Note File Descriptor Flags:(libc)Descriptor Flags.
+ | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
*/
static int set_cloexec_flag(int fd){
- int ret = fcntl(fd, F_GETFD, 0);
+ int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
/* If reading the flags failed, return error indication now. */
if(ret < 0){
return ret;
}
/* Store modified flag word in the descriptor. */
- return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
+ return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
+ ret | FD_CLOEXEC));
}
@@ -315,7 +335,6 @@
fd_set rfds_all;
int ret, maxfd = 0;
ssize_t sret;
- intmax_t tmpmax;
uid_t uid = 65534;
gid_t gid = 65534;
bool debug = false;
@@ -380,8 +399,9 @@
error_t parse_opt(int key, char *arg, __attribute__((unused))
struct argp_state *state){
- char *tmp;
switch(key){
+ char *tmp;
+ intmax_t tmpmax;
case 'g': /* --global-options */
if(arg != NULL){
char *plugin_option;
@@ -457,7 +477,7 @@
plugindir = strdup(arg);
if(plugindir == NULL){
perror("strdup");
- }
+ }
break;
case 129: /* --config-file */
/* This is already done by parse_opt_config_file() */
@@ -527,7 +547,7 @@
if(argfile == NULL){
perror("strdup");
}
- break;
+ break;
case 130: /* --userid */
case 131: /* --groupid */
case 132: /* --debug */
@@ -562,7 +582,7 @@
conffp = fopen(AFILE, "r");
} else {
conffp = fopen(argfile, "r");
- }
+ }
if(conffp != NULL){
char *org_line = NULL;
char *p, *arg, *new_arg, *line;
@@ -612,9 +632,17 @@
goto fallback;
}
custom_argv[custom_argc-1] = new_arg;
- custom_argv[custom_argc] = NULL;
+ custom_argv[custom_argc] = NULL;
}
}
+ do {
+ ret = fclose(conffp);
+ } while(ret == EOF and errno == EINTR);
+ if(ret == EOF){
+ perror("fclose");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
free(org_line);
} else {
/* Check for harmful errors and go to fallback. Other errors might
@@ -699,7 +727,9 @@
/* Read and execute any executable in the plugin directory*/
while(true){
- dirst = readdir(dir);
+ do {
+ dirst = readdir(dir);
+ } while(dirst == NULL and errno == EINTR);
/* All directory entries have been processed */
if(dirst == NULL){
@@ -759,16 +789,19 @@
char *filename;
if(plugindir == NULL){
- ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
+ ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
+ dirst->d_name));
} else {
- ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
+ ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
+ plugindir,
+ dirst->d_name));
}
if(ret < 0){
perror("asprintf");
continue;
}
- ret = stat(filename, &st);
+ ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
if(ret == -1){
perror("stat");
free(filename);
@@ -776,7 +809,8 @@
}
/* Ignore non-executable files */
- if(not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
+ if(not S_ISREG(st.st_mode)
+ or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
if(debug){
fprintf(stderr, "Ignoring plugin dir entry \"%s\""
" with bad type or mode\n", filename);
@@ -828,7 +862,7 @@
}
int pipefd[2];
- ret = pipe(pipefd);
+ ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
if(ret == -1){
perror("pipe");
exitstatus = EXIT_FAILURE;
@@ -848,14 +882,19 @@
goto fallback;
}
/* Block SIGCHLD until process is safely in process list */
- ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
+ ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
+ &sigchld_action.sa_mask,
+ NULL));
if(ret < 0){
perror("sigprocmask");
exitstatus = EXIT_FAILURE;
goto fallback;
}
/* Starting a new process to be watched */
- pid_t pid = fork();
+ pid_t pid;
+ do {
+ pid = fork();
+ } while(pid == -1 and errno == EINTR);
if(pid == -1){
perror("fork");
exitstatus = EXIT_FAILURE;
@@ -899,12 +938,15 @@
/* no return */
}
/* Parent process */
- close(pipefd[1]); /* Close unused write end of pipe */
+ TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
+ pipe */
free(filename);
plugin *new_plugin = getplugin(dirst->d_name);
if(new_plugin == NULL){
perror("getplugin");
- ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
+ ret = (int)(TEMP_FAILURE_RETRY
+ (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
+ NULL)));
if(ret < 0){
perror("sigprocmask");
}
@@ -917,7 +959,9 @@
/* Unblock SIGCHLD so signal handler can be run if this process
has already completed */
- ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
+ ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
+ &sigchld_action.sa_mask,
+ NULL));
if(ret < 0){
perror("sigprocmask");
exitstatus = EXIT_FAILURE;
@@ -931,7 +975,7 @@
}
}
- closedir(dir);
+ TEMP_FAILURE_RETRY(closedir(dir));
dir = NULL;
free_plugin(getplugin(NULL));
@@ -950,7 +994,7 @@
while(plugin_list){
fd_set rfds = rfds_all;
int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
- if(select_ret == -1){
+ if(select_ret == -1 and errno != EINTR){
perror("select");
exitstatus = EXIT_FAILURE;
goto fallback;
@@ -973,9 +1017,10 @@
WEXITSTATUS(proc->status));
} else if(WIFSIGNALED(proc->status)){
fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
- " signal %d\n", proc->name,
+ " signal %d: %s\n", proc->name,
(intmax_t) (proc->pid),
- WTERMSIG(proc->status));
+ WTERMSIG(proc->status),
+ strsignal(WTERMSIG(proc->status)));
} else if(WCOREDUMP(proc->status)){
fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
" core\n", proc->name, (intmax_t) (proc->pid));
@@ -986,7 +1031,10 @@
FD_CLR(proc->fd, &rfds_all);
/* Block signal while modifying process_list */
- ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
+ ret = (int)TEMP_FAILURE_RETRY(sigprocmask
+ (SIG_BLOCK,
+ &sigchld_action.sa_mask,
+ NULL));
if(ret < 0){
perror("sigprocmask");
exitstatus = EXIT_FAILURE;
@@ -998,8 +1046,9 @@
proc = next_plugin;
/* We are done modifying process list, so unblock signal */
- ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
- NULL);
+ ret = (int)(TEMP_FAILURE_RETRY
+ (sigprocmask(SIG_UNBLOCK,
+ &sigchld_action.sa_mask, NULL)));
if(ret < 0){
perror("sigprocmask");
exitstatus = EXIT_FAILURE;
@@ -1042,8 +1091,10 @@
proc->buffer_size += BUFFER_SIZE;
}
/* Read from the process */
- sret = read(proc->fd, proc->buffer + proc->buffer_length,
- BUFFER_SIZE);
+ sret = TEMP_FAILURE_RETRY(read(proc->fd,
+ proc->buffer
+ + proc->buffer_length,
+ BUFFER_SIZE));
if(sret < 0){
/* Read error from this process; ignore the error */
proc = proc->next;
@@ -1111,7 +1162,7 @@
}
/* Wait for any remaining child processes to terminate */
- do{
+ do {
ret = wait(NULL);
} while(ret >= 0);
if(errno != ECHILD){
=== modified file 'plugin-runner.conf'
--- plugin-runner.conf 2009-02-09 02:01:13 +0000
+++ plugin-runner.conf 2009-04-17 08:26:17 +0000
@@ -1,6 +1,7 @@
-## This is the configuration file for plugin-runner. It should be
-## installed as "/etc/mandos/plugin-runner.conf", which will be copied
-## to "/conf/conf.d/mandos/plugin-runner.conf" in the initrd.img file.
+## This is the configuration file for plugin-runner(8mandos). This
+## file should be installed as "/etc/mandos/plugin-runner.conf", and
+## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the
+## initrd.img file.
##
## After editing this file, the initrd image file must be updated for
## the changes to take effect!
=== modified file 'plugins.d/askpass-fifo.c'
--- plugins.d/askpass-fifo.c 2009-01-10 06:00:50 +0000
+++ plugins.d/askpass-fifo.c 2009-09-16 23:28:39 +0000
@@ -19,8 +19,7 @@
* along with this program. If not, see
* .
*
- * Contact the authors at and
- * .
+ * Contact the authors at .
*/
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
@@ -29,7 +28,7 @@
#include /* and */
#include /* errno, EEXIST */
#include /* perror() */
-#include /* EXIT_FAILURE, NULL, size_t, free(),
+#include /* EXIT_FAILURE, NULL, size_t, free(),
realloc(), EXIT_SUCCESS */
#include /* open(), O_RDONLY */
#include /* read(), close(), write(),
@@ -43,14 +42,14 @@
/* Create FIFO */
const char passfifo[] = "/lib/cryptsetup/passfifo";
- ret = (int)TEMP_FAILURE_RETRY(mkfifo(passfifo, S_IRUSR | S_IWUSR));
+ ret = mkfifo(passfifo, S_IRUSR | S_IWUSR);
if(ret == -1 and errno != EEXIST){
perror("mkfifo");
return EXIT_FAILURE;
}
/* Open FIFO */
- int fifo_fd = (int)TEMP_FAILURE_RETRY(open(passfifo, O_RDONLY));
+ int fifo_fd = open(passfifo, O_RDONLY);
if(fifo_fd == -1){
perror("open");
return EXIT_FAILURE;
@@ -62,7 +61,7 @@
{
size_t buf_allocated = 0;
const size_t blocksize = 1024;
- do{
+ do {
if(buf_len + blocksize > buf_allocated){
char *tmp = realloc(buf, buf_allocated + blocksize);
if(tmp == NULL){
@@ -73,25 +72,23 @@
buf = tmp;
buf_allocated += blocksize;
}
- sret = TEMP_FAILURE_RETRY(read(fifo_fd, buf + buf_len,
- buf_allocated - buf_len));
+ sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
if(sret == -1){
perror("read");
free(buf);
return EXIT_FAILURE;
}
buf_len += (size_t)sret;
- }while(sret != 0);
+ } while(sret != 0);
}
/* Close FIFO */
- TEMP_FAILURE_RETRY(close(fifo_fd));
+ close(fifo_fd);
/* Print password to stdout */
size_t written = 0;
while(written < buf_len){
- sret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf + written,
- buf_len - written));
+ sret = write(STDOUT_FILENO, buf + written, buf_len - written);
if(sret == -1){
perror("write");
free(buf);
=== modified file 'plugins.d/mandos-client.c'
--- plugins.d/mandos-client.c 2009-02-14 18:07:05 +0000
+++ plugins.d/mandos-client.c 2009-09-17 11:22:28 +0000
@@ -44,7 +44,7 @@
#include /* uint16_t, uint32_t */
#include /* NULL, size_t, ssize_t */
#include /* free(), EXIT_SUCCESS, EXIT_FAILURE,
- srand(), strtof() */
+ srand(), strtof(), abort() */
#include /* bool, false, true */
#include /* memset(), strcmp(), strlen(),
strerror(), asprintf(), strcpy() */
@@ -71,8 +71,8 @@
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
*/
#include /* close(), SEEK_SET, off_t, write(),
- getuid(), getgid(), setuid(),
- setgid() */
+ getuid(), getgid(), seteuid(),
+ setgid(), pause() */
#include /* inet_pton(), htons */
#include /* not, or, and */
#include /* struct argp_option, error_t, struct
@@ -80,8 +80,8 @@
argp_parse(), ARGP_KEY_ARG,
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
#include /* sigemptyset(), sigaddset(),
- sigaction(), SIGTERM, sigaction,
- sig_atomic_t */
+ sigaction(), SIGTERM, sig_atomic_t,
+ raise() */
#ifdef __linux__
#include /* klogctl() */
@@ -142,6 +142,9 @@
.dh_bits = 1024, .priority = "SECURE256"
":!CTYPE-X.509:+CTYPE-OPENPGP" };
+sig_atomic_t quit_now = 0;
+int signal_received = 0;
+
/*
* Make additional room in "buffer" for at least BUFFER_SIZE more
* bytes. "buffer_capacity" is how much is currently allocated,
@@ -164,7 +167,6 @@
*/
static bool init_gpgme(const char *seckey,
const char *pubkey, const char *tempdir){
- int ret;
gpgme_error_t rc;
gpgme_engine_info_t engine_info;
@@ -173,6 +175,7 @@
* Helper function to insert pub and seckey to the engine keyring.
*/
bool import_key(const char *filename){
+ int ret;
int fd;
gpgme_data_t pgp_data;
@@ -249,7 +252,7 @@
return false;
}
- return true;
+ return true;
}
/*
@@ -309,16 +312,14 @@
}
gpgme_recipient_t recipient;
recipient = result->recipients;
- if(recipient){
- while(recipient != NULL){
- fprintf(stderr, "Public key algorithm: %s\n",
- gpgme_pubkey_algo_name(recipient->pubkey_algo));
- fprintf(stderr, "Key ID: %s\n", recipient->keyid);
- fprintf(stderr, "Secret key available: %s\n",
- recipient->status == GPG_ERR_NO_SECKEY
- ? "No" : "Yes");
- recipient = recipient->next;
- }
+ while(recipient != NULL){
+ fprintf(stderr, "Public key algorithm: %s\n",
+ gpgme_pubkey_algo_name(recipient->pubkey_algo));
+ fprintf(stderr, "Key ID: %s\n", recipient->keyid);
+ fprintf(stderr, "Secret key available: %s\n",
+ recipient->status == GPG_ERR_NO_SECKEY
+ ? "No" : "Yes");
+ recipient = recipient->next;
}
}
}
@@ -476,7 +477,12 @@
static int init_gnutls_session(gnutls_session_t *session){
int ret;
/* GnuTLS session creation */
- ret = gnutls_init(session, GNUTLS_SERVER);
+ do {
+ ret = gnutls_init(session, GNUTLS_SERVER);
+ if(quit_now){
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
if(ret != GNUTLS_E_SUCCESS){
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
safer_gnutls_strerror(ret));
@@ -484,7 +490,13 @@
{
const char *err;
- ret = gnutls_priority_set_direct(*session, mc.priority, &err);
+ do {
+ ret = gnutls_priority_set_direct(*session, mc.priority, &err);
+ if(quit_now){
+ gnutls_deinit(*session);
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
if(ret != GNUTLS_E_SUCCESS){
fprintf(stderr, "Syntax error at: %s\n", err);
fprintf(stderr, "GnuTLS error: %s\n",
@@ -494,8 +506,14 @@
}
}
- ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
- mc.cred);
+ do {
+ ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
+ mc.cred);
+ if(quit_now){
+ gnutls_deinit(*session);
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
if(ret != GNUTLS_E_SUCCESS){
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
safer_gnutls_strerror(ret));
@@ -504,8 +522,7 @@
}
/* ignore client certificate if any. */
- gnutls_certificate_server_set_request(*session,
- GNUTLS_CERT_IGNORE);
+ gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
@@ -520,22 +537,25 @@
static int start_mandos_communication(const char *ip, uint16_t port,
AvahiIfIndex if_index,
int af){
- int ret, tcp_sd;
+ int ret, tcp_sd = -1;
ssize_t sret;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} to;
char *buffer = NULL;
- char *decrypted_buffer;
+ char *decrypted_buffer = NULL;
size_t buffer_length = 0;
size_t buffer_capacity = 0;
- ssize_t decrypted_buffer_size;
size_t written;
- int retval = 0;
+ int retval = -1;
gnutls_session_t session;
int pf; /* Protocol family */
+ if(quit_now){
+ return -1;
+ }
+
switch(af){
case AF_INET6:
pf = PF_INET6;
@@ -561,12 +581,16 @@
tcp_sd = socket(pf, SOCK_STREAM, 0);
if(tcp_sd < 0){
perror("socket");
- return -1;
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ goto mandos_end;
}
memset(&to, 0, sizeof(to));
if(af == AF_INET6){
- to.in6.sin6_family = (uint16_t)af;
+ to.in6.sin6_family = (sa_family_t)af;
ret = inet_pton(af, ip, &to.in6.sin6_addr);
} else { /* IPv4 */
to.in.sin_family = (sa_family_t)af;
@@ -574,11 +598,11 @@
}
if(ret < 0 ){
perror("inet_pton");
- return -1;
+ goto mandos_end;
}
if(ret == 0){
fprintf(stderr, "Bad address: %s\n", ip);
- return -1;
+ goto mandos_end;
}
if(af == AF_INET6){
to.in6.sin6_port = htons(port); /* Spurious warnings from
@@ -591,7 +615,7 @@
if(if_index == AVAHI_IF_UNSPEC){
fprintf(stderr, "An IPv6 link-local address is incomplete"
" without a network interface\n");
- return -1;
+ goto mandos_end;
}
/* Set the network interface number as scope */
to.in6.sin6_scope_id = (uint32_t)if_index;
@@ -602,6 +626,10 @@
-Wunreachable-code */
}
+ if(quit_now){
+ goto mandos_end;
+ }
+
if(debug){
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
char interface[IF_NAMESIZE];
@@ -634,6 +662,10 @@
}
}
+ if(quit_now){
+ goto mandos_end;
+ }
+
if(af == AF_INET6){
ret = connect(tcp_sd, &to.in6, sizeof(to));
} else {
@@ -641,7 +673,11 @@
}
if(ret < 0){
perror("connect");
- return -1;
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ goto mandos_end;
}
const char *out = mandos_protocol_version;
@@ -652,7 +688,6 @@
out_size - written));
if(ret == -1){
perror("write");
- retval = -1;
goto mandos_end;
}
written += (size_t)ret;
@@ -666,16 +701,31 @@
break;
}
}
+
+ if(quit_now){
+ goto mandos_end;
+ }
}
if(debug){
fprintf(stderr, "Establishing TLS session with %s\n", ip);
}
+ if(quit_now){
+ goto mandos_end;
+ }
+
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
- do{
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ do {
ret = gnutls_handshake(session);
+ if(quit_now){
+ goto mandos_end;
+ }
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
if(ret != GNUTLS_E_SUCCESS){
@@ -683,7 +733,6 @@
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
gnutls_perror(ret);
}
- retval = -1;
goto mandos_end;
}
@@ -695,11 +744,19 @@
}
while(true){
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
buffer_capacity = incbuffer(&buffer, buffer_length,
buffer_capacity);
if(buffer_capacity == 0){
perror("incbuffer");
- retval = -1;
+ goto mandos_end;
+ }
+
+ if(quit_now){
goto mandos_end;
}
@@ -714,20 +771,22 @@
case GNUTLS_E_AGAIN:
break;
case GNUTLS_E_REHANDSHAKE:
- do{
+ do {
ret = gnutls_handshake(session);
+
+ if(quit_now){
+ goto mandos_end;
+ }
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
if(ret < 0){
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
gnutls_perror(ret);
- retval = -1;
goto mandos_end;
}
break;
default:
fprintf(stderr, "Unknown error while reading data from"
" encrypted session with Mandos server\n");
- retval = -1;
gnutls_bye(session, GNUTLS_SHUT_RDWR);
goto mandos_end;
}
@@ -740,15 +799,30 @@
fprintf(stderr, "Closing TLS session\n");
}
- gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if(quit_now){
+ goto mandos_end;
+ }
+ } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
if(buffer_length > 0){
+ ssize_t decrypted_buffer_size;
decrypted_buffer_size = pgp_packet_decrypt(buffer,
buffer_length,
&decrypted_buffer);
if(decrypted_buffer_size >= 0){
+
written = 0;
while(written < (size_t) decrypted_buffer_size){
+ if(quit_now){
+ goto mandos_end;
+ }
+
ret = (int)fwrite(decrypted_buffer + written, 1,
(size_t)decrypted_buffer_size - written,
stdout);
@@ -757,28 +831,29 @@
fprintf(stderr, "Error writing encrypted data: %s\n",
strerror(errno));
}
- retval = -1;
- break;
+ goto mandos_end;
}
written += (size_t)ret;
}
- free(decrypted_buffer);
- } else {
- retval = -1;
+ retval = 0;
}
- } else {
- retval = -1;
}
/* Shutdown procedure */
mandos_end:
+ free(decrypted_buffer);
free(buffer);
- ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
+ if(tcp_sd >= 0){
+ ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
+ }
if(ret == -1){
perror("close");
}
gnutls_deinit(session);
+ if(quit_now){
+ retval = -1;
+ }
return retval;
}
@@ -801,6 +876,10 @@
/* Called whenever a service has been resolved successfully or
timed out */
+ if(quit_now){
+ return;
+ }
+
switch(event){
default:
case AVAHI_RESOLVER_FAILURE:
@@ -843,6 +922,10 @@
/* Called whenever a new services becomes available on the LAN or
is removed from the LAN */
+ if(quit_now){
+ return;
+ }
+
switch(event){
default:
case AVAHI_BROWSER_FAILURE:
@@ -877,14 +960,13 @@
}
}
-sig_atomic_t quit_now = 0;
-
/* stop main loop after sigterm has been called */
-static void handle_sigterm(__attribute__((unused)) int sig){
+static void handle_sigterm(int sig){
if(quit_now){
return;
}
quit_now = 1;
+ signal_received = sig;
int old_errno = errno;
if(mc.simple_poll != NULL){
avahi_simple_poll_quit(mc.simple_poll);
@@ -901,7 +983,8 @@
int exitcode = EXIT_SUCCESS;
const char *interface = "eth0";
struct ifreq network;
- int sd;
+ int sd = -1;
+ bool take_down_interface = false;
uid_t uid;
gid_t gid;
char *connect_to = NULL;
@@ -915,9 +998,30 @@
bool gpgme_initialized = false;
float delay = 2.5f;
- struct sigaction old_sigterm_action;
+ struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
+ uid = getuid();
+ gid = getgid();
+
+ /* Lower any group privileges we might have, just to be safe */
+ errno = 0;
+ ret = setgid(gid);
+ if(ret == -1){
+ perror("setgid");
+ }
+
+ /* Lower user privileges (temporarily) */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
{
struct argp_option options[] = {
{ .name = "debug", .key = 128,
@@ -1050,15 +1154,70 @@
exitcode = EXIT_FAILURE;
goto end;
}
- ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
- if(ret == -1){
- perror("sigaction");
- exitcode = EXIT_FAILURE;
- goto end;
- }
+ /* Need to check if the handler is SIG_IGN before handling:
+ | [[info:libc:Initial Signal Actions]] |
+ | [[info:libc:Basic Signal Handling]] |
+ */
+ ret = sigaction(SIGINT, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGINT, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+ ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGHUP, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+ ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGTERM, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
/* If the interface is down, bring it up */
if(interface[0] != '\0'){
+ if_index = (AvahiIfIndex) if_nametoindex(interface);
+ if(if_index == 0){
+ fprintf(stderr, "No such interface: \"%s\"\n", interface);
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ /* Re-raise priviliges */
+ errno = 0;
+ ret = seteuid(0);
+ if(ret == -1){
+ perror("seteuid");
+ }
+
#ifdef __linux__
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
messages to mess up the prompt */
@@ -1082,6 +1241,12 @@
}
}
#endif /* __linux__ */
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
goto end;
}
strcpy(network.ifr_name, interface);
@@ -1097,12 +1262,20 @@
}
#endif /* __linux__ */
exitcode = EXIT_FAILURE;
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
goto end;
}
if((network.ifr_flags & IFF_UP) == 0){
network.ifr_flags |= IFF_UP;
+ take_down_interface = true;
ret = ioctl(sd, SIOCSIFFLAGS, &network);
if(ret == -1){
+ take_down_interface = false;
perror("ioctl SIOCSIFFLAGS");
exitcode = EXIT_FAILURE;
#ifdef __linux__
@@ -1113,6 +1286,12 @@
}
}
#endif /* __linux__ */
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
goto end;
}
}
@@ -1130,9 +1309,12 @@
perror("nanosleep");
}
}
- ret = (int)TEMP_FAILURE_RETRY(close(sd));
- if(ret == -1){
- perror("close");
+ if(not take_down_interface){
+ /* We won't need the socket anymore */
+ ret = (int)TEMP_FAILURE_RETRY(close(sd));
+ if(ret == -1){
+ perror("close");
+ }
}
#ifdef __linux__
if(restore_loglevel){
@@ -1143,20 +1325,25 @@
}
}
#endif /* __linux__ */
- }
-
- uid = getuid();
- gid = getgid();
-
- errno = 0;
- setgid(gid);
- if(ret == -1){
- perror("setgid");
- }
-
- ret = setuid(uid);
- if(ret == -1){
- perror("setuid");
+ /* Lower privileges */
+ errno = 0;
+ if(take_down_interface){
+ /* Lower privileges */
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ } else {
+ /* Lower privileges permanently */
+ ret = setuid(uid);
+ if(ret == -1){
+ perror("setuid");
+ }
+ }
+ }
+
+ if(quit_now){
+ goto end;
}
ret = init_gnutls_global(pubkey, seckey);
@@ -1168,11 +1355,20 @@
gnutls_initialized = true;
}
+ if(quit_now){
+ goto end;
+ }
+
+ tempdir_created = true;
if(mkdtemp(tempdir) == NULL){
+ tempdir_created = false;
perror("mkdtemp");
goto end;
}
- tempdir_created = true;
+
+ if(quit_now){
+ goto end;
+ }
if(not init_gpgme(pubkey, seckey, tempdir)){
fprintf(stderr, "init_gpgme failed\n");
@@ -1182,13 +1378,8 @@
gpgme_initialized = true;
}
- if(interface[0] != '\0'){
- if_index = (AvahiIfIndex) if_nametoindex(interface);
- if(if_index == 0){
- fprintf(stderr, "No such interface: \"%s\"\n", interface);
- exitcode = EXIT_FAILURE;
- goto end;
- }
+ if(quit_now){
+ goto end;
}
if(connect_to != NULL){
@@ -1200,6 +1391,11 @@
exitcode = EXIT_FAILURE;
goto end;
}
+
+ if(quit_now){
+ goto end;
+ }
+
uint16_t port;
errno = 0;
tmpmax = strtoimax(address+1, &tmp, 10);
@@ -1209,6 +1405,11 @@
exitcode = EXIT_FAILURE;
goto end;
}
+
+ if(quit_now){
+ goto end;
+ }
+
port = (uint16_t)tmpmax;
*address = '\0';
address = connect_to;
@@ -1219,6 +1420,11 @@
} else {
af = AF_INET;
}
+
+ if(quit_now){
+ goto end;
+ }
+
ret = start_mandos_communication(address, port, if_index, af);
if(ret < 0){
exitcode = EXIT_FAILURE;
@@ -1227,7 +1433,11 @@
}
goto end;
}
-
+
+ if(quit_now){
+ goto end;
+ }
+
{
AvahiServerConfig config;
/* Do not publish any local Zeroconf records */
@@ -1254,6 +1464,10 @@
goto end;
}
+ if(quit_now){
+ goto end;
+ }
+
/* Create the Avahi service browser */
sb = avahi_s_service_browser_new(mc.server, if_index,
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
@@ -1265,6 +1479,10 @@
goto end;
}
+ if(quit_now){
+ goto end;
+ }
+
/* Run the main loop */
if(debug){
@@ -1299,6 +1517,38 @@
gpgme_release(mc.ctx);
}
+ /* Take down the network interface */
+ if(take_down_interface){
+ /* Re-raise priviliges */
+ errno = 0;
+ ret = seteuid(0);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ if(geteuid() == 0){
+ ret = ioctl(sd, SIOCGIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCGIFFLAGS");
+ } else if(network.ifr_flags & IFF_UP) {
+ network.ifr_flags &= ~IFF_UP; /* clear flag */
+ ret = ioctl(sd, SIOCSIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCSIFFLAGS");
+ }
+ }
+ ret = (int)TEMP_FAILURE_RETRY(close(sd));
+ if(ret == -1){
+ perror("close");
+ }
+ /* Lower privileges permanently */
+ errno = 0;
+ ret = setuid(uid);
+ if(ret == -1){
+ perror("setuid");
+ }
+ }
+ }
+
/* Removes the temp directory used by GPGME */
if(tempdir_created){
DIR *d;
@@ -1343,5 +1593,24 @@
}
}
+ if(quit_now){
+ sigemptyset(&old_sigterm_action.sa_mask);
+ old_sigterm_action.sa_handler = SIG_DFL;
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &old_sigterm_action,
+ NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
+ }
+
return exitcode;
}
=== modified file 'plugins.d/password-prompt.c'
--- plugins.d/password-prompt.c 2009-02-05 02:33:05 +0000
+++ plugins.d/password-prompt.c 2009-09-07 07:48:59 +0000
@@ -1,4 +1,4 @@
-/* -*- coding: utf-8 -*- */
+/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
/*
* Password-prompt - Read a password from the terminal and print it
*
@@ -19,8 +19,7 @@
* along with this program. If not, see
* .
*
- * Contact the authors at and
- * .
+ * Contact the authors at .
*/
#define _GNU_SOURCE /* getline() */
@@ -33,7 +32,8 @@
#include /* sig_atomic_t, raise(), struct
sigaction, sigemptyset(),
sigaction(), sigaddset(), SIGINT,
- SIGQUIT, SIGHUP, SIGTERM */
+ SIGQUIT, SIGHUP, SIGTERM,
+ raise() */
#include /* NULL, size_t, ssize_t */
#include /* ssize_t */
#include /* EXIT_SUCCESS, EXIT_FAILURE,
@@ -52,12 +52,17 @@
ARGP_ERR_UNKNOWN */
volatile sig_atomic_t quit_now = 0;
+int signal_received;
bool debug = false;
const char *argp_program_version = "password-prompt " VERSION;
const char *argp_program_bug_address = "";
-static void termination_handler(__attribute__((unused))int signum){
+static void termination_handler(int signum){
+ if(quit_now){
+ return;
+ }
quit_now = 1;
+ signal_received = signum;
}
int main(int argc, char **argv){
@@ -123,9 +128,25 @@
}
sigemptyset(&new_action.sa_mask);
- sigaddset(&new_action.sa_mask, SIGINT);
- sigaddset(&new_action.sa_mask, SIGHUP);
- sigaddset(&new_action.sa_mask, SIGTERM);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ /* Need to check if the handler is SIG_IGN before handling:
+ | [[info:libc:Initial Signal Actions]] |
+ | [[info:libc:Basic Signal Handling]] |
+ */
ret = sigaction(SIGINT, NULL, &old_action);
if(ret == -1){
perror("sigaction");
@@ -194,7 +215,7 @@
const char *cryptsource = getenv("cryptsource");
const char *crypttarget = getenv("crypttarget");
const char *const prompt
- = "Enter passphrase to unlock the disk";
+ = "Enter passphrase to unlock the disk";
if(cryptsource == NULL){
if(crypttarget == NULL){
fprintf(stderr, "%s: ", prompt);
@@ -242,7 +263,7 @@
/* if(ret == 0), then the only sensible thing to do is to retry to
read from stdin */
fputc('\n', stderr);
- if(debug and quit_now == 0){
+ if(debug and not quit_now){
/* If quit_now is nonzero, we were interrupted by a signal, and
will print that later, so no need to show this too. */
fprintf(stderr, "getline() returned 0, retrying.\n");
@@ -258,6 +279,16 @@
perror("tcsetattr+echo");
}
+ if(quit_now){
+ sigemptyset(&old_action.sa_mask);
+ old_action.sa_handler = SIG_DFL;
+ ret = sigaction(signal_received, &old_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ }
+ raise(signal_received);
+ }
+
if(debug){
fprintf(stderr, "%s is exiting with status %d\n", argv[0],
status);
=== modified file 'plugins.d/splashy.c'
--- plugins.d/splashy.c 2009-02-12 18:56:52 +0000
+++ plugins.d/splashy.c 2009-09-16 23:28:39 +0000
@@ -19,11 +19,10 @@
* along with this program. If not, see
* .
*
- * Contact the authors at and
- * .
+ * Contact the authors at .
*/
-#define _GNU_SOURCE /* asprintf() */
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
#include /* sig_atomic_t, struct sigaction,
sigemptyset(), sigaddset(), SIGINT,
SIGHUP, SIGTERM, sigaction,
@@ -41,24 +40,33 @@
#include /* not, or, and */
#include /* readlink(), fork(), execl(),
sleep(), dup2() STDERR_FILENO,
- STDOUT_FILENO, _exit() */
+ STDOUT_FILENO, _exit(),
+ pause() */
#include /* memcmp() */
#include /* errno */
#include /* waitpid(), WIFEXITED(),
WEXITSTATUS() */
sig_atomic_t interrupted_by_signal = 0;
+int signal_received;
-static void termination_handler(__attribute__((unused))int signum){
+static void termination_handler(int signum){
+ if(interrupted_by_signal){
+ return;
+ }
interrupted_by_signal = 1;
+ signal_received = signum;
}
int main(__attribute__((unused))int argc,
__attribute__((unused))char **argv){
int ret = 0;
+ char *prompt = NULL;
+ DIR *proc_dir = NULL;
+ pid_t splashy_pid = 0;
+ pid_t splashy_command_pid = 0;
/* Create prompt string */
- char *prompt = NULL;
{
const char *const cryptsource = getenv("cryptsource");
const char *const crypttarget = getenv("crypttarget");
@@ -81,19 +89,18 @@
}
}
if(ret == -1){
- return EXIT_FAILURE;
+ prompt = NULL;
+ goto failure;
}
}
/* Find splashy process */
- pid_t splashy_pid = 0;
{
const char splashy_name[] = "/sbin/splashy";
- DIR *proc_dir = opendir("/proc");
+ proc_dir = opendir("/proc");
if(proc_dir == NULL){
- free(prompt);
perror("opendir");
- return EXIT_FAILURE;
+ goto failure;
}
for(struct dirent *proc_ent = readdir(proc_dir);
proc_ent != NULL;
@@ -120,9 +127,7 @@
ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
if(ret == -1){
perror("asprintf");
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
+ goto failure;
}
/* Check that it refers to a symlink owned by root:root */
@@ -135,9 +140,7 @@
}
perror("lstat");
free(exe_link);
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
+ goto failure;
}
if(not S_ISLNK(exe_stat.st_mode)
or exe_stat.st_uid != 0
@@ -157,10 +160,10 @@
}
}
closedir(proc_dir);
+ proc_dir = NULL;
}
if(splashy_pid == 0){
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
/* Set up the signal handler */
@@ -169,135 +172,180 @@
new_action = { .sa_handler = termination_handler,
.sa_flags = 0 };
sigemptyset(&new_action.sa_mask);
- sigaddset(&new_action.sa_mask, SIGINT);
- sigaddset(&new_action.sa_mask, SIGHUP);
- sigaddset(&new_action.sa_mask, SIGTERM);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
ret = sigaction(SIGINT, NULL, &old_action);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGINT, &new_action, NULL);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
}
ret = sigaction(SIGHUP, NULL, &old_action);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGHUP, &new_action, NULL);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
}
ret = sigaction(SIGTERM, NULL, &old_action);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGTERM, &new_action, NULL);
if(ret == -1){
perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
}
}
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
/* Fork off the splashy command to prompt for password */
- pid_t splashy_command_pid = 0;
- if(not interrupted_by_signal){
- splashy_command_pid = fork();
- if(splashy_command_pid == -1){
- if(not interrupted_by_signal){
- perror("fork");
- }
- return EXIT_FAILURE;
- }
- /* Child */
- if(splashy_command_pid == 0){
+ splashy_command_pid = fork();
+ if(splashy_command_pid != 0 and interrupted_by_signal){
+ goto failure;
+ }
+ if(splashy_command_pid == -1){
+ perror("fork");
+ goto failure;
+ }
+ /* Child */
+ if(splashy_command_pid == 0){
+ if(not interrupted_by_signal){
const char splashy_command[] = "/sbin/splashy_update";
- ret = execl(splashy_command, splashy_command, prompt,
- (char *)NULL);
- if(not interrupted_by_signal){
- perror("execl");
- }
- free(prompt);
- _exit(EXIT_FAILURE);
+ execl(splashy_command, splashy_command, prompt, (char *)NULL);
+ perror("execl");
}
+ free(prompt);
+ _exit(EXIT_FAILURE);
}
/* Parent */
free(prompt);
+ prompt = NULL;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
/* Wait for command to complete */
- if(not interrupted_by_signal and splashy_command_pid != 0){
+ {
int status;
- ret = waitpid(splashy_command_pid, &status, 0);
+ do {
+ ret = waitpid(splashy_command_pid, &status, 0);
+ } while(ret == -1 and errno == EINTR
+ and not interrupted_by_signal);
+ if(interrupted_by_signal){
+ goto failure;
+ }
if(ret == -1){
- if(errno != EINTR){
- perror("waitpid");
- }
+ perror("waitpid");
if(errno == ECHILD){
splashy_command_pid = 0;
}
} else {
/* The child process has exited */
splashy_command_pid = 0;
- if(not interrupted_by_signal and WIFEXITED(status)
- and WEXITSTATUS(status)==0){
+ if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
return EXIT_SUCCESS;
}
}
}
- kill(splashy_pid, SIGTERM);
- if(interrupted_by_signal and splashy_command_pid != 0){
- kill(splashy_command_pid, SIGTERM);
- }
- sleep(2);
- while(kill(splashy_pid, 0) == 0){
- kill(splashy_pid, SIGKILL);
- sleep(1);
- }
- pid_t new_splashy_pid = fork();
- if(new_splashy_pid == 0){
- /* Child; will become new splashy process */
+
+ failure:
+
+ free(prompt);
+
+ if(proc_dir != NULL){
+ TEMP_FAILURE_RETRY(closedir(proc_dir));
+ }
+
+ if(splashy_command_pid != 0){
+ TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM));
- /* Make the effective user ID (root) the only user ID instead of
- the real user ID (_mandos) */
- ret = setuid(geteuid());
- if(ret == -1){
- perror("setuid");
+ TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM));
+ sleep(2);
+ while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){
+ TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL));
+ sleep(1);
}
+ pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork());
+ if(new_splashy_pid == 0){
+ /* Child; will become new splashy process */
+
+ /* Make the effective user ID (root) the only user ID instead of
+ the real user ID (_mandos) */
+ ret = setuid(geteuid());
+ if(ret == -1){
+ perror("setuid");
+ }
+
+ setsid();
+ ret = chdir("/");
+ if(ret == -1){
+ perror("chdir");
+ }
+/* if(fork() != 0){ */
+/* _exit(EXIT_SUCCESS); */
+/* } */
+ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
+ if(ret == -1){
+ perror("dup2");
+ _exit(EXIT_FAILURE);
+ }
- setsid();
- ret = chdir("/");
-/* if(fork() != 0){ */
-/* _exit(EXIT_SUCCESS); */
-/* } */
- ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
- if(ret == -1){
- perror("dup2");
+ execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
+ perror("execl");
_exit(EXIT_FAILURE);
}
-
- execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
- if(not interrupted_by_signal){
- perror("execl");
- }
- _exit(EXIT_FAILURE);
+ }
+
+ if(interrupted_by_signal){
+ struct sigaction signal_action;
+ sigemptyset(&signal_action.sa_mask);
+ signal_action.sa_handler = SIG_DFL;
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &signal_action, NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
}
return EXIT_FAILURE;
=== modified file 'plugins.d/usplash.c'
--- plugins.d/usplash.c 2009-02-12 18:56:52 +0000
+++ plugins.d/usplash.c 2009-09-17 04:16:32 +0000
@@ -19,11 +19,10 @@
* along with this program. If not, see
* .
*
- * Contact the authors at and
- * .
+ * Contact the authors at .
*/
-#define _GNU_SOURCE /* asprintf() */
+#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */
#include /* sig_atomic_t, struct sigaction,
sigemptyset(), sigaddset(), SIGINT,
SIGHUP, SIGTERM, sigaction(),
@@ -50,242 +49,266 @@
#include /* struct stat, lstat(), S_ISLNK */
sig_atomic_t interrupted_by_signal = 0;
+int signal_received;
+const char usplash_name[] = "/sbin/usplash";
-static void termination_handler(__attribute__((unused))int signum){
+static void termination_handler(int signum){
+ if(interrupted_by_signal){
+ return;
+ }
interrupted_by_signal = 1;
+ signal_received = signum;
}
-static bool usplash_write(const char *cmd, const char *arg){
+static bool usplash_write(int *fifo_fd_r,
+ const char *cmd, const char *arg){
/*
- * usplash_write("TIMEOUT", "15") will write "TIMEOUT 15\0"
- * usplash_write("PULSATE", NULL) will write "PULSATE\0"
+ * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0"
+ * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0"
* SEE ALSO
* usplash_write(8)
*/
int ret;
- int fifo_fd;
- do{
- fifo_fd = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
- if(fifo_fd == -1 and (errno != EINTR or interrupted_by_signal)){
+ if(*fifo_fd_r == -1){
+ ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
+ if(ret == -1){
return false;
}
- }while(fifo_fd == -1);
+ *fifo_fd_r = ret;
+ }
const char *cmd_line;
size_t cmd_line_len;
char *cmd_line_alloc = NULL;
if(arg == NULL){
cmd_line = cmd;
- cmd_line_len = strlen(cmd);
- }else{
- do{
+ cmd_line_len = strlen(cmd) + 1;
+ } else {
+ do {
ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
- if(ret == -1 and (errno != EINTR or interrupted_by_signal)){
+ if(ret == -1){
int e = errno;
- close(fifo_fd);
+ TEMP_FAILURE_RETRY(close(*fifo_fd_r));
errno = e;
return false;
}
- }while(ret == -1);
+ } while(ret == -1);
cmd_line = cmd_line_alloc;
cmd_line_len = (size_t)ret + 1;
}
size_t written = 0;
ssize_t sret = 0;
- while(not interrupted_by_signal and written < cmd_line_len){
- sret = write(fifo_fd, cmd_line + written,
+ while(written < cmd_line_len){
+ sret = write(*fifo_fd_r, cmd_line + written,
cmd_line_len - written);
if(sret == -1){
- if(errno != EINTR or interrupted_by_signal){
- int e = errno;
- close(fifo_fd);
- free(cmd_line_alloc);
- errno = e;
- return false;
- } else {
- continue;
- }
+ int e = errno;
+ TEMP_FAILURE_RETRY(close(*fifo_fd_r));
+ free(cmd_line_alloc);
+ errno = e;
+ return false;
}
written += (size_t)sret;
}
free(cmd_line_alloc);
- do{
- ret = close(fifo_fd);
- if(ret == -1 and (errno != EINTR or interrupted_by_signal)){
- return false;
- }
- }while(ret == -1);
- if(interrupted_by_signal){
- return false;
- }
+
return true;
}
+/* 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;
+}
+
+pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){
+ int ret = 0;
+ ssize_t sret = 0;
+ char *cmdline = NULL;
+ size_t cmdline_len = 0;
+ DIR *proc_dir = opendir("/proc");
+ if(proc_dir == NULL){
+ perror("opendir");
+ return -1;
+ }
+ errno = 0;
+ for(struct dirent *proc_ent = readdir(proc_dir);
+ proc_ent != NULL;
+ proc_ent = readdir(proc_dir)){
+ pid_t pid;
+ {
+ intmax_t tmpmax;
+ char *tmp;
+ tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
+ if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
+ or tmpmax != (pid_t)tmpmax){
+ /* Not a process */
+ errno = 0;
+ continue;
+ }
+ pid = (pid_t)tmpmax;
+ }
+ /* Find the executable name by doing readlink() on the
+ /proc//exe link */
+ char exe_target[sizeof(usplash_name)];
+ {
+ /* create file name string */
+ char *exe_link;
+ ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
+ if(ret == -1){
+ perror("asprintf");
+ goto fail_find_usplash;
+ }
+
+ /* Check that it refers to a symlink owned by root:root */
+ struct stat exe_stat;
+ ret = lstat(exe_link, &exe_stat);
+ if(ret == -1){
+ if(errno == ENOENT){
+ free(exe_link);
+ continue;
+ }
+ perror("lstat");
+ free(exe_link);
+ goto fail_find_usplash;
+ }
+ if(not S_ISLNK(exe_stat.st_mode)
+ or exe_stat.st_uid != 0
+ or exe_stat.st_gid != 0){
+ free(exe_link);
+ continue;
+ }
+
+ sret = readlink(exe_link, exe_target, sizeof(exe_target));
+ free(exe_link);
+ }
+ /* Compare executable name */
+ if((sret != ((ssize_t)sizeof(exe_target)-1))
+ or (memcmp(usplash_name, exe_target,
+ sizeof(exe_target)-1) != 0)){
+ /* Not it */
+ continue;
+ }
+ /* Found usplash */
+ /* Read and save the command line of usplash in "cmdline" */
+ {
+ /* Open /proc//cmdline */
+ int cl_fd;
+ {
+ char *cmdline_filename;
+ ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
+ proc_ent->d_name);
+ if(ret == -1){
+ perror("asprintf");
+ goto fail_find_usplash;
+ }
+ cl_fd = open(cmdline_filename, O_RDONLY);
+ free(cmdline_filename);
+ if(cl_fd == -1){
+ perror("open");
+ goto fail_find_usplash;
+ }
+ }
+ size_t cmdline_allocated = 0;
+ 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){
+ perror("realloc");
+ close(cl_fd);
+ goto fail_find_usplash;
+ }
+ cmdline = tmp;
+ cmdline_allocated += blocksize;
+ }
+ /* Read data */
+ sret = read(cl_fd, cmdline + cmdline_len,
+ cmdline_allocated - cmdline_len);
+ if(sret == -1){
+ perror("read");
+ close(cl_fd);
+ goto fail_find_usplash;
+ }
+ cmdline_len += (size_t)sret;
+ } while(sret != 0);
+ ret = close(cl_fd);
+ if(ret == -1){
+ perror("close");
+ goto fail_find_usplash;
+ }
+ }
+ /* Close directory */
+ ret = closedir(proc_dir);
+ if(ret == -1){
+ perror("closedir");
+ goto fail_find_usplash;
+ }
+ /* Success */
+ *cmdline_r = cmdline;
+ *cmdline_len_r = cmdline_len;
+ return pid;
+ }
+
+ fail_find_usplash:
+
+ free(cmdline);
+ if(proc_dir != NULL){
+ int e = errno;
+ closedir(proc_dir);
+ errno = e;
+ }
+ return 0;
+}
+
int main(__attribute__((unused))int argc,
__attribute__((unused))char **argv){
int ret = 0;
ssize_t sret;
- bool an_error_occured = false;
+ int fifo_fd = -1;
+ int outfifo_fd = -1;
+ char *buf = NULL;
+ size_t buf_len = 0;
+ pid_t usplash_pid = -1;
+ bool usplash_accessed = false;
- /* Create prompt string */
- char *prompt = NULL;
- {
- 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 EXIT_FAILURE;
- }
+ char *prompt = makeprompt();
+ if(prompt == NULL){
+ goto failure;
}
/* Find usplash process */
- pid_t usplash_pid = 0;
char *cmdline = NULL;
size_t cmdline_len = 0;
- const char usplash_name[] = "/sbin/usplash";
- {
- DIR *proc_dir = opendir("/proc");
- if(proc_dir == NULL){
- free(prompt);
- perror("opendir");
- return EXIT_FAILURE;
- }
- for(struct dirent *proc_ent = readdir(proc_dir);
- proc_ent != NULL;
- proc_ent = readdir(proc_dir)){
- pid_t pid;
- {
- intmax_t tmpmax;
- char *tmp;
- errno = 0;
- tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
- if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
- or tmpmax != (pid_t)tmpmax){
- /* Not a process */
- continue;
- }
- pid = (pid_t)tmpmax;
- }
- /* Find the executable name by doing readlink() on the
- /proc//exe link */
- char exe_target[sizeof(usplash_name)];
- {
- /* create file name string */
- char *exe_link;
- ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
- if(ret == -1){
- perror("asprintf");
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
-
- /* Check that it refers to a symlink owned by root:root */
- struct stat exe_stat;
- ret = lstat(exe_link, &exe_stat);
- if(ret == -1){
- if(errno == ENOENT){
- free(exe_link);
- continue;
- }
- perror("lstat");
- free(exe_link);
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
- if(not S_ISLNK(exe_stat.st_mode)
- or exe_stat.st_uid != 0
- or exe_stat.st_gid != 0){
- free(exe_link);
- continue;
- }
-
- sret = readlink(exe_link, exe_target, sizeof(exe_target));
- free(exe_link);
- }
- if((sret == ((ssize_t)sizeof(exe_target)-1))
- and (memcmp(usplash_name, exe_target,
- sizeof(exe_target)-1) == 0)){
- usplash_pid = pid;
- /* Read and save the command line of usplash in "cmdline" */
- {
- /* Open /proc//cmdline */
- int cl_fd;
- {
- char *cmdline_filename;
- ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
- proc_ent->d_name);
- if(ret == -1){
- perror("asprintf");
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
- cl_fd = open(cmdline_filename, O_RDONLY);
- if(cl_fd == -1){
- perror("open");
- free(cmdline_filename);
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
- free(cmdline_filename);
- }
- size_t cmdline_allocated = 0;
- char *tmp;
- const size_t blocksize = 1024;
- do{
- if(cmdline_len + blocksize > cmdline_allocated){
- tmp = realloc(cmdline, cmdline_allocated + blocksize);
- if(tmp == NULL){
- perror("realloc");
- free(cmdline);
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
- cmdline = tmp;
- cmdline_allocated += blocksize;
- }
- sret = read(cl_fd, cmdline + cmdline_len,
- cmdline_allocated - cmdline_len);
- if(sret == -1){
- perror("read");
- free(cmdline);
- free(prompt);
- closedir(proc_dir);
- return EXIT_FAILURE;
- }
- cmdline_len += (size_t)sret;
- } while(sret != 0);
- close(cl_fd);
- }
- break;
- }
- }
- closedir(proc_dir);
- }
+ usplash_pid = find_usplash(&cmdline, &cmdline_len);
if(usplash_pid == 0){
- free(prompt);
- return EXIT_FAILURE;
+ goto failure;
}
/* Set up the signal handler */
@@ -294,172 +317,231 @@
new_action = { .sa_handler = termination_handler,
.sa_flags = 0 };
sigemptyset(&new_action.sa_mask);
- sigaddset(&new_action.sa_mask, SIGINT);
- sigaddset(&new_action.sa_mask, SIGHUP);
- sigaddset(&new_action.sa_mask, SIGTERM);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
ret = sigaction(SIGINT, NULL, &old_action);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGINT, &new_action, NULL);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
}
ret = sigaction(SIGHUP, NULL, &old_action);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGHUP, &new_action, NULL);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
}
ret = sigaction(SIGTERM, NULL, &old_action);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
if(old_action.sa_handler != SIG_IGN){
ret = sigaction(SIGTERM, &new_action, NULL);
if(ret == -1){
- perror("sigaction");
- free(prompt);
- return EXIT_FAILURE;
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
}
}
}
+ usplash_accessed = true;
/* Write command to FIFO */
- if(not interrupted_by_signal){
- if(not usplash_write("TIMEOUT", "0")
- and (errno != EINTR)){
- perror("usplash_write");
- an_error_occured = true;
- }
- }
- if(not interrupted_by_signal and not an_error_occured){
- if(not usplash_write("INPUTQUIET", prompt)
- and (errno != EINTR)){
- perror("usplash_write");
- an_error_occured = true;
- }
- }
+ if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
free(prompt);
-
- /* This is not really a loop; while() is used to be able to "break"
- out of it; those breaks are marked "Big" */
- while(not interrupted_by_signal and not an_error_occured){
- char *buf = NULL;
- size_t buf_len = 0;
-
- /* Open FIFO */
- int fifo_fd;
- do{
- fifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
- if(fifo_fd == -1){
+ prompt = NULL;
+
+ /* Read reply from usplash */
+ /* Open FIFO */
+ outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
+ if(outfifo_fd == -1){
+ if(errno != EINTR){
+ perror("open");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ /* Read from FIFO */
+ size_t buf_allocated = 0;
+ const size_t blocksize = 1024;
+ do {
+ /* Allocate more space */
+ if(buf_len + blocksize > buf_allocated){
+ char *tmp = realloc(buf, buf_allocated + blocksize);
+ if(tmp == NULL){
if(errno != EINTR){
- perror("open");
- an_error_occured = true;
- break;
- }
- if(interrupted_by_signal){
- break;
- }
- }
- }while(fifo_fd == -1);
- if(interrupted_by_signal or an_error_occured){
- break; /* Big */
- }
-
- /* Read from FIFO */
- size_t buf_allocated = 0;
- const size_t blocksize = 1024;
- do{
- if(buf_len + blocksize > buf_allocated){
- char *tmp = realloc(buf, buf_allocated + blocksize);
- if(tmp == NULL){
perror("realloc");
- an_error_occured = true;
- break;
- }
- buf = tmp;
- buf_allocated += blocksize;
- }
- do{
- sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
- if(sret == -1){
- if(errno != EINTR){
- perror("read");
- an_error_occured = true;
- break;
- }
- if(interrupted_by_signal){
- break;
- }
- }
- }while(sret == -1);
- if(interrupted_by_signal or an_error_occured){
- break;
- }
-
- buf_len += (size_t)sret;
- }while(sret != 0);
- close(fifo_fd);
- if(interrupted_by_signal or an_error_occured){
- break; /* Big */
- }
-
- if(not usplash_write("TIMEOUT", "15")
- and (errno != EINTR)){
- perror("usplash_write");
- an_error_occured = true;
- }
- if(interrupted_by_signal or an_error_occured){
- break; /* Big */
- }
-
- /* Print password to stdout */
- size_t written = 0;
- while(written < buf_len){
- do{
- sret = write(STDOUT_FILENO, buf + written, buf_len - written);
- if(sret == -1){
- if(errno != EINTR){
- perror("write");
- an_error_occured = true;
- break;
- }
- if(interrupted_by_signal){
- break;
- }
- }
- }while(sret == -1);
- if(interrupted_by_signal or an_error_occured){
- break;
- }
- written += (size_t)sret;
- }
- free(buf);
- if(not interrupted_by_signal and not an_error_occured){
- free(cmdline);
- return EXIT_SUCCESS;
- }
- break; /* Big */
- } /* end of non-loop while() */
-
- /* If we got here, an error or interrupt must have happened */
+ }
+ goto failure;
+ }
+ buf = tmp;
+ buf_allocated += blocksize;
+ }
+ sret = read(outfifo_fd, buf + buf_len,
+ buf_allocated - buf_len);
+ if(sret == -1){
+ if(errno != EINTR){
+ perror("read");
+ }
+ TEMP_FAILURE_RETRY(close(outfifo_fd));
+ goto failure;
+ }
+ if(interrupted_by_signal){
+ break;
+ }
+
+ buf_len += (size_t)sret;
+ } while(sret != 0);
+ ret = close(outfifo_fd);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("close");
+ }
+ goto failure;
+ }
+ outfifo_fd = -1;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ ret = close(fifo_fd);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("close");
+ }
+ goto failure;
+ }
+ fifo_fd = -1;
+
+ /* Print password to stdout */
+ size_t written = 0;
+ while(written < buf_len){
+ do {
+ sret = write(STDOUT_FILENO, buf + written, buf_len - written);
+ if(sret == -1){
+ if(errno != EINTR){
+ perror("write");
+ }
+ goto failure;
+ }
+ } while(sret == -1);
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+ written += (size_t)sret;
+ }
+ free(buf);
+ buf = NULL;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ free(cmdline);
+ return EXIT_SUCCESS;
+
+ failure:
+
+ free(buf);
+
+ free(prompt);
+
+ /* If usplash was never accessed, we can stop now */
+ if(not usplash_accessed){
+ return EXIT_FAILURE;
+ }
+
+ /* Close FIFO */
+ if(fifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
+ if(ret == -1 and errno != EINTR){
+ perror("close");
+ }
+ fifo_fd = -1;
+ }
+
+ /* Close output FIFO */
+ if(outfifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
+ if(ret == -1){
+ perror("close");
+ }
+ }
/* Create argc and argv for new usplash*/
int cmdline_argc = 0;
@@ -489,6 +571,7 @@
kill(usplash_pid, SIGKILL);
sleep(1);
}
+
pid_t new_usplash_pid = fork();
if(new_usplash_pid == 0){
/* Child; will become new usplash process */
@@ -522,9 +605,37 @@
free(cmdline);
free(cmdline_argv);
sleep(2);
- if(not usplash_write("PULSATE", NULL)
- and (errno != EINTR)){
- perror("usplash_write");
+ if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ }
+
+ /* Close FIFO (again) */
+ if(fifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
+ if(ret == -1 and errno != EINTR){
+ perror("close");
+ }
+ fifo_fd = -1;
+ }
+
+ if(interrupted_by_signal){
+ struct sigaction signal_action = { .sa_handler = SIG_DFL };
+ sigemptyset(&signal_action.sa_mask);
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &signal_action, NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
}
return EXIT_FAILURE;