88
101
except ImportError:
89
102
SO_BINDTODEVICE = None
104
if sys.version_info.major == 2:
92
108
stored_state_file = "clients.pickle"
94
110
logger = logging.getLogger()
98
if_nametoindex = (ctypes.cdll.LoadLibrary
99
(ctypes.util.find_library("c"))
114
if_nametoindex = ctypes.cdll.LoadLibrary(
115
ctypes.util.find_library("c")).if_nametoindex
101
116
except (OSError, AttributeError):
102
118
def if_nametoindex(interface):
103
119
"Get an interface index the hard way, i.e. using fcntl()"
104
120
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
105
121
with contextlib.closing(socket.socket()) as s:
106
122
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
107
struct.pack(str("16s16x"),
109
interface_index = struct.unpack(str("I"),
123
struct.pack(b"16s16x", interface))
124
interface_index = struct.unpack("I", ifreq[16:20])[0]
111
125
return interface_index
276
297
self.entry_group_state_changed_match = None
299
def rename(self, remove=True):
279
300
"""Derived from the Avahi example code"""
280
301
if self.rename_count >= self.max_renames:
281
302
logger.critical("No suitable Zeroconf service name found"
282
303
" after %i retries, exiting.",
283
304
self.rename_count)
284
305
raise AvahiServiceError("Too many renames")
285
self.name = unicode(self.server
286
.GetAlternativeServiceName(self.name))
307
self.server.GetAlternativeServiceName(self.name))
308
self.rename_count += 1
287
309
logger.info("Changing Zeroconf service name to %r ...",
292
315
except dbus.exceptions.DBusException as error:
293
logger.critical("D-Bus Exception", exc_info=error)
296
self.rename_count += 1
316
if (error.get_dbus_name()
317
== "org.freedesktop.Avahi.CollisionError"):
318
logger.info("Local Zeroconf service name collision.")
319
return self.rename(remove=False)
321
logger.critical("D-Bus Exception", exc_info=error)
298
325
def remove(self):
299
326
"""Derived from the Avahi example code"""
355
381
def server_state_changed(self, state, error=None):
356
382
"""Derived from the Avahi example code"""
357
383
logger.debug("Avahi server state change: %i", state)
358
bad_states = { avahi.SERVER_INVALID:
359
"Zeroconf server invalid",
360
avahi.SERVER_REGISTERING: None,
361
avahi.SERVER_COLLISION:
362
"Zeroconf server name collision",
363
avahi.SERVER_FAILURE:
364
"Zeroconf server failure" }
385
avahi.SERVER_INVALID: "Zeroconf server invalid",
386
avahi.SERVER_REGISTERING: None,
387
avahi.SERVER_COLLISION: "Zeroconf server name collision",
388
avahi.SERVER_FAILURE: "Zeroconf server failure",
365
390
if state in bad_states:
366
391
if bad_states[state] is not None:
367
392
if error is None:
386
422
follow_name_owner_changes=True),
387
423
avahi.DBUS_INTERFACE_SERVER)
388
424
self.server.connect_to_signal("StateChanged",
389
self.server_state_changed)
425
self.server_state_changed)
390
426
self.server_state_changed(self.server.GetState())
393
429
class AvahiServiceToSyslog(AvahiService):
430
def rename(self, *args, **kwargs):
395
431
"""Add the new name to the syslog messages"""
396
ret = AvahiService.rename(self)
397
syslogger.setFormatter(logging.Formatter
398
('Mandos ({0}) [%(process)d]:'
399
' %(levelname)s: %(message)s'
432
ret = AvahiService.rename(self, *args, **kwargs)
433
syslogger.setFormatter(logging.Formatter(
434
'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
404
def timedelta_to_milliseconds(td):
405
"Convert a datetime.timedelta() to milliseconds"
406
return ((td.days * 24 * 60 * 60 * 1000)
407
+ (td.seconds * 1000)
408
+ (td.microseconds // 1000))
438
def call_pipe(connection, # : multiprocessing.Connection
439
func, *args, **kwargs):
440
"""This function is meant to be called by multiprocessing.Process
442
This function runs func(*args, **kwargs), and writes the resulting
443
return value on the provided multiprocessing.Connection.
445
connection.send(func(*args, **kwargs))
411
448
class Client(object):
412
449
"""A representation of a client host served by this server.
457
496
"fingerprint", "host", "interval",
458
497
"last_approval_request", "last_checked_ok",
459
498
"last_enabled", "name", "timeout")
460
client_defaults = { "timeout": "PT5M",
461
"extended_timeout": "PT15M",
463
"checker": "fping -q -- %%(host)s",
465
"approval_delay": "PT0S",
466
"approval_duration": "PT1S",
467
"approved_by_default": "True",
471
def timeout_milliseconds(self):
472
"Return the 'timeout' attribute in milliseconds"
473
return timedelta_to_milliseconds(self.timeout)
475
def extended_timeout_milliseconds(self):
476
"Return the 'extended_timeout' attribute in milliseconds"
477
return timedelta_to_milliseconds(self.extended_timeout)
479
def interval_milliseconds(self):
480
"Return the 'interval' attribute in milliseconds"
481
return timedelta_to_milliseconds(self.interval)
483
def approval_delay_milliseconds(self):
484
return timedelta_to_milliseconds(self.approval_delay)
501
"extended_timeout": "PT15M",
503
"checker": "fping -q -- %%(host)s",
505
"approval_delay": "PT0S",
506
"approval_duration": "PT1S",
507
"approved_by_default": "True",
487
512
def config_parser(config):
565
590
self.current_checker_command = None
566
591
self.approved = None
567
592
self.approvals_pending = 0
568
self.changedstate = (multiprocessing_manager
569
.Condition(multiprocessing_manager
571
self.client_structure = [attr for attr in
572
self.__dict__.iterkeys()
593
self.changedstate = multiprocessing_manager.Condition(
594
multiprocessing_manager.Lock())
595
self.client_structure = [attr
596
for attr in self.__dict__.iterkeys()
573
597
if not attr.startswith("_")]
574
598
self.client_structure.append("client_structure")
576
for name, t in inspect.getmembers(type(self),
600
for name, t in inspect.getmembers(
601
type(self), lambda obj: isinstance(obj, property)):
580
602
if not name.startswith("_"):
581
603
self.client_structure.append(name)
624
646
# and every interval from then on.
625
647
if self.checker_initiator_tag is not None:
626
648
gobject.source_remove(self.checker_initiator_tag)
627
self.checker_initiator_tag = (gobject.timeout_add
628
(self.interval_milliseconds(),
649
self.checker_initiator_tag = gobject.timeout_add(
650
int(self.interval.total_seconds() * 1000),
630
652
# Schedule a disable() when 'timeout' has passed
631
653
if self.disable_initiator_tag is not None:
632
654
gobject.source_remove(self.disable_initiator_tag)
633
self.disable_initiator_tag = (gobject.timeout_add
634
(self.timeout_milliseconds(),
655
self.disable_initiator_tag = gobject.timeout_add(
656
int(self.timeout.total_seconds() * 1000), self.disable)
636
657
# Also start a new checker *right now*.
637
658
self.start_checker()
639
def checker_callback(self, pid, condition, command):
660
def checker_callback(self, source, condition, connection,
640
662
"""The checker has completed, so take appropriate actions."""
641
663
self.checker_callback_tag = None
642
664
self.checker = None
643
if os.WIFEXITED(condition):
644
self.last_checker_status = os.WEXITSTATUS(condition)
665
# Read return code from connection (see call_pipe)
666
returncode = connection.recv()
670
self.last_checker_status = returncode
671
self.last_checker_signal = None
645
672
if self.last_checker_status == 0:
646
673
logger.info("Checker for %(name)s succeeded",
648
675
self.checked_ok()
650
logger.info("Checker for %(name)s failed",
677
logger.info("Checker for %(name)s failed", vars(self))
653
679
self.last_checker_status = -1
680
self.last_checker_signal = -returncode
654
681
logger.warning("Checker for %(name)s crashed?",
657
685
def checked_ok(self):
658
686
"""Assert that the client has been seen, alive and well."""
659
687
self.last_checked_ok = datetime.datetime.utcnow()
660
688
self.last_checker_status = 0
689
self.last_checker_signal = None
661
690
self.bump_timeout()
663
692
def bump_timeout(self, timeout=None):
690
718
# than 'timeout' for the client to be disabled, which is as it
693
# If a checker exists, make sure it is not a zombie
695
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
696
except AttributeError:
698
except OSError as error:
699
if error.errno != errno.ECHILD:
703
logger.warning("Checker was a zombie")
704
gobject.source_remove(self.checker_callback_tag)
705
self.checker_callback(pid, status,
706
self.current_checker_command)
721
if self.checker is not None and not self.checker.is_alive():
722
logger.warning("Checker was not alive; joining")
707
725
# Start a new checker if needed
708
726
if self.checker is None:
709
727
# Escape attributes for the shell
710
escaped_attrs = dict(
711
(attr, re.escape(unicode(getattr(self, attr))))
713
self.runtime_expansions)
729
attr: re.escape(str(getattr(self, attr)))
730
for attr in self.runtime_expansions }
715
732
command = self.checker_command % escaped_attrs
716
733
except TypeError as error:
717
734
logger.error('Could not format string "%s"',
718
self.checker_command, exc_info=error)
719
return True # Try again later
735
self.checker_command,
737
return True # Try again later
720
738
self.current_checker_command = command
722
logger.info("Starting checker %r for %s",
724
# We don't need to redirect stdout and stderr, since
725
# in normal mode, that is already done by daemon(),
726
# and in debug mode we don't want to. (Stdin is
727
# always replaced by /dev/null.)
728
# The exception is when not debugging but nevertheless
729
# running in the foreground; use the previously
732
if (not self.server_settings["debug"]
733
and self.server_settings["foreground"]):
734
popen_args.update({"stdout": wnull,
736
self.checker = subprocess.Popen(command,
740
except OSError as error:
741
logger.error("Failed to start subprocess",
744
self.checker_callback_tag = (gobject.child_watch_add
746
self.checker_callback,
748
# The checker may have completed before the gobject
749
# watch was added. Check for this.
751
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
752
except OSError as error:
753
if error.errno == errno.ECHILD:
754
# This should never happen
755
logger.error("Child process vanished",
760
gobject.source_remove(self.checker_callback_tag)
761
self.checker_callback(pid, status, command)
739
logger.info("Starting checker %r for %s", command,
741
# We don't need to redirect stdout and stderr, since
742
# in normal mode, that is already done by daemon(),
743
# and in debug mode we don't want to. (Stdin is
744
# always replaced by /dev/null.)
745
# The exception is when not debugging but nevertheless
746
# running in the foreground; use the previously
748
popen_args = { "close_fds": True,
751
if (not self.server_settings["debug"]
752
and self.server_settings["foreground"]):
753
popen_args.update({"stdout": wnull,
755
pipe = multiprocessing.Pipe(duplex = False)
756
self.checker = multiprocessing.Process(
758
args = (pipe[1], subprocess.call, command),
761
self.checker_callback_tag = gobject.io_add_watch(
762
pipe[0].fileno(), gobject.IO_IN,
763
self.checker_callback, pipe[0], command)
762
764
# Re-run this periodically if run by gobject.timeout_add
834
835
"""Decorator to annotate D-Bus methods, signals or properties
838
@dbus_annotations({"org.freedesktop.DBus.Deprecated": "true",
839
"org.freedesktop.DBus.Property."
840
"EmitsChangedSignal": "false"})
837
841
@dbus_service_property("org.example.Interface", signature="b",
839
@dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
840
"org.freedesktop.DBus.Property."
841
"EmitsChangedSignal": "false"})
842
843
def Property_dbus_property(self):
843
844
return dbus.Boolean(False)
846
See also the DBusObjectWithAnnotations class.
845
849
def decorator(func):
846
850
func._dbus_annotations = annotations
851
856
class DBusPropertyException(dbus.exceptions.DBusException):
852
857
"""A base class for D-Bus property-related exceptions
854
def __unicode__(self):
855
return unicode(str(self))
858
862
class DBusPropertyAccessException(DBusPropertyException):
882
885
If called like _is_dbus_thing("method") it returns a function
883
886
suitable for use as predicate to inspect.getmembers().
885
return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
888
return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
888
891
def _get_all_dbus_things(self, thing):
889
892
"""Returns a generator of (name, attribute) pairs
891
return ((getattr(athing.__get__(self), "_dbus_name",
894
return ((getattr(athing.__get__(self), "_dbus_name", name),
893
895
athing.__get__(self))
894
896
for cls in self.__class__.__mro__
895
897
for name, athing in
896
inspect.getmembers(cls,
897
self._is_dbus_thing(thing)))
898
inspect.getmembers(cls, self._is_dbus_thing(thing)))
900
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
902
path_keyword = 'object_path',
903
connection_keyword = 'connection')
904
def Introspect(self, object_path, connection):
905
"""Overloading of standard D-Bus method.
907
Inserts annotation tags on methods and signals.
909
xmlstring = dbus.service.Object.Introspect(self, object_path,
912
document = xml.dom.minidom.parseString(xmlstring)
914
for if_tag in document.getElementsByTagName("interface"):
915
# Add annotation tags
916
for typ in ("method", "signal"):
917
for tag in if_tag.getElementsByTagName(typ):
919
for name, prop in (self.
920
_get_all_dbus_things(typ)):
921
if (name == tag.getAttribute("name")
922
and prop._dbus_interface
923
== if_tag.getAttribute("name")):
924
annots.update(getattr(
925
prop, "_dbus_annotations", {}))
926
for name, value in annots.items():
927
ann_tag = document.createElement(
929
ann_tag.setAttribute("name", name)
930
ann_tag.setAttribute("value", value)
931
tag.appendChild(ann_tag)
932
# Add interface annotation tags
933
for annotation, value in dict(
934
itertools.chain.from_iterable(
935
annotations().items()
936
for name, annotations
937
in self._get_all_dbus_things("interface")
938
if name == if_tag.getAttribute("name")
940
ann_tag = document.createElement("annotation")
941
ann_tag.setAttribute("name", annotation)
942
ann_tag.setAttribute("value", value)
943
if_tag.appendChild(ann_tag)
944
# Fix argument name for the Introspect method itself
945
if (if_tag.getAttribute("name")
946
== dbus.INTROSPECTABLE_IFACE):
947
for cn in if_tag.getElementsByTagName("method"):
948
if cn.getAttribute("name") == "Introspect":
949
for arg in cn.getElementsByTagName("arg"):
950
if (arg.getAttribute("direction")
952
arg.setAttribute("name",
954
xmlstring = document.toxml("utf-8")
956
except (AttributeError, xml.dom.DOMException,
957
xml.parsers.expat.ExpatError) as error:
958
logger.error("Failed to override Introspection method",
963
class DBusObjectWithProperties(DBusObjectWithAnnotations):
964
"""A D-Bus object with properties.
966
Classes inheriting from this can use the dbus_service_property
967
decorator to expose methods as D-Bus properties. It exposes the
968
standard Get(), Set(), and GetAll() methods on the D-Bus.
899
971
def _get_dbus_property(self, interface_name, property_name):
900
972
"""Returns a bound method if one exists which is a D-Bus
901
973
property with the specified name and interface.
903
for cls in self.__class__.__mro__:
904
for name, value in (inspect.getmembers
906
self._is_dbus_thing("property"))):
975
for cls in self.__class__.__mro__:
976
for name, value in inspect.getmembers(
977
cls, self._is_dbus_thing("property")):
907
978
if (value._dbus_name == property_name
908
979
and value._dbus_interface == interface_name):
909
980
return value.__get__(self)
911
982
# No such property
912
raise DBusPropertyNotFound(self.dbus_object_path + ":"
913
+ interface_name + "."
916
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
983
raise DBusPropertyNotFound("{}:{}.{}".format(
984
self.dbus_object_path, interface_name, property_name))
987
def _get_all_interface_names(cls):
988
"""Get a sequence of all interfaces supported by an object"""
989
return (name for name in set(getattr(getattr(x, attr),
990
"_dbus_interface", None)
991
for x in (inspect.getmro(cls))
995
@dbus.service.method(dbus.PROPERTIES_IFACE,
917
997
out_signature="v")
918
998
def Get(self, interface_name, property_name):
919
999
"""Standard D-Bus property Get() method, see D-Bus standard.
996
1088
if prop._dbus_interface
997
1089
== if_tag.getAttribute("name")):
998
1090
if_tag.appendChild(tag)
999
# Add annotation tags
1000
for typ in ("method", "signal", "property"):
1001
for tag in if_tag.getElementsByTagName(typ):
1003
for name, prop in (self.
1004
_get_all_dbus_things(typ)):
1005
if (name == tag.getAttribute("name")
1006
and prop._dbus_interface
1007
== if_tag.getAttribute("name")):
1008
annots.update(getattr
1010
"_dbus_annotations",
1012
for name, value in annots.iteritems():
1013
ann_tag = document.createElement(
1015
ann_tag.setAttribute("name", name)
1016
ann_tag.setAttribute("value", value)
1017
tag.appendChild(ann_tag)
1018
# Add interface annotation tags
1019
for annotation, value in dict(
1020
itertools.chain.from_iterable(
1021
annotations().iteritems()
1022
for name, annotations in
1023
self._get_all_dbus_things("interface")
1024
if name == if_tag.getAttribute("name")
1026
ann_tag = document.createElement("annotation")
1027
ann_tag.setAttribute("name", annotation)
1028
ann_tag.setAttribute("value", value)
1029
if_tag.appendChild(ann_tag)
1091
# Add annotation tags for properties
1092
for tag in if_tag.getElementsByTagName("property"):
1094
for name, prop in self._get_all_dbus_things(
1096
if (name == tag.getAttribute("name")
1097
and prop._dbus_interface
1098
== if_tag.getAttribute("name")):
1099
annots.update(getattr(
1100
prop, "_dbus_annotations", {}))
1101
for name, value in annots.items():
1102
ann_tag = document.createElement(
1104
ann_tag.setAttribute("name", name)
1105
ann_tag.setAttribute("value", value)
1106
tag.appendChild(ann_tag)
1030
1107
# Add the names to the return values for the
1031
1108
# "org.freedesktop.DBus.Properties" methods
1032
1109
if (if_tag.getAttribute("name")
1050
1127
exc_info=error)
1051
1128
return xmlstring
1131
dbus.OBJECT_MANAGER_IFACE
1132
except AttributeError:
1133
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1135
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1136
"""A D-Bus object with an ObjectManager.
1138
Classes inheriting from this exposes the standard
1139
GetManagedObjects call and the InterfacesAdded and
1140
InterfacesRemoved signals on the standard
1141
"org.freedesktop.DBus.ObjectManager" interface.
1143
Note: No signals are sent automatically; they must be sent
1146
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1147
out_signature = "a{oa{sa{sv}}}")
1148
def GetManagedObjects(self):
1149
"""This function must be overridden"""
1150
raise NotImplementedError()
1152
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1153
signature = "oa{sa{sv}}")
1154
def InterfacesAdded(self, object_path, interfaces_and_properties):
1157
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1159
def InterfacesRemoved(self, object_path, interfaces):
1162
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1163
out_signature = "s",
1164
path_keyword = 'object_path',
1165
connection_keyword = 'connection')
1166
def Introspect(self, object_path, connection):
1167
"""Overloading of standard D-Bus method.
1169
Override return argument name of GetManagedObjects to be
1170
"objpath_interfaces_and_properties"
1172
xmlstring = DBusObjectWithAnnotations(self, object_path,
1175
document = xml.dom.minidom.parseString(xmlstring)
1177
for if_tag in document.getElementsByTagName("interface"):
1178
# Fix argument name for the GetManagedObjects method
1179
if (if_tag.getAttribute("name")
1180
== dbus.OBJECT_MANAGER_IFACE):
1181
for cn in if_tag.getElementsByTagName("method"):
1182
if (cn.getAttribute("name")
1183
== "GetManagedObjects"):
1184
for arg in cn.getElementsByTagName("arg"):
1185
if (arg.getAttribute("direction")
1189
"objpath_interfaces"
1191
xmlstring = document.toxml("utf-8")
1193
except (AttributeError, xml.dom.DOMException,
1194
xml.parsers.expat.ExpatError) as error:
1195
logger.error("Failed to override Introspection method",
1054
1199
def datetime_to_dbus(dt, variant_level=0):
1055
1200
"""Convert a UTC datetime.datetime() to a D-Bus type."""
1057
1202
return dbus.String("", variant_level = variant_level)
1058
return dbus.String(dt.isoformat(),
1059
variant_level=variant_level)
1203
return dbus.String(dt.isoformat(), variant_level=variant_level)
1062
1206
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1092
1237
# Ignore non-D-Bus attributes, and D-Bus attributes
1093
1238
# with the wrong interface name
1094
1239
if (not hasattr(attribute, "_dbus_interface")
1095
or not attribute._dbus_interface
1096
.startswith(orig_interface_name)):
1240
or not attribute._dbus_interface.startswith(
1241
orig_interface_name)):
1098
1243
# Create an alternate D-Bus interface name based on
1099
1244
# the current name
1100
alt_interface = (attribute._dbus_interface
1101
.replace(orig_interface_name,
1102
alt_interface_name))
1245
alt_interface = attribute._dbus_interface.replace(
1246
orig_interface_name, alt_interface_name)
1103
1247
interface_names.add(alt_interface)
1104
1248
# Is this a D-Bus signal?
1105
1249
if getattr(attribute, "_dbus_is_signal", False):
1106
# Extract the original non-method undecorated
1107
# function by black magic
1108
nonmethod_func = (dict(
1250
if sys.version_info.major == 2:
1251
# Extract the original non-method undecorated
1252
# function by black magic
1253
nonmethod_func = (dict(
1109
1254
zip(attribute.func_code.co_freevars,
1110
attribute.__closure__))["func"]
1255
attribute.__closure__))
1256
["func"].cell_contents)
1258
nonmethod_func = attribute
1112
1259
# Create a new, but exactly alike, function
1113
1260
# object, and decorate it to be a new D-Bus signal
1114
1261
# with the alternate D-Bus interface name
1115
new_function = (dbus.service.signal
1117
attribute._dbus_signature)
1118
(types.FunctionType(
1119
nonmethod_func.func_code,
1120
nonmethod_func.func_globals,
1121
nonmethod_func.func_name,
1122
nonmethod_func.func_defaults,
1123
nonmethod_func.func_closure)))
1262
if sys.version_info.major == 2:
1263
new_function = types.FunctionType(
1264
nonmethod_func.func_code,
1265
nonmethod_func.func_globals,
1266
nonmethod_func.func_name,
1267
nonmethod_func.func_defaults,
1268
nonmethod_func.func_closure)
1270
new_function = types.FunctionType(
1271
nonmethod_func.__code__,
1272
nonmethod_func.__globals__,
1273
nonmethod_func.__name__,
1274
nonmethod_func.__defaults__,
1275
nonmethod_func.__closure__)
1276
new_function = (dbus.service.signal(
1278
attribute._dbus_signature)(new_function))
1124
1279
# Copy annotations, if any
1126
new_function._dbus_annotations = (
1127
dict(attribute._dbus_annotations))
1281
new_function._dbus_annotations = dict(
1282
attribute._dbus_annotations)
1128
1283
except AttributeError:
1130
1285
# Define a creator of a function to call both the
1150
1307
# object. Decorate it to be a new D-Bus method
1151
1308
# with the alternate D-Bus interface name. Add it
1152
1309
# to the class.
1153
attr[attrname] = (dbus.service.method
1155
attribute._dbus_in_signature,
1156
attribute._dbus_out_signature)
1158
(attribute.func_code,
1159
attribute.func_globals,
1160
attribute.func_name,
1161
attribute.func_defaults,
1162
attribute.func_closure)))
1311
dbus.service.method(
1313
attribute._dbus_in_signature,
1314
attribute._dbus_out_signature)
1315
(types.FunctionType(attribute.func_code,
1316
attribute.func_globals,
1317
attribute.func_name,
1318
attribute.func_defaults,
1319
attribute.func_closure)))
1163
1320
# Copy annotations, if any
1165
attr[attrname]._dbus_annotations = (
1166
dict(attribute._dbus_annotations))
1322
attr[attrname]._dbus_annotations = dict(
1323
attribute._dbus_annotations)
1167
1324
except AttributeError:
1169
1326
# Is this a D-Bus property?
1172
1329
# object, and decorate it to be a new D-Bus
1173
1330
# property with the alternate D-Bus interface
1174
1331
# name. Add it to the class.
1175
attr[attrname] = (dbus_service_property
1177
attribute._dbus_signature,
1178
attribute._dbus_access,
1180
._dbus_get_args_options
1183
(attribute.func_code,
1184
attribute.func_globals,
1185
attribute.func_name,
1186
attribute.func_defaults,
1187
attribute.func_closure)))
1332
attr[attrname] = (dbus_service_property(
1333
alt_interface, attribute._dbus_signature,
1334
attribute._dbus_access,
1335
attribute._dbus_get_args_options
1337
(types.FunctionType(
1338
attribute.func_code,
1339
attribute.func_globals,
1340
attribute.func_name,
1341
attribute.func_defaults,
1342
attribute.func_closure)))
1188
1343
# Copy annotations, if any
1190
attr[attrname]._dbus_annotations = (
1191
dict(attribute._dbus_annotations))
1345
attr[attrname]._dbus_annotations = dict(
1346
attribute._dbus_annotations)
1192
1347
except AttributeError:
1194
1349
# Is this a D-Bus interface?
1248
1406
Client.__init__(self, *args, **kwargs)
1249
1407
# Only now, when this client is initialized, can it show up on
1251
client_object_name = unicode(self.name).translate(
1409
client_object_name = str(self.name).translate(
1252
1410
{ord("."): ord("_"),
1253
1411
ord("-"): ord("_")})
1254
self.dbus_object_path = (dbus.ObjectPath
1255
("/clients/" + client_object_name))
1412
self.dbus_object_path = dbus.ObjectPath(
1413
"/clients/" + client_object_name)
1256
1414
DBusObjectWithProperties.__init__(self, self.bus,
1257
1415
self.dbus_object_path)
1259
def notifychangeproperty(transform_func,
1260
dbus_name, type_func=lambda x: x,
1417
def notifychangeproperty(transform_func, dbus_name,
1418
type_func=lambda x: x,
1420
invalidate_only=False,
1421
_interface=_interface):
1262
1422
""" Modify a variable so that it's a property which announces
1263
1423
its changes to DBus.
1269
1429
to the D-Bus. Default: no transform
1270
1430
variant_level: D-Bus variant level. Default: 1
1272
attrname = "_{0}".format(dbus_name)
1432
attrname = "_{}".format(dbus_name)
1273
1434
def setter(self, value):
1274
1435
if hasattr(self, "dbus_object_path"):
1275
1436
if (not hasattr(self, attrname) or
1276
1437
type_func(getattr(self, attrname, None))
1277
1438
!= type_func(value)):
1278
dbus_value = transform_func(type_func(value),
1281
self.PropertyChanged(dbus.String(dbus_name),
1440
self.PropertiesChanged(
1441
_interface, dbus.Dictionary(),
1442
dbus.Array((dbus_name, )))
1444
dbus_value = transform_func(
1446
variant_level = variant_level)
1447
self.PropertyChanged(dbus.String(dbus_name),
1449
self.PropertiesChanged(
1451
dbus.Dictionary({ dbus.String(dbus_name):
1283
1454
setattr(self, attrname, value)
1285
1456
return property(lambda self: getattr(self, attrname), setter)
1302
1473
datetime_to_dbus, "LastApprovalRequest")
1303
1474
approved_by_default = notifychangeproperty(dbus.Boolean,
1304
1475
"ApprovedByDefault")
1305
approval_delay = notifychangeproperty(dbus.UInt64,
1308
timedelta_to_milliseconds)
1476
approval_delay = notifychangeproperty(
1477
dbus.UInt64, "ApprovalDelay",
1478
type_func = lambda td: td.total_seconds() * 1000)
1309
1479
approval_duration = notifychangeproperty(
1310
1480
dbus.UInt64, "ApprovalDuration",
1311
type_func = timedelta_to_milliseconds)
1481
type_func = lambda td: td.total_seconds() * 1000)
1312
1482
host = notifychangeproperty(dbus.String, "Host")
1313
timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1315
timedelta_to_milliseconds)
1483
timeout = notifychangeproperty(
1484
dbus.UInt64, "Timeout",
1485
type_func = lambda td: td.total_seconds() * 1000)
1316
1486
extended_timeout = notifychangeproperty(
1317
1487
dbus.UInt64, "ExtendedTimeout",
1318
type_func = timedelta_to_milliseconds)
1319
interval = notifychangeproperty(dbus.UInt64,
1322
timedelta_to_milliseconds)
1488
type_func = lambda td: td.total_seconds() * 1000)
1489
interval = notifychangeproperty(
1490
dbus.UInt64, "Interval",
1491
type_func = lambda td: td.total_seconds() * 1000)
1323
1492
checker_command = notifychangeproperty(dbus.String, "Checker")
1493
secret = notifychangeproperty(dbus.ByteArray, "Secret",
1494
invalidate_only=True)
1325
1496
del notifychangeproperty
1333
1504
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1334
1505
Client.__del__(self, *args, **kwargs)
1336
def checker_callback(self, pid, condition, command,
1338
self.checker_callback_tag = None
1340
if os.WIFEXITED(condition):
1341
exitstatus = os.WEXITSTATUS(condition)
1507
def checker_callback(self, source, condition,
1508
connection, command, *args, **kwargs):
1509
ret = Client.checker_callback(self, source, condition,
1510
connection, command, *args,
1512
exitstatus = self.last_checker_status
1342
1514
# Emit D-Bus signal
1343
1515
self.CheckerCompleted(dbus.Int16(exitstatus),
1344
dbus.Int64(condition),
1516
# This is specific to GNU libC
1517
dbus.Int64(exitstatus << 8),
1345
1518
dbus.String(command))
1347
1520
# Emit D-Bus signal
1348
1521
self.CheckerCompleted(dbus.Int16(-1),
1349
dbus.Int64(condition),
1523
# This is specific to GNU libC
1525
| self.last_checker_signal),
1350
1526
dbus.String(command))
1352
return Client.checker_callback(self, pid, condition, command,
1355
1529
def start_checker(self, *args, **kwargs):
1356
1530
old_checker_pid = getattr(self.checker, "pid", None)
1475
1648
self.approved_by_default = bool(value)
1477
1650
# ApprovalDelay - property
1478
@dbus_service_property(_interface, signature="t",
1651
@dbus_service_property(_interface,
1479
1653
access="readwrite")
1480
1654
def ApprovalDelay_dbus_property(self, value=None):
1481
1655
if value is None: # get
1482
return dbus.UInt64(self.approval_delay_milliseconds())
1656
return dbus.UInt64(self.approval_delay.total_seconds()
1483
1658
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1485
1660
# ApprovalDuration - property
1486
@dbus_service_property(_interface, signature="t",
1661
@dbus_service_property(_interface,
1487
1663
access="readwrite")
1488
1664
def ApprovalDuration_dbus_property(self, value=None):
1489
1665
if value is None: # get
1490
return dbus.UInt64(timedelta_to_milliseconds(
1491
self.approval_duration))
1666
return dbus.UInt64(self.approval_duration.total_seconds()
1492
1668
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1494
1670
# Name - property
1672
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1495
1673
@dbus_service_property(_interface, signature="s", access="read")
1496
1674
def Name_dbus_property(self):
1497
1675
return dbus.String(self.name)
1499
1677
# Fingerprint - property
1679
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1500
1680
@dbus_service_property(_interface, signature="s", access="read")
1501
1681
def Fingerprint_dbus_property(self):
1502
1682
return dbus.String(self.fingerprint)
1504
1684
# Host - property
1505
@dbus_service_property(_interface, signature="s",
1685
@dbus_service_property(_interface,
1506
1687
access="readwrite")
1507
1688
def Host_dbus_property(self, value=None):
1508
1689
if value is None: # get
1509
1690
return dbus.String(self.host)
1510
self.host = unicode(value)
1691
self.host = str(value)
1512
1693
# Created - property
1695
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1513
1696
@dbus_service_property(_interface, signature="s", access="read")
1514
1697
def Created_dbus_property(self):
1515
1698
return datetime_to_dbus(self.created)
1577
1762
gobject.source_remove(self.disable_initiator_tag)
1578
self.disable_initiator_tag = (
1579
gobject.timeout_add(
1580
timedelta_to_milliseconds(self.expires - now),
1763
self.disable_initiator_tag = gobject.timeout_add(
1764
int((self.expires - now).total_seconds() * 1000),
1583
1767
# ExtendedTimeout - property
1584
@dbus_service_property(_interface, signature="t",
1768
@dbus_service_property(_interface,
1585
1770
access="readwrite")
1586
1771
def ExtendedTimeout_dbus_property(self, value=None):
1587
1772
if value is None: # get
1588
return dbus.UInt64(self.extended_timeout_milliseconds())
1773
return dbus.UInt64(self.extended_timeout.total_seconds()
1589
1775
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1591
1777
# Interval - property
1592
@dbus_service_property(_interface, signature="t",
1778
@dbus_service_property(_interface,
1593
1780
access="readwrite")
1594
1781
def Interval_dbus_property(self, value=None):
1595
1782
if value is None: # get
1596
return dbus.UInt64(self.interval_milliseconds())
1783
return dbus.UInt64(self.interval.total_seconds() * 1000)
1597
1784
self.interval = datetime.timedelta(0, 0, 0, value)
1598
1785
if getattr(self, "checker_initiator_tag", None) is None:
1600
1787
if self.enabled:
1601
1788
# Reschedule checker run
1602
1789
gobject.source_remove(self.checker_initiator_tag)
1603
self.checker_initiator_tag = (gobject.timeout_add
1604
(value, self.start_checker))
1605
self.start_checker() # Start one now, too
1790
self.checker_initiator_tag = gobject.timeout_add(
1791
value, self.start_checker)
1792
self.start_checker() # Start one now, too
1607
1794
# Checker - property
1608
@dbus_service_property(_interface, signature="s",
1795
@dbus_service_property(_interface,
1609
1797
access="readwrite")
1610
1798
def Checker_dbus_property(self, value=None):
1611
1799
if value is None: # get
1612
1800
return dbus.String(self.checker_command)
1613
self.checker_command = unicode(value)
1801
self.checker_command = str(value)
1615
1803
# CheckerRunning - property
1616
@dbus_service_property(_interface, signature="b",
1804
@dbus_service_property(_interface,
1617
1806
access="readwrite")
1618
1807
def CheckerRunning_dbus_property(self, value=None):
1619
1808
if value is None: # get
1845
2039
def fingerprint(openpgp):
1846
2040
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1847
2041
# New GnuTLS "datum" with the OpenPGP public key
1848
datum = (gnutls.library.types
1849
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1852
ctypes.c_uint(len(openpgp))))
2042
datum = gnutls.library.types.gnutls_datum_t(
2043
ctypes.cast(ctypes.c_char_p(openpgp),
2044
ctypes.POINTER(ctypes.c_ubyte)),
2045
ctypes.c_uint(len(openpgp)))
1853
2046
# New empty GnuTLS certificate
1854
2047
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1855
(gnutls.library.functions
1856
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
2048
gnutls.library.functions.gnutls_openpgp_crt_init(
1857
2050
# Import the OpenPGP public key into the certificate
1858
(gnutls.library.functions
1859
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1860
gnutls.library.constants
1861
.GNUTLS_OPENPGP_FMT_RAW))
2051
gnutls.library.functions.gnutls_openpgp_crt_import(
2052
crt, ctypes.byref(datum),
2053
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
1862
2054
# Verify the self signature in the key
1863
2055
crtverify = ctypes.c_uint()
1864
(gnutls.library.functions
1865
.gnutls_openpgp_crt_verify_self(crt, 0,
1866
ctypes.byref(crtverify)))
2056
gnutls.library.functions.gnutls_openpgp_crt_verify_self(
2057
crt, 0, ctypes.byref(crtverify))
1867
2058
if crtverify.value != 0:
1868
2059
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1869
raise (gnutls.errors.CertificateSecurityError
2060
raise gnutls.errors.CertificateSecurityError(
1871
2062
# New buffer for the fingerprint
1872
2063
buf = ctypes.create_string_buffer(20)
1873
2064
buf_len = ctypes.c_size_t()
1874
2065
# Get the fingerprint from the certificate into the buffer
1875
(gnutls.library.functions
1876
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1877
ctypes.byref(buf_len)))
2066
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint(
2067
crt, ctypes.byref(buf), ctypes.byref(buf_len))
1878
2068
# Deinit the certificate
1879
2069
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1880
2070
# Convert the buffer to a Python bytestring
2048
2248
def add_pipe(self, parent_pipe, proc):
2049
2249
# Call "handle_ipc" for both data and EOF events
2050
gobject.io_add_watch(parent_pipe.fileno(),
2051
gobject.IO_IN | gobject.IO_HUP,
2052
functools.partial(self.handle_ipc,
2250
gobject.io_add_watch(
2251
parent_pipe.fileno(),
2252
gobject.IO_IN | gobject.IO_HUP,
2253
functools.partial(self.handle_ipc,
2254
parent_pipe = parent_pipe,
2057
def handle_ipc(self, source, condition, parent_pipe=None,
2058
proc = None, client_object=None):
2257
def handle_ipc(self, source, condition,
2260
client_object=None):
2059
2261
# error, or the other end of multiprocessing.Pipe has closed
2060
2262
if condition & (gobject.IO_ERR | gobject.IO_HUP):
2061
2263
# Wait for other process to exit
2148
2350
# avoid excessive use of external libraries.
2150
2352
# New type for defining tokens, syntax, and semantics all-in-one
2151
Token = collections.namedtuple("Token",
2152
("regexp", # To match token; if
2153
# "value" is not None,
2154
# must have a "group"
2156
"value", # datetime.timedelta or
2158
"followers")) # Tokens valid after
2353
Token = collections.namedtuple("Token", (
2354
"regexp", # To match token; if "value" is not None, must have
2355
# a "group" containing digits
2356
"value", # datetime.timedelta or None
2357
"followers")) # Tokens valid after this token
2160
2358
# RFC 3339 "duration" tokens, syntax, and semantics; taken from
2161
2359
# the "duration" ABNF definition in RFC 3339, Appendix A.
2162
2360
token_end = Token(re.compile(r"$"), None, frozenset())
2163
2361
token_second = Token(re.compile(r"(\d+)S"),
2164
2362
datetime.timedelta(seconds=1),
2165
frozenset((token_end,)))
2363
frozenset((token_end, )))
2166
2364
token_minute = Token(re.compile(r"(\d+)M"),
2167
2365
datetime.timedelta(minutes=1),
2168
2366
frozenset((token_second, token_end)))
2390
2592
# Override the settings from the config file with command line
2391
2593
# options, if set.
2392
2594
for option in ("interface", "address", "port", "debug",
2393
"priority", "servicename", "configdir",
2394
"use_dbus", "use_ipv6", "debuglevel", "restore",
2395
"statedir", "socket", "foreground"):
2595
"priority", "servicename", "configdir", "use_dbus",
2596
"use_ipv6", "debuglevel", "restore", "statedir",
2597
"socket", "foreground", "zeroconf"):
2396
2598
value = getattr(options, option)
2397
2599
if value is not None:
2398
2600
server_settings[option] = value
2400
2602
# Force all strings to be unicode
2401
2603
for option in server_settings.keys():
2402
if type(server_settings[option]) is str:
2403
server_settings[option] = unicode(server_settings[option])
2604
if isinstance(server_settings[option], bytes):
2605
server_settings[option] = (server_settings[option]
2404
2607
# Force all boolean options to be boolean
2405
2608
for option in ("debug", "use_dbus", "use_ipv6", "restore",
2609
"foreground", "zeroconf"):
2407
2610
server_settings[option] = bool(server_settings[option])
2408
2611
# Debug implies foreground
2409
2612
if server_settings["debug"]:
2446
2654
global mandos_dbus_service
2447
2655
mandos_dbus_service = None
2449
tcp_server = MandosServer((server_settings["address"],
2450
server_settings["port"]),
2452
interface=(server_settings["interface"]
2456
server_settings["priority"],
2458
socketfd=(server_settings["socket"]
2658
if server_settings["socket"] != "":
2659
socketfd = server_settings["socket"]
2660
tcp_server = MandosServer(
2661
(server_settings["address"], server_settings["port"]),
2663
interface=(server_settings["interface"] or None),
2665
gnutls_priority=server_settings["priority"],
2460
2668
if not foreground:
2461
2669
pidfilename = "/run/mandos.pid"
2462
2670
if not os.path.isdir("/run/."):
2463
2671
pidfilename = "/var/run/mandos.pid"
2466
pidfile = open(pidfilename, "w")
2674
pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
2467
2675
except IOError as e:
2468
2676
logger.error("Could not open file %r", pidfilename,
2525
2733
bus_name = dbus.service.BusName("se.recompile.Mandos",
2526
bus, do_not_queue=True)
2527
old_bus_name = (dbus.service.BusName
2528
("se.bsnet.fukt.Mandos", bus,
2530
except dbus.exceptions.NameExistsException as e:
2736
old_bus_name = dbus.service.BusName(
2737
"se.bsnet.fukt.Mandos", bus,
2739
except dbus.exceptions.DBusException as e:
2531
2740
logger.error("Disabling D-Bus:", exc_info=e)
2532
2741
use_dbus = False
2533
2742
server_settings["use_dbus"] = False
2534
2743
tcp_server.use_dbus = False
2535
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2536
service = AvahiServiceToSyslog(name =
2537
server_settings["servicename"],
2538
servicetype = "_mandos._tcp",
2539
protocol = protocol, bus = bus)
2540
if server_settings["interface"]:
2541
service.interface = (if_nametoindex
2542
(str(server_settings["interface"])))
2745
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2746
service = AvahiServiceToSyslog(
2747
name = server_settings["servicename"],
2748
servicetype = "_mandos._tcp",
2749
protocol = protocol,
2751
if server_settings["interface"]:
2752
service.interface = if_nametoindex(
2753
server_settings["interface"].encode("utf-8"))
2544
2755
global multiprocessing_manager
2545
2756
multiprocessing_manager = multiprocessing.Manager()
2564
2775
if server_settings["restore"]:
2566
2777
with open(stored_state_path, "rb") as stored_state:
2567
clients_data, old_client_settings = (pickle.load
2778
clients_data, old_client_settings = pickle.load(
2569
2780
os.remove(stored_state_path)
2570
2781
except IOError as e:
2571
2782
if e.errno == errno.ENOENT:
2572
logger.warning("Could not load persistent state: {0}"
2573
.format(os.strerror(e.errno)))
2783
logger.warning("Could not load persistent state:"
2784
" {}".format(os.strerror(e.errno)))
2575
2786
logger.critical("Could not load persistent state:",
2578
2789
except EOFError as e:
2579
2790
logger.warning("Could not load persistent state: "
2580
"EOFError:", exc_info=e)
2582
2794
with PGPEngine() as pgp:
2583
for client_name, client in clients_data.iteritems():
2795
for client_name, client in clients_data.items():
2584
2796
# Skip removed clients
2585
2797
if client_name not in client_settings:
2611
2823
if datetime.datetime.utcnow() >= client["expires"]:
2612
2824
if not client["last_checked_ok"]:
2613
2825
logger.warning(
2614
"disabling client {0} - Client never "
2615
"performed a successful checker"
2616
.format(client_name))
2826
"disabling client {} - Client never "
2827
"performed a successful checker".format(
2617
2829
client["enabled"] = False
2618
2830
elif client["last_checker_status"] != 0:
2619
2831
logger.warning(
2620
"disabling client {0} - Client "
2621
"last checker failed with error code {1}"
2622
.format(client_name,
2623
client["last_checker_status"]))
2832
"disabling client {} - Client last"
2833
" checker failed with error code"
2836
client["last_checker_status"]))
2624
2837
client["enabled"] = False
2626
client["expires"] = (datetime.datetime
2628
+ client["timeout"])
2839
client["expires"] = (
2840
datetime.datetime.utcnow()
2841
+ client["timeout"])
2629
2842
logger.debug("Last checker succeeded,"
2630
" keeping {0} enabled"
2631
.format(client_name))
2843
" keeping {} enabled".format(
2633
client["secret"] = (
2634
pgp.decrypt(client["encrypted_secret"],
2635
client_settings[client_name]
2846
client["secret"] = pgp.decrypt(
2847
client["encrypted_secret"],
2848
client_settings[client_name]["secret"])
2637
2849
except PGPError:
2638
2850
# If decryption fails, we use secret from new settings
2639
logger.debug("Failed to decrypt {0} old secret"
2640
.format(client_name))
2641
client["secret"] = (
2642
client_settings[client_name]["secret"])
2851
logger.debug("Failed to decrypt {} old secret".format(
2853
client["secret"] = (client_settings[client_name]
2644
2856
# Add/remove clients based on new changes made to config
2645
2857
for client_name in (set(old_client_settings)
2911
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2701
2913
@dbus.service.signal(_interface, signature="os")
2702
2914
def ClientRemoved(self, objpath, name):
2918
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2706
2920
@dbus.service.method(_interface, out_signature="ao")
2707
2921
def GetAllClients(self):
2709
return dbus.Array(c.dbus_object_path
2923
return dbus.Array(c.dbus_object_path for c in
2711
2924
tcp_server.clients.itervalues())
2926
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2713
2928
@dbus.service.method(_interface,
2714
2929
out_signature="a{oa{sv}}")
2715
2930
def GetAllClientsWithProperties(self):
2717
2932
return dbus.Dictionary(
2718
((c.dbus_object_path, c.GetAll(""))
2719
for c in tcp_server.clients.itervalues()),
2933
{ c.dbus_object_path: c.GetAll(
2934
"se.recompile.Mandos.Client")
2935
for c in tcp_server.clients.itervalues() },
2720
2936
signature="oa{sv}")
2722
2938
@dbus.service.method(_interface, in_signature="o")
2726
2942
if c.dbus_object_path == object_path:
2727
2943
del tcp_server.clients[c.name]
2728
2944
c.remove_from_connection()
2729
# Don't signal anything except ClientRemoved
2945
# Don't signal the disabling
2730
2946
c.disable(quiet=True)
2732
self.ClientRemoved(object_path, c.name)
2947
# Emit D-Bus signal for removal
2948
self.client_removed_signal(c)
2734
2950
raise KeyError(object_path)
2954
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
2955
out_signature = "a{oa{sa{sv}}}")
2956
def GetManagedObjects(self):
2958
return dbus.Dictionary(
2959
{ client.dbus_object_path:
2961
{ interface: client.GetAll(interface)
2963
client._get_all_interface_names()})
2964
for client in tcp_server.clients.values()})
2966
def client_added_signal(self, client):
2967
"""Send the new standard signal and the old signal"""
2969
# New standard signal
2970
self.InterfacesAdded(
2971
client.dbus_object_path,
2973
{ interface: client.GetAll(interface)
2975
client._get_all_interface_names()}))
2977
self.ClientAdded(client.dbus_object_path)
2979
def client_removed_signal(self, client):
2980
"""Send the new standard signal and the old signal"""
2982
# New standard signal
2983
self.InterfacesRemoved(
2984
client.dbus_object_path,
2985
client._get_all_interface_names())
2987
self.ClientRemoved(client.dbus_object_path,
2738
2990
mandos_dbus_service = MandosDBusService()
2741
2993
"Cleanup function; run on exit"
2744
2997
multiprocessing.active_children()
2775
3028
del client_settings[client.name]["secret"]
2778
with (tempfile.NamedTemporaryFile
2779
(mode='wb', suffix=".pickle", prefix='clients-',
2780
dir=os.path.dirname(stored_state_path),
2781
delete=False)) as stored_state:
3031
with tempfile.NamedTemporaryFile(
3035
dir=os.path.dirname(stored_state_path),
3036
delete=False) as stored_state:
2782
3037
pickle.dump((clients, client_settings), stored_state)
2783
tempname=stored_state.name
3038
tempname = stored_state.name
2784
3039
os.rename(tempname, stored_state_path)
2785
3040
except (IOError, OSError) as e: