=== modified file '.bzrignore'
--- .bzrignore 2010-09-30 06:24:20 +0000
+++ .bzrignore 2012-05-17 01:55:58 +0000
@@ -3,6 +3,7 @@
*.8mandos
confdir
keydir
+statedir
man
plugin-runner
plugins.d/askpass-fifo
=== modified file 'Makefile'
--- Makefile 2012-01-15 21:07:44 +0000
+++ Makefile 2012-05-05 10:52:11 +0000
@@ -264,7 +264,7 @@
./mandos-keygen --dir keydir --force
# Run the server with a local config
-run-server: confdir/mandos.conf confdir/clients.conf
+run-server: confdir/mandos.conf confdir/clients.conf statedir
./mandos --debug --no-dbus --configdir=confdir \
--statedir=statedir $(SERVERARGS)
=== modified file 'TODO'
--- TODO 2012-01-15 20:27:28 +0000
+++ TODO 2012-05-12 19:29:05 +0000
@@ -11,6 +11,7 @@
** TODO [#B] Use getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton()
** TODO [#B] Use getnameinfo(serv=NULL, NI_NUMERICHOST) instead of inet_ntop()
** TODO [#B] Prefer /run/tmp over /tmp, if it exists
+** TODO [#B] Use in_port_t instead of uint16_t for port numbers.
* splashy
** TODO [#B] use scandir(3) instead of readdir(3)
@@ -39,7 +40,6 @@
** TODO [#B] Use openat()
* mandos (server)
-** TODO Document why we ignore sigint
** TODO [#B] Log level :BUGS:
*** TODO /etc/mandos/clients.d/*.conf
Watch this directory and add/remove/update clients?
@@ -54,8 +54,7 @@
** TODO [#B] Global enable/disable flag
** TODO [#B] By-client countdown on number of secrets given
** TODO [#B] Support RFC 3339 time duration syntax
-** More D-Bus methods
-*** NeedsPassword(50) - Timeout, default disapprove
+** D-Bus Client method NeedsPassword(50) - Timeout, default disapprove
+ SetPass(u"gazonk", True) -> Approval, persistent
+ Approve(False) -> Close client connection immediately
** TODO [#C] python-parsedatetime
@@ -78,6 +77,8 @@
and signals ClientAdded and ClientRemoved.
** TODO Save state periodically to recover better from hard shutdowns
** TODO CheckerCompleted method, deprecate CheckedOK
+** TODO Secret Service API?
+ http://standards.freedesktop.org/secret-service/
* mandos.xml
** Add mandos contact info in manual pages
=== modified file 'mandos'
--- mandos 2012-01-15 21:07:44 +0000
+++ mandos 2012-05-12 19:29:05 +0000
@@ -34,6 +34,8 @@
from __future__ import (division, absolute_import, print_function,
unicode_literals)
+from future_builtins import *
+
import SocketServer as socketserver
import socket
import argparse
@@ -65,6 +67,7 @@
import types
import binascii
import tempfile
+import itertools
import dbus
import dbus.service
@@ -175,7 +178,7 @@
def encrypt(self, data, password):
self.gnupg.passphrase = self.password_encode(password)
- with open(os.devnull) as devnull:
+ with open(os.devnull, "w") as devnull:
try:
proc = self.gnupg.run(['--symmetric'],
create_fhs=['stdin', 'stdout'],
@@ -192,12 +195,12 @@
def decrypt(self, data, password):
self.gnupg.passphrase = self.password_encode(password)
- with open(os.devnull) as devnull:
+ with open(os.devnull, "w") as devnull:
try:
proc = self.gnupg.run(['--decrypt'],
create_fhs=['stdin', 'stdout'],
attach_fhs={'stderr': devnull})
- with contextlib.closing(proc.handles['stdin'] ) as f:
+ with contextlib.closing(proc.handles['stdin']) as f:
f.write(data)
with contextlib.closing(proc.handles['stdout']) as f:
decrypted_plaintext = f.read()
@@ -208,7 +211,6 @@
return decrypted_plaintext
-
class AvahiError(Exception):
def __init__(self, value, *args, **kwargs):
self.value = value
@@ -243,6 +245,7 @@
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,
@@ -261,6 +264,7 @@
self.server = None
self.bus = bus
self.entry_group_state_changed_match = None
+
def rename(self):
"""Derived from the Avahi example code"""
if self.rename_count >= self.max_renames:
@@ -276,10 +280,11 @@
try:
self.add()
except dbus.exceptions.DBusException as error:
- logger.critical("DBusException: %s", error)
+ logger.critical("D-Bus Exception", exc_info=error)
self.cleanup()
os._exit(1)
self.rename_count += 1
+
def remove(self):
"""Derived from the Avahi example code"""
if self.entry_group_state_changed_match is not None:
@@ -287,6 +292,7 @@
self.entry_group_state_changed_match = None
if self.group is not None:
self.group.Reset()
+
def add(self):
"""Derived from the Avahi example code"""
self.remove()
@@ -309,6 +315,7 @@
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("Avahi entry group state change: %i", state)
@@ -321,8 +328,9 @@
elif state == avahi.ENTRY_GROUP_FAILURE:
logger.critical("Avahi: Error in group state changed %s",
unicode(error))
- raise AvahiGroupError("State changed: %s"
- % unicode(error))
+ raise AvahiGroupError("State changed: {0!s}"
+ .format(error))
+
def cleanup(self):
"""Derived from the Avahi example code"""
if self.group is not None:
@@ -333,6 +341,7 @@
pass
self.group = None
self.remove()
+
def server_state_changed(self, state, error=None):
"""Derived from the Avahi example code"""
logger.debug("Avahi server state change: %i", state)
@@ -357,6 +366,7 @@
logger.debug("Unknown state: %r", state)
else:
logger.debug("Unknown state: %r: %r", state, error)
+
def activate(self):
"""Derived from the Avahi example code"""
if self.server is None:
@@ -369,22 +379,25 @@
self.server_state_changed)
self.server_state_changed(self.server.GetState())
+
class AvahiServiceToSyslog(AvahiService):
def rename(self):
"""Add the new name to the syslog messages"""
ret = AvahiService.rename(self)
syslogger.setFormatter(logging.Formatter
- ('Mandos (%s) [%%(process)d]:'
- ' %%(levelname)s: %%(message)s'
- % self.name))
+ ('Mandos ({0}) [%(process)d]:'
+ ' %(levelname)s: %(message)s'
+ .format(self.name)))
return ret
+
def timedelta_to_milliseconds(td):
"Convert a datetime.timedelta() to milliseconds"
return ((td.days * 24 * 60 * 60 * 1000)
+ (td.seconds * 1000)
+ (td.microseconds // 1000))
-
+
+
class Client(object):
"""A representation of a client host served by this server.
@@ -429,8 +442,9 @@
"""
runtime_expansions = ("approval_delay", "approval_duration",
- "created", "enabled", "fingerprint",
- "host", "interval", "last_checked_ok",
+ "created", "enabled", "expires",
+ "fingerprint", "host", "interval",
+ "last_approval_request", "last_checked_ok",
"last_enabled", "name", "timeout")
client_defaults = { "timeout": "5m",
"extended_timeout": "15m",
@@ -457,7 +471,7 @@
def approval_delay_milliseconds(self):
return timedelta_to_milliseconds(self.approval_delay)
-
+
@staticmethod
def config_parser(config):
"""Construct a new dict of client settings of this form:
@@ -488,8 +502,8 @@
"rb") as secfile:
client["secret"] = secfile.read()
else:
- raise TypeError("No secret or secfile for section %s"
- % section)
+ raise TypeError("No secret or secfile for section {0}"
+ .format(section))
client["timeout"] = string_to_delta(section["timeout"])
client["extended_timeout"] = string_to_delta(
section["extended_timeout"])
@@ -504,12 +518,8 @@
client["last_checker_status"] = -2
return settings
-
-
+
def __init__(self, settings, name = None):
- """Note: the 'checker' key in 'config' sets the
- 'checker_command' attribute and *not* the 'checker'
- attribute."""
self.name = name
# adding all client settings
for setting, value in settings.iteritems():
@@ -524,7 +534,7 @@
else:
self.last_enabled = None
self.expires = None
-
+
logger.debug("Creating client %r", self.name)
# Uppercase and remove spaces from fingerprint for later
# comparison purposes with return value from the fingerprint()
@@ -532,7 +542,7 @@
logger.debug(" Fingerprint: %s", self.fingerprint)
self.created = settings.get("created",
datetime.datetime.utcnow())
-
+
# attributes specific for this server instance
self.checker = None
self.checker_initiator_tag = None
@@ -566,29 +576,29 @@
if getattr(self, "enabled", False):
# Already enabled
return
- self.send_changedstate()
self.expires = datetime.datetime.utcnow() + self.timeout
self.enabled = True
self.last_enabled = datetime.datetime.utcnow()
self.init_checker()
+ self.send_changedstate()
def disable(self, quiet=True):
"""Disable this client."""
if not getattr(self, "enabled", False):
return False
if not quiet:
- self.send_changedstate()
- if not quiet:
logger.info("Disabling client %s", self.name)
- if getattr(self, "disable_initiator_tag", False):
+ if getattr(self, "disable_initiator_tag", None) is not None:
gobject.source_remove(self.disable_initiator_tag)
self.disable_initiator_tag = None
self.expires = None
- if getattr(self, "checker_initiator_tag", False):
+ if getattr(self, "checker_initiator_tag", None) is not None:
gobject.source_remove(self.checker_initiator_tag)
self.checker_initiator_tag = None
self.stop_checker()
self.enabled = False
+ if not quiet:
+ self.send_changedstate()
# Do not run this again if called by a gobject.timeout_add
return False
@@ -598,10 +608,14 @@
def init_checker(self):
# Schedule a new checker to be started an 'interval' from now,
# and every interval from then on.
+ if self.checker_initiator_tag is not None:
+ gobject.source_remove(self.checker_initiator_tag)
self.checker_initiator_tag = (gobject.timeout_add
(self.interval_milliseconds(),
self.start_checker))
# Schedule a disable() when 'timeout' has passed
+ if self.disable_initiator_tag is not None:
+ gobject.source_remove(self.disable_initiator_tag)
self.disable_initiator_tag = (gobject.timeout_add
(self.timeout_milliseconds(),
self.disable))
@@ -638,6 +652,7 @@
timeout = self.timeout
if self.disable_initiator_tag is not None:
gobject.source_remove(self.disable_initiator_tag)
+ self.disable_initiator_tag = None
if getattr(self, "enabled", False):
self.disable_initiator_tag = (gobject.timeout_add
(timedelta_to_milliseconds
@@ -653,10 +668,10 @@
If a checker already exists, leave it running and do
nothing."""
# The reason for not killing a running checker is that if we
- # did that, then if a checker (for some reason) started
- # running slowly and taking more than 'interval' time, the
- # client would inevitably timeout, since no checker would get
- # a chance to run to completion. If we instead leave running
+ # did that, and if a checker (for some reason) started running
+ # slowly and taking more than 'interval' time, then the client
+ # would inevitably timeout, since no checker would get a
+ # chance to run to completion. If we instead leave running
# checkers alone, the checker would have to take more time
# than 'timeout' for the client to be disabled, which is as it
# should be.
@@ -676,25 +691,17 @@
self.current_checker_command)
# Start a new checker if needed
if self.checker is None:
+ # Escape attributes for the shell
+ escaped_attrs = dict(
+ (attr, re.escape(unicode(getattr(self, attr))))
+ for attr in
+ self.runtime_expansions)
try:
- # In case checker_command has exactly one % operator
- command = self.checker_command % self.host
- except TypeError:
- # Escape attributes for the shell
- escaped_attrs = dict(
- (attr,
- re.escape(unicode(str(getattr(self, attr, "")),
- errors=
- 'replace')))
- for attr in
- self.runtime_expansions)
-
- try:
- command = self.checker_command % escaped_attrs
- except TypeError as error:
- logger.error('Could not format string "%s":'
- ' %s', self.checker_command, error)
- return True # Try again later
+ command = self.checker_command % escaped_attrs
+ except TypeError as error:
+ logger.error('Could not format string "%s"',
+ self.checker_command, exc_info=error)
+ return True # Try again later
self.current_checker_command = command
try:
logger.info("Starting checker %r for %s",
@@ -706,19 +713,19 @@
self.checker = subprocess.Popen(command,
close_fds=True,
shell=True, cwd="/")
- self.checker_callback_tag = (gobject.child_watch_add
- (self.checker.pid,
- self.checker_callback,
- data=command))
- # The checker may have completed before the gobject
- # watch was added. Check for this.
- pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
- if pid:
- gobject.source_remove(self.checker_callback_tag)
- self.checker_callback(pid, status, command)
except OSError as error:
- logger.error("Failed to start subprocess: %s",
- error)
+ logger.error("Failed to start subprocess",
+ exc_info=error)
+ self.checker_callback_tag = (gobject.child_watch_add
+ (self.checker.pid,
+ self.checker_callback,
+ data=command))
+ # The checker may have completed before the gobject
+ # watch was added. Check for this.
+ pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
+ if pid:
+ gobject.source_remove(self.checker_callback_tag)
+ self.checker_callback(pid, status, command)
# Re-run this periodically if run by gobject.timeout_add
return True
@@ -731,10 +738,10 @@
return
logger.debug("Stopping checker for %(name)s", vars(self))
try:
- os.kill(self.checker.pid, signal.SIGTERM)
+ self.checker.terminate()
#time.sleep(0.5)
#if self.checker.poll() is None:
- # os.kill(self.checker.pid, signal.SIGKILL)
+ # self.checker.kill()
except OSError as error:
if error.errno != errno.ESRCH: # No such process
raise
@@ -757,7 +764,7 @@
# "Set" method, so we fail early here:
if byte_arrays and signature != "ay":
raise ValueError("Byte arrays not supported for non-'ay'"
- " signature %r" % signature)
+ " signature {0!r}".format(signature))
def decorator(func):
func._dbus_is_property = True
func._dbus_interface = dbus_interface
@@ -771,6 +778,43 @@
return decorator
+def dbus_interface_annotations(dbus_interface):
+ """Decorator for marking functions returning interface annotations
+
+ Usage:
+
+ @dbus_interface_annotations("org.example.Interface")
+ def _foo(self): # Function name does not matter
+ return {"org.freedesktop.DBus.Deprecated": "true",
+ "org.freedesktop.DBus.Property.EmitsChangedSignal":
+ "false"}
+ """
+ def decorator(func):
+ func._dbus_is_interface = True
+ func._dbus_interface = dbus_interface
+ func._dbus_name = dbus_interface
+ return func
+ return decorator
+
+
+def dbus_annotations(annotations):
+ """Decorator to annotate D-Bus methods, signals or properties
+ Usage:
+
+ @dbus_service_property("org.example.Interface", signature="b",
+ access="r")
+ @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
+ "org.freedesktop.DBus.Property."
+ "EmitsChangedSignal": "false"})
+ def Property_dbus_property(self):
+ return dbus.Boolean(False)
+ """
+ def decorator(func):
+ func._dbus_annotations = annotations
+ return func
+ return decorator
+
+
class DBusPropertyException(dbus.exceptions.DBusException):
"""A base class for D-Bus property-related exceptions
"""
@@ -799,16 +843,25 @@
"""
@staticmethod
- def _is_dbus_property(obj):
- return getattr(obj, "_dbus_is_property", False)
+ def _is_dbus_thing(thing):
+ """Returns a function testing if an attribute is a D-Bus thing
+
+ If called like _is_dbus_thing("method") it returns a function
+ suitable for use as predicate to inspect.getmembers().
+ """
+ return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
+ False)
- def _get_all_dbus_properties(self):
+ def _get_all_dbus_things(self, thing):
"""Returns a generator of (name, attribute) pairs
"""
- return ((prop.__get__(self)._dbus_name, prop.__get__(self))
+ return ((getattr(athing.__get__(self), "_dbus_name",
+ name),
+ athing.__get__(self))
for cls in self.__class__.__mro__
- for name, prop in
- inspect.getmembers(cls, self._is_dbus_property))
+ for name, athing in
+ inspect.getmembers(cls,
+ self._is_dbus_thing(thing)))
def _get_dbus_property(self, interface_name, property_name):
"""Returns a bound method if one exists which is a D-Bus
@@ -816,7 +869,8 @@
"""
for cls in self.__class__.__mro__:
for name, value in (inspect.getmembers
- (cls, self._is_dbus_property)):
+ (cls,
+ self._is_dbus_thing("property"))):
if (value._dbus_name == property_name
and value._dbus_interface == interface_name):
return value.__get__(self)
@@ -864,7 +918,7 @@
Note: Will not include properties with access="write".
"""
properties = {}
- for name, prop in self._get_all_dbus_properties():
+ for name, prop in self._get_all_dbus_things("property"):
if (interface_name
and interface_name != prop._dbus_interface):
# Interface non-empty but did not match
@@ -885,7 +939,9 @@
path_keyword='object_path',
connection_keyword='connection')
def Introspect(self, object_path, connection):
- """Standard D-Bus method, overloaded to insert property tags.
+ """Overloading of standard D-Bus method.
+
+ Inserts property tags and interface annotation tags.
"""
xmlstring = dbus.service.Object.Introspect(self, object_path,
connection)
@@ -898,12 +954,44 @@
e.setAttribute("access", prop._dbus_access)
return e
for if_tag in document.getElementsByTagName("interface"):
+ # Add property tags
for tag in (make_tag(document, name, prop)
for name, prop
- in self._get_all_dbus_properties()
+ in self._get_all_dbus_things("property")
if prop._dbus_interface
== if_tag.getAttribute("name")):
if_tag.appendChild(tag)
+ # Add annotation tags
+ for typ in ("method", "signal", "property"):
+ for tag in if_tag.getElementsByTagName(typ):
+ annots = dict()
+ for name, prop in (self.
+ _get_all_dbus_things(typ)):
+ if (name == tag.getAttribute("name")
+ and prop._dbus_interface
+ == if_tag.getAttribute("name")):
+ annots.update(getattr
+ (prop,
+ "_dbus_annotations",
+ {}))
+ for name, value in annots.iteritems():
+ ann_tag = document.createElement(
+ "annotation")
+ ann_tag.setAttribute("name", name)
+ ann_tag.setAttribute("value", value)
+ tag.appendChild(ann_tag)
+ # Add interface annotation tags
+ for annotation, value in dict(
+ itertools.chain.from_iterable(
+ annotations().iteritems()
+ for name, annotations in
+ self._get_all_dbus_things("interface")
+ if name == if_tag.getAttribute("name")
+ )).iteritems():
+ ann_tag = document.createElement("annotation")
+ ann_tag.setAttribute("name", annotation)
+ ann_tag.setAttribute("value", value)
+ if_tag.appendChild(ann_tag)
# Add the names to the return values for the
# "org.freedesktop.DBus.Properties" methods
if (if_tag.getAttribute("name")
@@ -924,7 +1012,7 @@
except (AttributeError, xml.dom.DOMException,
xml.parsers.expat.ExpatError) as error:
logger.error("Failed to override Introspection method",
- error)
+ exc_info=error)
return xmlstring
@@ -936,29 +1024,48 @@
variant_level=variant_level)
-class AlternateDBusNamesMetaclass(DBusObjectWithProperties
- .__metaclass__):
- """Applied to an empty subclass of a D-Bus object, this metaclass
- will add additional D-Bus attributes matching a certain pattern.
+def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
+ """A class decorator; applied to a subclass of
+ dbus.service.Object, it will add alternate D-Bus attributes with
+ interface names according to the "alt_interface_names" mapping.
+ Usage:
+
+ @alternate_dbus_names({"org.example.Interface":
+ "net.example.AlternateInterface"})
+ class SampleDBusObject(dbus.service.Object):
+ @dbus.service.method("org.example.Interface")
+ def SampleDBusMethod():
+ pass
+
+ The above "SampleDBusMethod" on "SampleDBusObject" will be
+ reachable via two interfaces: "org.example.Interface" and
+ "net.example.AlternateInterface", the latter of which will have
+ its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
+ "true", unless "deprecate" is passed with a False value.
+
+ This works for methods and signals, and also for D-Bus properties
+ (from DBusObjectWithProperties) and interfaces (from the
+ dbus_interface_annotations decorator).
"""
- def __new__(mcs, name, bases, attr):
- # Go through all the base classes which could have D-Bus
- # methods, signals, or properties in them
- for base in (b for b in bases
- if issubclass(b, dbus.service.Object)):
- # Go though all attributes of the base class
- for attrname, attribute in inspect.getmembers(base):
+ def wrapper(cls):
+ for orig_interface_name, alt_interface_name in (
+ alt_interface_names.iteritems()):
+ attr = {}
+ interface_names = set()
+ # Go though all attributes of the class
+ for attrname, attribute in inspect.getmembers(cls):
# Ignore non-D-Bus attributes, and D-Bus attributes
# with the wrong interface name
if (not hasattr(attribute, "_dbus_interface")
or not attribute._dbus_interface
- .startswith("se.recompile.Mandos")):
+ .startswith(orig_interface_name)):
continue
# Create an alternate D-Bus interface name based on
# the current name
alt_interface = (attribute._dbus_interface
- .replace("se.recompile.Mandos",
- "se.bsnet.fukt.Mandos"))
+ .replace(orig_interface_name,
+ alt_interface_name))
+ interface_names.add(alt_interface)
# Is this a D-Bus signal?
if getattr(attribute, "_dbus_is_signal", False):
# Extract the original non-method function by
@@ -979,9 +1086,16 @@
nonmethod_func.func_name,
nonmethod_func.func_defaults,
nonmethod_func.func_closure)))
+ # Copy annotations, if any
+ try:
+ new_function._dbus_annotations = (
+ dict(attribute._dbus_annotations))
+ except AttributeError:
+ pass
# Define a creator of a function to call both the
- # old and new functions, so both the old and new
- # signals gets sent when the function is called
+ # original and alternate functions, so both the
+ # original and alternate signals gets sent when
+ # the function is called
def fixscope(func1, func2):
"""This function is a scope container to pass
func1 and func2 to the "call_both" function
@@ -994,8 +1108,7 @@
return call_both
# Create the "call_both" function and add it to
# the class
- attr[attrname] = fixscope(attribute,
- new_function)
+ attr[attrname] = fixscope(attribute, new_function)
# Is this a D-Bus method?
elif getattr(attribute, "_dbus_is_method", False):
# Create a new, but exactly alike, function
@@ -1012,6 +1125,12 @@
attribute.func_name,
attribute.func_defaults,
attribute.func_closure)))
+ # Copy annotations, if any
+ try:
+ attr[attrname]._dbus_annotations = (
+ dict(attribute._dbus_annotations))
+ except AttributeError:
+ pass
# Is this a D-Bus property?
elif getattr(attribute, "_dbus_is_property", False):
# Create a new, but exactly alike, function
@@ -1031,9 +1150,51 @@
attribute.func_name,
attribute.func_defaults,
attribute.func_closure)))
- return type.__new__(mcs, name, bases, attr)
-
-
+ # Copy annotations, if any
+ try:
+ attr[attrname]._dbus_annotations = (
+ dict(attribute._dbus_annotations))
+ except AttributeError:
+ pass
+ # Is this a D-Bus interface?
+ elif getattr(attribute, "_dbus_is_interface", False):
+ # Create a new, but exactly alike, function
+ # object. Decorate it to be a new D-Bus interface
+ # with the alternate D-Bus interface name. Add it
+ # to the class.
+ attr[attrname] = (dbus_interface_annotations
+ (alt_interface)
+ (types.FunctionType
+ (attribute.func_code,
+ attribute.func_globals,
+ attribute.func_name,
+ attribute.func_defaults,
+ attribute.func_closure)))
+ if deprecate:
+ # Deprecate all alternate interfaces
+ iname="_AlternateDBusNames_interface_annotation{0}"
+ for interface_name in interface_names:
+ @dbus_interface_annotations(interface_name)
+ def func(self):
+ return { "org.freedesktop.DBus.Deprecated":
+ "true" }
+ # Find an unused name
+ for aname in (iname.format(i)
+ for i in itertools.count()):
+ if aname not in attr:
+ attr[aname] = func
+ break
+ if interface_names:
+ # Replace the class with a new subclass of it with
+ # methods, signals, etc. as created above.
+ cls = type(b"{0}Alternate".format(cls.__name__),
+ (cls,), attr)
+ return cls
+ return wrapper
+
+
+@alternate_dbus_interfaces({"se.recompile.Mandos":
+ "se.bsnet.fukt.Mandos"})
class ClientDBus(Client, DBusObjectWithProperties):
"""A Client class using D-Bus
@@ -1059,7 +1220,7 @@
("/clients/" + client_object_name))
DBusObjectWithProperties.__init__(self, self.bus,
self.dbus_object_path)
-
+
def notifychangeproperty(transform_func,
dbus_name, type_func=lambda x: x,
variant_level=1):
@@ -1088,7 +1249,6 @@
return property(lambda self: getattr(self, attrname), setter)
-
expires = notifychangeproperty(datetime_to_dbus, "Expires")
approvals_pending = notifychangeproperty(dbus.Boolean,
"ApprovalPending",
@@ -1176,16 +1336,22 @@
return False
def approve(self, value=True):
- self.send_changedstate()
self.approved = value
gobject.timeout_add(timedelta_to_milliseconds
(self.approval_duration),
self._reset_approved)
-
+ self.send_changedstate()
## D-Bus methods, signals & properties
_interface = "se.recompile.Mandos.Client"
+ ## Interfaces
+
+ @dbus_interface_annotations(_interface)
+ def _foo(self):
+ return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
+ "false"}
+
## Signals
# CheckerCompleted - signal
@@ -1364,26 +1530,24 @@
def Timeout_dbus_property(self, value=None):
if value is None: # get
return dbus.UInt64(self.timeout_milliseconds())
+ old_timeout = self.timeout
self.timeout = datetime.timedelta(0, 0, 0, value)
- # Reschedule timeout
+ # Reschedule disabling
if self.enabled:
now = datetime.datetime.utcnow()
- time_to_die = timedelta_to_milliseconds(
- (self.last_checked_ok + self.timeout) - now)
- if time_to_die <= 0:
+ self.expires += self.timeout - old_timeout
+ if self.expires <= now:
# The timeout has passed
self.disable()
else:
- self.expires = (now +
- datetime.timedelta(milliseconds =
- time_to_die))
if (getattr(self, "disable_initiator_tag", None)
is None):
return
gobject.source_remove(self.disable_initiator_tag)
- self.disable_initiator_tag = (gobject.timeout_add
- (time_to_die,
- self.disable))
+ self.disable_initiator_tag = (
+ gobject.timeout_add(
+ timedelta_to_milliseconds(self.expires - now),
+ self.disable))
# ExtendedTimeout - property
@dbus_service_property(_interface, signature="t",
@@ -1468,10 +1632,6 @@
self._pipe.send(('setattr', name, value))
-class ClientDBusTransitional(ClientDBus):
- __metaclass__ = AlternateDBusNamesMetaclass
-
-
class ClientHandler(socketserver.BaseRequestHandler, object):
"""A class to handle client connections.
@@ -1581,9 +1741,9 @@
#wait until timeout or approved
time = datetime.datetime.now()
client.changedstate.acquire()
- (client.changedstate.wait
- (float(client.timedelta_to_milliseconds(delay)
- / 1000)))
+ client.changedstate.wait(
+ float(timedelta_to_milliseconds(delay)
+ / 1000))
client.changedstate.release()
time2 = datetime.datetime.now()
if (time2 - time) >= delay:
@@ -1605,7 +1765,8 @@
try:
sent = session.send(client.secret[sent_size:])
except gnutls.errors.GNUTLSError as error:
- logger.warning("gnutls send failed")
+ logger.warning("gnutls send failed",
+ exc_info=error)
return
logger.debug("Sent: %d, remaining: %d",
sent, len(client.secret)
@@ -1625,7 +1786,8 @@
try:
session.bye()
except gnutls.errors.GNUTLSError as error:
- logger.warning("GnuTLS bye failed")
+ logger.warning("GnuTLS bye failed",
+ exc_info=error)
@staticmethod
def peer_certificate(session):
@@ -1703,8 +1865,7 @@
def process_request(self, request, address):
"""Start a new process to process the request."""
proc = multiprocessing.Process(target = self.sub_process_main,
- args = (request,
- address))
+ args = (request, address))
proc.start()
return proc
@@ -1760,14 +1921,18 @@
str(self.interface
+ '\0'))
except socket.error as error:
- if error[0] == errno.EPERM:
+ if error.errno == errno.EPERM:
logger.error("No permission to"
" bind to interface %s",
self.interface)
- elif error[0] == errno.ENOPROTOOPT:
+ elif error.errno == errno.ENOPROTOOPT:
logger.error("SO_BINDTODEVICE not available;"
" cannot bind to interface %s",
self.interface)
+ elif error.errno == errno.ENODEV:
+ logger.error("Interface %s does not"
+ " exist, cannot bind",
+ self.interface)
else:
raise
# Only bind(2) the socket if we really need to.
@@ -1832,22 +1997,8 @@
def handle_ipc(self, source, condition, parent_pipe=None,
proc = None, client_object=None):
- condition_names = {
- gobject.IO_IN: "IN", # There is data to read.
- gobject.IO_OUT: "OUT", # Data can be written (without
- # blocking).
- gobject.IO_PRI: "PRI", # There is urgent data to read.
- gobject.IO_ERR: "ERR", # Error condition.
- gobject.IO_HUP: "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)
# error, or the other end of multiprocessing.Pipe has closed
- if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
+ if condition & (gobject.IO_ERR | gobject.IO_HUP):
# Wait for other process to exit
proc.join()
return False
@@ -1943,7 +2094,8 @@
elif suffix == "w":
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
else:
- raise ValueError("Unknown suffix %r" % suffix)
+ raise ValueError("Unknown suffix {0!r}"
+ .format(suffix))
except (ValueError, IndexError) as e:
raise ValueError(*(e.args))
timevalue += delta
@@ -1963,11 +2115,11 @@
sys.exit()
if not noclose:
# Close all standard open file descriptors
- null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
+ null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
if not stat.S_ISCHR(os.fstat(null).st_mode):
raise OSError(errno.ENODEV,
- "%s not a character device"
- % os.path.devnull)
+ "{0} not a character device"
+ .format(os.devnull))
os.dup2(null, sys.stdin.fileno())
os.dup2(null, sys.stdout.fileno())
os.dup2(null, sys.stderr.fileno())
@@ -1982,7 +2134,7 @@
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version", action="version",
- version = "%%(prog)s %s" % version,
+ version = "%(prog)s {0}".format(version),
help="show version number and exit")
parser.add_argument("-i", "--interface", metavar="IF",
help="Bind to interface IF")
@@ -2091,9 +2243,10 @@
if server_settings["servicename"] != "Mandos":
syslogger.setFormatter(logging.Formatter
- ('Mandos (%s) [%%(process)d]:'
- ' %%(levelname)s: %%(message)s'
- % server_settings["servicename"]))
+ ('Mandos ({0}) [%(process)d]:'
+ ' %(levelname)s: %(message)s'
+ .format(server_settings
+ ["servicename"])))
# Parse config file with clients
client_config = configparser.SafeConfigParser(Client
@@ -2117,28 +2270,25 @@
pidfilename = "/var/run/mandos.pid"
try:
pidfile = open(pidfilename, "w")
- except IOError:
- logger.error("Could not open file %r", pidfilename)
+ except IOError as e:
+ logger.error("Could not open file %r", pidfilename,
+ exc_info=e)
- try:
- uid = pwd.getpwnam("_mandos").pw_uid
- gid = pwd.getpwnam("_mandos").pw_gid
- except KeyError:
+ for name in ("_mandos", "mandos", "nobody"):
try:
- uid = pwd.getpwnam("mandos").pw_uid
- gid = pwd.getpwnam("mandos").pw_gid
+ uid = pwd.getpwnam(name).pw_uid
+ gid = pwd.getpwnam(name).pw_gid
+ break
except KeyError:
- try:
- uid = pwd.getpwnam("nobody").pw_uid
- gid = pwd.getpwnam("nobody").pw_gid
- except KeyError:
- uid = 65534
- gid = 65534
+ continue
+ else:
+ uid = 65534
+ gid = 65534
try:
os.setgid(gid)
os.setuid(uid)
except OSError as error:
- if error[0] != errno.EPERM:
+ if error.errno != errno.EPERM:
raise error
if debug:
@@ -2156,7 +2306,7 @@
.gnutls_global_set_log_function(debug_gnutls))
# Redirect stdin so all checkers get /dev/null
- null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
+ null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
os.dup2(null, sys.stdin.fileno())
if null > 2:
os.close(null)
@@ -2170,7 +2320,7 @@
global main_loop
# From the Avahi example code
- DBusGMainLoop(set_as_default=True )
+ DBusGMainLoop(set_as_default=True)
main_loop = gobject.MainLoop()
bus = dbus.SystemBus()
# End of Avahi example code
@@ -2182,7 +2332,7 @@
("se.bsnet.fukt.Mandos", bus,
do_not_queue=True))
except dbus.exceptions.NameExistsException as e:
- logger.error(unicode(e) + ", disabling D-Bus")
+ logger.error("Disabling D-Bus:", exc_info=e)
use_dbus = False
server_settings["use_dbus"] = False
tcp_server.use_dbus = False
@@ -2200,8 +2350,7 @@
client_class = Client
if use_dbus:
- client_class = functools.partial(ClientDBusTransitional,
- bus = bus)
+ client_class = functools.partial(ClientDBus, bus = bus)
client_settings = Client.config_parser(client_config)
old_client_settings = {}
@@ -2215,13 +2364,16 @@
(stored_state))
os.remove(stored_state_path)
except IOError as e:
- logger.warning("Could not load persistent state: {0}"
- .format(e))
- if e.errno != errno.ENOENT:
+ if e.errno == errno.ENOENT:
+ logger.warning("Could not load persistent state: {0}"
+ .format(os.strerror(e.errno)))
+ else:
+ logger.critical("Could not load persistent state:",
+ exc_info=e)
raise
except EOFError as e:
logger.warning("Could not load persistent state: "
- "EOFError: {0}".format(e))
+ "EOFError:", exc_info=e)
with PGPEngine() as pgp:
for client_name, client in clients_data.iteritems():
@@ -2280,7 +2432,6 @@
.format(client_name))
client["secret"] = (
client_settings[client_name]["secret"])
-
# Add/remove clients based on new changes made to config
for client_name in (set(old_client_settings)
@@ -2289,7 +2440,7 @@
for client_name in (set(client_settings)
- set(old_client_settings)):
clients_data[client_name] = client_settings[client_name]
-
+
# Create all client objects
for client_name, client in clients_data.iteritems():
tcp_server.clients[client_name] = client_class(
@@ -2297,7 +2448,7 @@
if not tcp_server.clients:
logger.warning("No clients defined")
-
+
if not debug:
try:
with pidfile:
@@ -2311,18 +2462,25 @@
# "pidfile" was never created
pass
del pidfilename
- signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
if use_dbus:
- class MandosDBusService(dbus.service.Object):
+ @alternate_dbus_interfaces({"se.recompile.Mandos":
+ "se.bsnet.fukt.Mandos"})
+ class MandosDBusService(DBusObjectWithProperties):
"""A D-Bus proxy object"""
def __init__(self):
dbus.service.Object.__init__(self, bus, "/")
_interface = "se.recompile.Mandos"
+ @dbus_interface_annotations(_interface)
+ def _foo(self):
+ return { "org.freedesktop.DBus.Property"
+ ".EmitsChangedSignal":
+ "false"}
+
@dbus.service.signal(_interface, signature="o")
def ClientAdded(self, objpath):
"D-Bus signal"
@@ -2370,9 +2528,7 @@
del _interface
- class MandosDBusServiceTransitional(MandosDBusService):
- __metaclass__ = AlternateDBusNamesMetaclass
- mandos_dbus_service = MandosDBusServiceTransitional()
+ mandos_dbus_service = MandosDBusService()
def cleanup():
"Cleanup function; run on exit"
@@ -2411,23 +2567,25 @@
del client_settings[client.name]["secret"]
try:
- tempfd, tempname = tempfile.mkstemp(suffix=".pickle",
- prefix="clients-",
- dir=os.path.dirname
- (stored_state_path))
- with os.fdopen(tempfd, "wb") as stored_state:
+ with (tempfile.NamedTemporaryFile
+ (mode='wb', suffix=".pickle", prefix='clients-',
+ dir=os.path.dirname(stored_state_path),
+ delete=False)) as stored_state:
pickle.dump((clients, client_settings), stored_state)
+ tempname=stored_state.name
os.rename(tempname, stored_state_path)
except (IOError, OSError) as e:
- logger.warning("Could not save persistent state: {0}"
- .format(e))
if not debug:
try:
os.remove(tempname)
except NameError:
pass
- if e.errno not in set((errno.ENOENT, errno.EACCES,
- errno.EEXIST)):
+ if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
+ logger.warning("Could not save persistent state: {0}"
+ .format(os.strerror(e.errno)))
+ else:
+ logger.warning("Could not save persistent state:",
+ exc_info=e)
raise e
# Delete all clients, and settings from config
@@ -2461,11 +2619,11 @@
service.port = tcp_server.socket.getsockname()[1]
if use_ipv6:
logger.info("Now listening on address %r, port %d,"
- " flowinfo %d, scope_id %d"
- % tcp_server.socket.getsockname())
+ " flowinfo %d, scope_id %d",
+ *tcp_server.socket.getsockname())
else: # IPv4
- logger.info("Now listening on address %r, port %d"
- % tcp_server.socket.getsockname())
+ logger.info("Now listening on address %r, port %d",
+ *tcp_server.socket.getsockname())
#service.interface = tcp_server.socket.getsockname()[3]
@@ -2474,7 +2632,7 @@
try:
service.activate()
except dbus.exceptions.DBusException as error:
- logger.critical("DBusException: %s", error)
+ logger.critical("D-Bus Exception", exc_info=error)
cleanup()
sys.exit(1)
# End of Avahi example code
@@ -2487,7 +2645,7 @@
logger.debug("Starting main loop")
main_loop.run()
except AvahiError as error:
- logger.critical("AvahiError: %s", error)
+ logger.critical("Avahi Error", exc_info=error)
cleanup()
sys.exit(1)
except KeyboardInterrupt:
=== modified file 'mandos-clients.conf.xml'
--- mandos-clients.conf.xml 2012-01-01 04:02:00 +0000
+++ mandos-clients.conf.xml 2012-05-12 19:29:05 +0000
@@ -3,7 +3,7 @@
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
/etc/mandos/clients.conf">
-
+
%common;
]>
@@ -409,6 +409,7 @@
approval_duration
,
created
,
enabled
,
+ expires
,
fingerprint
,
host
,
interval
,
=== modified file 'mandos-ctl'
--- mandos-ctl 2012-01-15 21:07:44 +0000
+++ mandos-ctl 2012-05-07 19:13:15 +0000
@@ -17,7 +17,8 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
+# along with this program. If not, see
+# .
#
# Contact the authors at .
#
@@ -25,6 +26,8 @@
from __future__ import (division, absolute_import, print_function,
unicode_literals)
+from future_builtins import *
+
import sys
import dbus
import argparse
@@ -70,12 +73,12 @@
def milliseconds_to_string(ms):
td = datetime.timedelta(0, 0, 0, ms)
- return ("%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d"
- % { "days": "%dT" % td.days if td.days else "",
- "hours": td.seconds // 3600,
- "minutes": (td.seconds % 3600) // 60,
- "seconds": td.seconds % 60,
- })
+ return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
+ .format(days = "{0}T".format(td.days) if td.days else "",
+ hours = td.seconds // 3600,
+ minutes = (td.seconds % 3600) // 60,
+ seconds = td.seconds % 60,
+ ))
def string_to_delta(interval):
"""Parse a string and return a datetime.timedelta
@@ -116,23 +119,25 @@
if type(value) is dbus.Boolean:
return "Yes" if value else "No"
if keyword in ("Timeout", "Interval", "ApprovalDelay",
- "ApprovalDuration"):
+ "ApprovalDuration", "ExtendedTimeout"):
return milliseconds_to_string(value)
return unicode(value)
# Create format string to print table rows
- format_string = " ".join("%%-%ds" %
- max(len(tablewords[key]),
- max(len(valuetostring(client[key],
- key))
- for client in
- clients))
- for key in keywords)
+ format_string = " ".join("{{{key}:{width}}}".format(
+ width = max(len(tablewords[key]),
+ max(len(valuetostring(client[key],
+ key))
+ for client in
+ clients)),
+ key = key) for key in keywords)
# Print header line
- print(format_string % tuple(tablewords[key] for key in keywords))
+ print(format_string.format(**tablewords))
for client in clients:
- print(format_string % tuple(valuetostring(client[key], key)
- for key in keywords))
+ print(format_string.format(**dict((key,
+ valuetostring(client[key],
+ key))
+ for key in keywords)))
def has_actions(options):
return any((options.enable,
@@ -157,7 +162,7 @@
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--version", action="version",
- version = "%%prog %s" % version,
+ version = "%(prog)s {0}".format(version),
help="show version number and exit")
parser.add_argument("-a", "--all", action="store_true",
help="Select all clients")
@@ -205,7 +210,7 @@
parser.add_argument("client", nargs="*", help="Client name")
options = parser.parse_args()
- if has_actions(options) and not options.client and not options.all:
+ if has_actions(options) and not (options.client or options.all):
parser.error("Options require clients names or --all.")
if options.verbose and has_actions(options):
parser.error("--verbose can only be used alone or with"
@@ -256,8 +261,8 @@
clients[client_objc] = client
break
else:
- print("Client not found on server: %r" % name,
- file=sys.stderr)
+ print("Client not found on server: {0!r}"
+ .format(name), file=sys.stderr)
sys.exit(1)
if not has_actions(options) and clients:
@@ -277,18 +282,28 @@
else:
# Process each client in the list by all selected options
for client in clients:
+ def set_client_prop(prop, value):
+ """Set a Client D-Bus property"""
+ client.Set(client_interface, prop, value,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ def set_client_prop_ms(prop, value):
+ """Set a Client D-Bus property, converted
+ from a string to milliseconds."""
+ set_client_prop(prop,
+ timedelta_to_milliseconds
+ (string_to_delta(value)))
if options.remove:
mandos_serv.RemoveClient(client.__dbus_object_path__)
if options.enable:
- client.Enable(dbus_interface=client_interface)
+ set_client_prop("Enabled", dbus.Boolean(True))
if options.disable:
- client.Disable(dbus_interface=client_interface)
+ set_client_prop("Enabled", dbus.Boolean(False))
if options.bump_timeout:
- client.CheckedOK(dbus_interface=client_interface)
+ set_client_prop("LastCheckedOK", "")
if options.start_checker:
- client.StartChecker(dbus_interface=client_interface)
+ set_client_prop("CheckerRunning", dbus.Boolean(True))
if options.stop_checker:
- client.StopChecker(dbus_interface=client_interface)
+ set_client_prop("CheckerRunning", dbus.Boolean(False))
if options.is_enabled:
sys.exit(0 if client.Get(client_interface,
"Enabled",
@@ -296,48 +311,29 @@
dbus.PROPERTIES_IFACE)
else 1)
if options.checker is not None:
- client.Set(client_interface, "Checker",
- options.checker,
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop("Checker", options.checker)
if options.host is not None:
- client.Set(client_interface, "Host", options.host,
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop("Host", options.host)
if options.interval is not None:
- client.Set(client_interface, "Interval",
- timedelta_to_milliseconds
- (string_to_delta(options.interval)),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop_ms("Interval", options.interval)
if options.approval_delay is not None:
- client.Set(client_interface, "ApprovalDelay",
- timedelta_to_milliseconds
- (string_to_delta(options.
- approval_delay)),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop_ms("ApprovalDelay",
+ options.approval_delay)
if options.approval_duration is not None:
- client.Set(client_interface, "ApprovalDuration",
- timedelta_to_milliseconds
- (string_to_delta(options.
- approval_duration)),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop_ms("ApprovalDuration",
+ options.approval_duration)
if options.timeout is not None:
- client.Set(client_interface, "Timeout",
- timedelta_to_milliseconds
- (string_to_delta(options.timeout)),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop_ms("Timeout", options.timeout)
if options.extended_timeout is not None:
- client.Set(client_interface, "ExtendedTimeout",
- timedelta_to_milliseconds
- (string_to_delta(options.extended_timeout)),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop_ms("ExtendedTimeout",
+ options.extended_timeout)
if options.secret is not None:
- client.Set(client_interface, "Secret",
- dbus.ByteArray(options.secret.read()),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop("Secret",
+ dbus.ByteArray(options.secret.read()))
if options.approved_by_default is not None:
- client.Set(client_interface, "ApprovedByDefault",
- dbus.Boolean(options
- .approved_by_default),
- dbus_interface=dbus.PROPERTIES_IFACE)
+ set_client_prop("ApprovedByDefault",
+ dbus.Boolean(options
+ .approved_by_default))
if options.approve:
client.Approve(dbus.Boolean(True),
dbus_interface=client_interface)
=== modified file 'mandos-monitor'
--- mandos-monitor 2012-01-15 21:07:44 +0000
+++ mandos-monitor 2012-05-12 15:45:57 +0000
@@ -17,7 +17,8 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
+# along with this program. If not, see
+# .
#
# Contact the authors at .
#
@@ -25,6 +26,8 @@
from __future__ import (division, absolute_import, print_function,
unicode_literals)
+from future_builtins import *
+
import sys
import os
import signal
@@ -83,23 +86,26 @@
properties and calls a hook function when any of them are
changed.
"""
- def __init__(self, proxy_object=None, *args, **kwargs):
+ def __init__(self, proxy_object=None, properties=None, **kwargs):
self.proxy = proxy_object # Mandos Client proxy object
-
- self.properties = dict()
+ self.properties = dict() if properties is None else properties
self.property_changed_match = (
self.proxy.connect_to_signal("PropertyChanged",
- self.property_changed,
+ self._property_changed,
client_interface,
byte_arrays=True))
- self.properties.update(
- self.proxy.GetAll(client_interface,
- dbus_interface = dbus.PROPERTIES_IFACE))
-
- #XXX This breaks good super behaviour
-# super(MandosClientPropertyCache, self).__init__(
-# *args, **kwargs)
+ if properties is None:
+ self.properties.update(
+ self.proxy.GetAll(client_interface,
+ dbus_interface
+ = dbus.PROPERTIES_IFACE))
+
+ super(MandosClientPropertyCache, self).__init__(**kwargs)
+
+ def _property_changed(self, property, value):
+ """Helper which takes positional arguments"""
+ return self.property_changed(property=property, value=value)
def property_changed(self, property=None, value=None):
"""This is called whenever we get a PropertyChanged signal
@@ -108,10 +114,8 @@
# Update properties dict with new value
self.properties[property] = value
- def delete(self, *args, **kwargs):
+ def delete(self):
self.property_changed_match.remove()
- super(MandosClientPropertyCache, self).__init__(
- *args, **kwargs)
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
@@ -119,7 +123,7 @@
"""
def __init__(self, server_proxy_object=None, update_hook=None,
- delete_hook=None, logger=None, *args, **kwargs):
+ delete_hook=None, logger=None, **kwargs):
# Called on update
self.update_hook = update_hook
# Called on delete
@@ -130,27 +134,15 @@
self.logger = logger
self._update_timer_callback_tag = None
- self._update_timer_callback_lock = 0
# The widget shown normally
self._text_widget = urwid.Text("")
# The widget shown when we have focus
self._focus_text_widget = urwid.Text("")
- super(MandosClientWidget, self).__init__(
- update_hook=update_hook, delete_hook=delete_hook,
- *args, **kwargs)
+ super(MandosClientWidget, self).__init__(**kwargs)
self.update()
self.opened = False
- last_checked_ok = isoformat_to_datetime(self.properties
- ["LastCheckedOK"])
-
- if self.properties ["LastCheckerStatus"] != 0:
- self.using_timer(True)
-
- if self.need_approval:
- self.using_timer(True)
-
self.match_objects = (
self.proxy.connect_to_signal("CheckerCompleted",
self.checker_completed,
@@ -172,34 +164,19 @@
self.rejected,
client_interface,
byte_arrays=True))
- #self.logger('Created client %s' % (self.properties["Name"]))
-
- def property_changed(self, property=None, value=None):
- super(self, MandosClientWidget).property_changed(property,
- value)
- if property == "ApprovalPending":
- using_timer(bool(value))
- if property == "LastCheckerStatus":
- using_timer(value != 0)
- #self.logger('Checker for client %s (command "%s")'
- # ' was successful'
- # % (self.properties["Name"], command))
+ #self.logger('Created client {0}'
+ # .format(self.properties["Name"]))
def using_timer(self, flag):
"""Call this method with True or False when timer should be
activated or deactivated.
"""
- old = self._update_timer_callback_lock
- if flag:
- self._update_timer_callback_lock += 1
- else:
- self._update_timer_callback_lock -= 1
- if old == 0 and self._update_timer_callback_lock:
+ if flag and self._update_timer_callback_tag is None:
# Will update the shown timer value every second
self._update_timer_callback_tag = (gobject.timeout_add
(1000,
self.update_timer))
- elif old and self._update_timer_callback_lock == 0:
+ elif not (flag or self._update_timer_callback_tag is None):
gobject.source_remove(self._update_timer_callback_tag)
self._update_timer_callback_tag = None
@@ -209,47 +186,48 @@
return
# Checker failed
if os.WIFEXITED(condition):
- self.logger('Checker for client %s (command "%s")'
- ' failed with exit code %s'
- % (self.properties["Name"], command,
- os.WEXITSTATUS(condition)))
+ self.logger('Checker for client {0} (command "{1}")'
+ ' failed with exit code {2}'
+ .format(self.properties["Name"], command,
+ os.WEXITSTATUS(condition)))
elif os.WIFSIGNALED(condition):
- self.logger('Checker for client %s (command "%s")'
- ' was killed by signal %s'
- % (self.properties["Name"], command,
- os.WTERMSIG(condition)))
+ self.logger('Checker for client {0} (command "{1}") was'
+ ' killed by signal {2}'
+ .format(self.properties["Name"], command,
+ os.WTERMSIG(condition)))
elif os.WCOREDUMP(condition):
- self.logger('Checker for client %s (command "%s")'
+ self.logger('Checker for client {0} (command "{1}")'
' dumped core'
- % (self.properties["Name"], command))
+ .format(self.properties["Name"], command))
else:
- self.logger('Checker for client %s completed'
- ' mysteriously')
+ self.logger('Checker for client {0} completed'
+ ' mysteriously'
+ .format(self.properties["Name"]))
self.update()
def checker_started(self, command):
"""Server signals that a checker started. This could be useful
to log in the future. """
- #self.logger('Client %s started checker "%s"'
- # % (self.properties["Name"], unicode(command)))
+ #self.logger('Client {0} started checker "{1}"'
+ # .format(self.properties["Name"],
+ # unicode(command)))
pass
def got_secret(self):
- self.logger('Client %s received its secret'
- % self.properties["Name"])
+ self.logger('Client {0} received its secret'
+ .format(self.properties["Name"]))
def need_approval(self, timeout, default):
if not default:
- message = 'Client %s needs approval within %s seconds'
+ message = 'Client {0} needs approval within {1} seconds'
else:
- message = 'Client %s will get its secret in %s seconds'
- self.logger(message
- % (self.properties["Name"], timeout/1000))
- self.using_timer(True)
+ message = 'Client {0} will get its secret in {1} seconds'
+ self.logger(message.format(self.properties["Name"],
+ timeout/1000))
def rejected(self, reason):
- self.logger('Client %s was rejected; reason: %s'
- % (self.properties["Name"], reason))
+ self.logger('Client {0} was rejected; reason: {1}'
+ .format(self.properties["Name"], reason))
def selectable(self):
"""Make this a "selectable" widget.
@@ -277,14 +255,14 @@
"bold-underline-blink":
"bold-underline-blink-standout",
}
-
+
# Rebuild focus and non-focus widgets using current properties
-
+
# Base part of a client. Name!
- base = ('%(name)s: '
- % {"name": self.properties["Name"]})
+ base = '{name}: '.format(name=self.properties["Name"])
if not self.properties["Enabled"]:
message = "DISABLED"
+ self.using_timer(False)
elif self.properties["ApprovalPending"]:
timeout = datetime.timedelta(milliseconds
= self.properties
@@ -292,31 +270,36 @@
last_approval_request = isoformat_to_datetime(
self.properties["LastApprovalRequest"])
if last_approval_request is not None:
- timer = timeout - (datetime.datetime.utcnow()
- - last_approval_request)
+ timer = max(timeout - (datetime.datetime.utcnow()
+ - last_approval_request),
+ datetime.timedelta())
else:
timer = datetime.timedelta()
if self.properties["ApprovedByDefault"]:
- message = "Approval in %s. (d)eny?"
+ message = "Approval in {0}. (d)eny?"
else:
- message = "Denial in %s. (a)pprove?"
- message = message % unicode(timer).rsplit(".", 1)[0]
+ message = "Denial in {0}. (a)pprove?"
+ message = message.format(unicode(timer).rsplit(".", 1)[0])
+ self.using_timer(True)
elif self.properties["LastCheckerStatus"] != 0:
- # When checker has failed, print a timer until client expires
+ # When checker has failed, show timer until client expires
expires = self.properties["Expires"]
if expires == "":
timer = datetime.timedelta(0)
else:
- expires = datetime.datetime.strptime(expires,
- '%Y-%m-%dT%H:%M:%S.%f')
- timer = expires - datetime.datetime.utcnow()
+ expires = (datetime.datetime.strptime
+ (expires, '%Y-%m-%dT%H:%M:%S.%f'))
+ timer = max(expires - datetime.datetime.utcnow(),
+ datetime.timedelta())
message = ('A checker has failed! Time until client'
- ' gets disabled: %s'
- % unicode(timer).rsplit(".", 1)[0])
+ ' gets disabled: {0}'
+ .format(unicode(timer).rsplit(".", 1)[0]))
+ self.using_timer(True)
else:
message = "enabled"
- self._text = "%s%s" % (base, message)
-
+ self.using_timer(False)
+ self._text = "{0}{1}".format(base, message)
+
if not urwid.supports_unicode():
self._text = self._text.encode("ascii", "replace")
textlist = [("normal", self._text)]
@@ -339,7 +322,7 @@
self.update()
return True # Keep calling this
- def delete(self, *args, **kwargs):
+ def delete(self, **kwargs):
if self._update_timer_callback_tag is not None:
gobject.source_remove(self._update_timer_callback_tag)
self._update_timer_callback_tag = None
@@ -348,7 +331,7 @@
self.match_objects = ()
if self.delete_hook is not None:
self.delete_hook(self)
- return super(MandosClientWidget, self).delete(*args, **kwargs)
+ return super(MandosClientWidget, self).delete(**kwargs)
def render(self, maxcolrow, focus=False):
"""Render differently if we have focus.
@@ -396,14 +379,13 @@
else:
return key
- def property_changed(self, property=None, value=None,
- *args, **kwargs):
+ def property_changed(self, property=None, **kwargs):
"""Call self.update() if old value is not new value.
This overrides the method from MandosClientPropertyCache"""
property_name = unicode(property)
old_value = self.properties.get(property_name)
super(MandosClientWidget, self).property_changed(
- property=property, value=value, *args, **kwargs)
+ property=property, **kwargs)
if self.properties.get(property_name) != old_value:
self.update()
@@ -413,8 +395,8 @@
"down" key presses, thus not allowing any containing widgets to
use them as an excuse to shift focus away from this widget.
"""
- def keypress(self, maxcolrow, key):
- ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
+ def keypress(self, *args, **kwargs):
+ ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
if ret in ("up", "down"):
return
return ret
@@ -486,9 +468,9 @@
self.main_loop = gobject.MainLoop()
def client_not_found(self, fingerprint, address):
- self.log_message(("Client with address %s and fingerprint %s"
- " could not be found" % (address,
- fingerprint)))
+ self.log_message("Client with address {0} and fingerprint"
+ " {1} could not be found"
+ .format(address, fingerprint))
def rebuild(self):
"""This rebuilds the User Interface.
@@ -547,8 +529,8 @@
client = self.clients_dict[path]
except KeyError:
# not found?
- self.log_message("Unknown client %r (%r) removed", name,
- path)
+ self.log_message("Unknown client {0!r} ({1!r}) removed"
+ .format(name, path))
return
client.delete()
@@ -635,7 +617,7 @@
logger
=self.log_message),
path=path)
-
+
self.refresh()
self._input_callback_tag = (gobject.io_add_watch
(sys.stdin.fileno(),
=== modified file 'network-hooks.d/bridge'
--- network-hooks.d/bridge 2012-01-15 16:10:09 +0000
+++ network-hooks.d/bridge 2012-04-24 06:55:34 +0000
@@ -6,8 +6,8 @@
# configuration file(s) should be copied into the
# /etc/mandos/network-hooks.d directory.
#
-# Copyright © 2011 Teddy Hogeborn
-# Copyright © 2011 Björn Påhlsson
+# Copyright © 2012 Teddy Hogeborn
+# Copyright © 2012 Björn Påhlsson
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
@@ -45,35 +45,40 @@
fi
done
-case "$1" in
- start)
- "$brctl" addbr "$BRIDGE"
- for address in $PORT_ADDRESSES; do
- interface=`addrtoif "$address"`
- "$brctl" addif "$BRIDGE" "$interface"
- ip link set dev "$interface" up
- done
- ip link set dev "$BRIDGE" up
- sleep "${DELAY%%.*}"
- if [ -n "$IPADDRS" ]; then
- for ipaddr in $IPADDRS; do
- ip addr add "$ipaddr" dev "$BRIDGE"
- done
- fi
- if [ -n "$ROUTES" ]; then
- for route in $ROUTES; do
- ip route add "$route" dev "$BRIDGE"
- done
- fi
- ;;
- stop)
- ip link set dev "$BRIDGE" down
- for address in $PORT_ADDRESSES; do
- interface=`addrtoif "$address"`
- ip link set dev "$interface" down
- "$brctl" delif "$BRIDGE" "$interface"
- done
- "$brctl" delbr "$BRIDGE"
+do_start(){
+ "$brctl" addbr "$BRIDGE"
+ for address in $PORT_ADDRESSES; do
+ interface=`addrtoif "$address"`
+ "$brctl" addif "$BRIDGE" "$interface"
+ ip link set dev "$interface" up
+ done
+ ip link set dev "$BRIDGE" up
+ sleep "${DELAY%%.*}"
+ if [ -n "$IPADDRS" ]; then
+ for ipaddr in $IPADDRS; do
+ ip addr add "$ipaddr" dev "$BRIDGE"
+ done
+ fi
+ if [ -n "$ROUTES" ]; then
+ for route in $ROUTES; do
+ ip route add "$route" dev "$BRIDGE"
+ done
+ fi
+}
+
+do_stop(){
+ ip link set dev "$BRIDGE" down
+ for address in $PORT_ADDRESSES; do
+ interface=`addrtoif "$address"`
+ ip link set dev "$interface" down
+ "$brctl" delif "$BRIDGE" "$interface"
+ done
+ "$brctl" delbr "$BRIDGE"
+}
+
+case "${MODE:-$1}" in
+ start|stop)
+ do_"${MODE:-$1}"
;;
files)
echo /bin/ip
=== modified file 'network-hooks.d/openvpn'
--- network-hooks.d/openvpn 2012-01-01 17:38:33 +0000
+++ network-hooks.d/openvpn 2012-04-24 06:55:34 +0000
@@ -37,18 +37,22 @@
openvpn=/usr/sbin/openvpn
-case "$1" in
- start)
- "$openvpn" --cd "$MANDOSNETHOOKDIR" \
- --daemon 'openvpn(Mandos)' --writepid "$PIDFILE" \
- --config "$CONFIG"
- sleep "$DELAY"
- ;;
- stop)
- PID="`cat \"$PIDFILE\"`"
- if [ "$PID" -gt 0 ]; then
- kill "$PID"
- fi
+do_start(){
+ "$openvpn" --cd "$MANDOSNETHOOKDIR" --daemon 'openvpn(Mandos)' \
+ --writepid "$PIDFILE" --config "$CONFIG"
+ sleep "$DELAY"
+}
+
+do_stop(){
+ PID="`cat \"$PIDFILE\"`"
+ if [ "$PID" -gt 0 ]; then
+ kill "$PID"
+ fi
+}
+
+case "${MODE:-$1}" in
+ start|stop)
+ do_"${MODE:-$1}"
;;
files)
echo "$openvpn"
=== modified file 'network-hooks.d/wireless'
--- network-hooks.d/wireless 2012-01-01 17:38:33 +0000
+++ network-hooks.d/wireless 2012-04-24 06:55:34 +0000
@@ -73,77 +73,81 @@
WPAS_OPTIONS="-P$PIDFILE $WPAS_OPTIONS"
fi
-case "${MODE:-$1}" in
- start)
- mkdir -m u=rwx,go= -p "$CTRLDIR"
- "$wpa_supplicant" -B -g "$CTRL" -p "$CTRLDIR" $WPAS_OPTIONS
- for KEY in $ifkeys; do
- ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
- INTERFACE=`addrtoif "$ADDRESS"`
- DRIVER=`eval 'echo "$WPA_DRIVER_'"$KEY"\"`
- IFDELAY=`eval 'echo "$DELAY_'"$KEY"\"`
- "$wpa_cli" -g "$CTRL" interface_add "$INTERFACE" "" \
- "${DRIVER:-wext}" "$CTRLDIR" > /dev/null \
- | sed -e '/^OK$/d'
- NETWORK=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" \
- add_network`
- eval wpa_interface_"$KEY"
- "$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" enable_network \
- "$NETWORK" | sed -e '/^OK$/d'
- sleep "${IFDELAY:-$DELAY}" &
- sleep=$!
- while :; do
- kill -0 $sleep 2>/dev/null || break
- STATE=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" \
- status | sed -n -e 's/^wpa_state=//p'`
- if [ "$STATE" = COMPLETED ]; then
- while :; do
- kill -0 $sleep 2>/dev/null || break 2
- UP=`cat /sys/class/net/"$INTERFACE"/operstate`
- if [ "$UP" = up ]; then
- kill $sleep 2>/dev/null
- break 2
- fi
- sleep 1
- done
- fi
- sleep 1
- done &
- wait $sleep || :
- IPADDRS=`eval 'echo "$IPADDRS_'"$KEY"\"`
- if [ -n "$IPADDRS" ]; then
- if [ "$IPADDRS" = dhcp ]; then
- ipconfig -c dhcp -d "$INTERFACE" || :
- #dhclient "$INTERFACE"
- else
- for ipaddr in $IPADDRS; do
- "$ip" addr add "$ipaddr" dev "$INTERFACE"
- done
- fi
- fi
- ROUTES=`eval 'echo "$ROUTES_'"$KEY"\"`
- if [ -n "$ROUTES" ]; then
- for route in $ROUTES; do
- "$ip" route add "$route" dev "$BRIDGE"
- done
- fi
- done
- ;;
- stop)
- "$wpa_cli" -g "$CTRL" terminate 2>&1 | sed -e '/^OK$/d'
- for KEY in $ifkeys; do
- ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
- INTERFACE=`addrtoif "$ADDRESS"`
- "$ip" addr show scope global permanent dev "$INTERFACE" \
- | while read type addr rest; do
+do_start(){
+ mkdir -m u=rwx,go= -p "$CTRLDIR"
+ "$wpa_supplicant" -B -g "$CTRL" -p "$CTRLDIR" $WPAS_OPTIONS
+ for KEY in $ifkeys; do
+ ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
+ INTERFACE=`addrtoif "$ADDRESS"`
+ DRIVER=`eval 'echo "$WPA_DRIVER_'"$KEY"\"`
+ IFDELAY=`eval 'echo "$DELAY_'"$KEY"\"`
+ "$wpa_cli" -g "$CTRL" interface_add "$INTERFACE" "" \
+ "${DRIVER:-wext}" "$CTRLDIR" > /dev/null \
+ | sed -e '/^OK$/d'
+ NETWORK=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" add_network`
+ eval wpa_interface_"$KEY"
+ "$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" enable_network \
+ "$NETWORK" | sed -e '/^OK$/d'
+ sleep "${IFDELAY:-$DELAY}" &
+ sleep=$!
+ while :; do
+ kill -0 $sleep 2>/dev/null || break
+ STATE=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" status \
+ | sed -n -e 's/^wpa_state=//p'`
+ if [ "$STATE" = COMPLETED ]; then
+ while :; do
+ kill -0 $sleep 2>/dev/null || break 2
+ UP=`cat /sys/class/net/"$INTERFACE"/operstate`
+ if [ "$UP" = up ]; then
+ kill $sleep 2>/dev/null
+ break 2
+ fi
+ sleep 1
+ done
+ fi
+ sleep 1
+ done &
+ wait $sleep || :
+ IPADDRS=`eval 'echo "$IPADDRS_'"$KEY"\"`
+ if [ -n "$IPADDRS" ]; then
+ if [ "$IPADDRS" = dhcp ]; then
+ ipconfig -c dhcp -d "$INTERFACE" || :
+ #dhclient "$INTERFACE"
+ else
+ for ipaddr in $IPADDRS; do
+ "$ip" addr add "$ipaddr" dev "$INTERFACE"
+ done
+ fi
+ fi
+ ROUTES=`eval 'echo "$ROUTES_'"$KEY"\"`
+ if [ -n "$ROUTES" ]; then
+ for route in $ROUTES; do
+ "$ip" route add "$route" dev "$BRIDGE"
+ done
+ fi
+ done
+}
+
+do_stop(){
+ "$wpa_cli" -g "$CTRL" terminate 2>&1 | sed -e '/^OK$/d'
+ for KEY in $ifkeys; do
+ ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
+ INTERFACE=`addrtoif "$ADDRESS"`
+ "$ip" addr show scope global permanent dev "$INTERFACE" \
+ | while read type addr rest; do
case "$type" in
inet|inet6)
"$ip" addr del "$addr" dev "$INTERFACE"
;;
esac
done
- "$ip" link set dev "$INTERFACE" down
- done
+ "$ip" link set dev "$INTERFACE" down
+ done
+}
+
+case "${MODE:-$1}" in
+ start|stop)
+ do_"${MODE:-$1}"
;;
files)
echo "$wpa_supplicant"