88
100
except ImportError:
89
101
SO_BINDTODEVICE = None
103
if sys.version_info.major == 2:
92
107
stored_state_file = "clients.pickle"
94
109
logger = logging.getLogger()
98
if_nametoindex = (ctypes.cdll.LoadLibrary
99
(ctypes.util.find_library("c"))
113
if_nametoindex = ctypes.cdll.LoadLibrary(
114
ctypes.util.find_library("c")).if_nametoindex
101
115
except (OSError, AttributeError):
102
117
def if_nametoindex(interface):
103
118
"Get an interface index the hard way, i.e. using fcntl()"
104
119
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
105
120
with contextlib.closing(socket.socket()) as s:
106
121
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
107
struct.pack(str("16s16x"),
109
interface_index = struct.unpack(str("I"),
122
struct.pack(b"16s16x", interface))
123
interface_index = struct.unpack("I", ifreq[16:20])[0]
111
124
return interface_index
114
127
def initlogger(debug, level=logging.WARNING):
115
128
"""init logger and add loglevel"""
117
syslogger = (logging.handlers.SysLogHandler
119
logging.handlers.SysLogHandler.LOG_DAEMON,
120
address = str("/dev/log")))
131
syslogger = (logging.handlers.SysLogHandler(
132
facility = logging.handlers.SysLogHandler.LOG_DAEMON,
133
address = "/dev/log"))
121
134
syslogger.setFormatter(logging.Formatter
122
135
('Mandos [%(process)d]: %(levelname)s:'
275
296
self.entry_group_state_changed_match = None
298
def rename(self, remove=True):
278
299
"""Derived from the Avahi example code"""
279
300
if self.rename_count >= self.max_renames:
280
301
logger.critical("No suitable Zeroconf service name found"
281
302
" after %i retries, exiting.",
282
303
self.rename_count)
283
304
raise AvahiServiceError("Too many renames")
284
self.name = unicode(self.server
285
.GetAlternativeServiceName(self.name))
306
self.server.GetAlternativeServiceName(self.name))
307
self.rename_count += 1
286
308
logger.info("Changing Zeroconf service name to %r ...",
291
314
except dbus.exceptions.DBusException as error:
292
logger.critical("D-Bus Exception", exc_info=error)
295
self.rename_count += 1
315
if (error.get_dbus_name()
316
== "org.freedesktop.Avahi.CollisionError"):
317
logger.info("Local Zeroconf service name collision.")
318
return self.rename(remove=False)
320
logger.critical("D-Bus Exception", exc_info=error)
297
324
def remove(self):
298
325
"""Derived from the Avahi example code"""
354
380
def server_state_changed(self, state, error=None):
355
381
"""Derived from the Avahi example code"""
356
382
logger.debug("Avahi server state change: %i", state)
357
bad_states = { avahi.SERVER_INVALID:
358
"Zeroconf server invalid",
359
avahi.SERVER_REGISTERING: None,
360
avahi.SERVER_COLLISION:
361
"Zeroconf server name collision",
362
avahi.SERVER_FAILURE:
363
"Zeroconf server failure" }
384
avahi.SERVER_INVALID: "Zeroconf server invalid",
385
avahi.SERVER_REGISTERING: None,
386
avahi.SERVER_COLLISION: "Zeroconf server name collision",
387
avahi.SERVER_FAILURE: "Zeroconf server failure",
364
389
if state in bad_states:
365
390
if bad_states[state] is not None:
366
391
if error is None:
385
421
follow_name_owner_changes=True),
386
422
avahi.DBUS_INTERFACE_SERVER)
387
423
self.server.connect_to_signal("StateChanged",
388
self.server_state_changed)
424
self.server_state_changed)
389
425
self.server_state_changed(self.server.GetState())
392
428
class AvahiServiceToSyslog(AvahiService):
429
def rename(self, *args, **kwargs):
394
430
"""Add the new name to the syslog messages"""
395
ret = AvahiService.rename(self)
396
syslogger.setFormatter(logging.Formatter
397
('Mandos ({0}) [%(process)d]:'
398
' %(levelname)s: %(message)s'
431
ret = AvahiService.rename(self, *args, **kwargs)
432
syslogger.setFormatter(logging.Formatter(
433
'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
403
def timedelta_to_milliseconds(td):
404
"Convert a datetime.timedelta() to milliseconds"
405
return ((td.days * 24 * 60 * 60 * 1000)
406
+ (td.seconds * 1000)
407
+ (td.microseconds // 1000))
437
def call_pipe(connection, # : multiprocessing.Connection
438
func, *args, **kwargs):
439
"""This function is meant to be called by multiprocessing.Process
441
This function runs func(*args, **kwargs), and writes the resulting
442
return value on the provided multiprocessing.Connection.
444
connection.send(func(*args, **kwargs))
410
447
class Client(object):
411
448
"""A representation of a client host served by this server.
456
495
"fingerprint", "host", "interval",
457
496
"last_approval_request", "last_checked_ok",
458
497
"last_enabled", "name", "timeout")
459
client_defaults = { "timeout": "PT5M",
460
"extended_timeout": "PT15M",
462
"checker": "fping -q -- %%(host)s",
464
"approval_delay": "PT0S",
465
"approval_duration": "PT1S",
466
"approved_by_default": "True",
470
def timeout_milliseconds(self):
471
"Return the 'timeout' attribute in milliseconds"
472
return timedelta_to_milliseconds(self.timeout)
474
def extended_timeout_milliseconds(self):
475
"Return the 'extended_timeout' attribute in milliseconds"
476
return timedelta_to_milliseconds(self.extended_timeout)
478
def interval_milliseconds(self):
479
"Return the 'interval' attribute in milliseconds"
480
return timedelta_to_milliseconds(self.interval)
482
def approval_delay_milliseconds(self):
483
return timedelta_to_milliseconds(self.approval_delay)
500
"extended_timeout": "PT15M",
502
"checker": "fping -q -- %%(host)s",
504
"approval_delay": "PT0S",
505
"approval_duration": "PT1S",
506
"approved_by_default": "True",
486
511
def config_parser(config):
564
589
self.current_checker_command = None
565
590
self.approved = None
566
591
self.approvals_pending = 0
567
self.changedstate = (multiprocessing_manager
568
.Condition(multiprocessing_manager
570
self.client_structure = [attr for attr in
571
self.__dict__.iterkeys()
592
self.changedstate = multiprocessing_manager.Condition(
593
multiprocessing_manager.Lock())
594
self.client_structure = [attr
595
for attr in self.__dict__.iterkeys()
572
596
if not attr.startswith("_")]
573
597
self.client_structure.append("client_structure")
575
for name, t in inspect.getmembers(type(self),
599
for name, t in inspect.getmembers(
600
type(self), lambda obj: isinstance(obj, property)):
579
601
if not name.startswith("_"):
580
602
self.client_structure.append(name)
623
645
# and every interval from then on.
624
646
if self.checker_initiator_tag is not None:
625
647
gobject.source_remove(self.checker_initiator_tag)
626
self.checker_initiator_tag = (gobject.timeout_add
627
(self.interval_milliseconds(),
648
self.checker_initiator_tag = gobject.timeout_add(
649
int(self.interval.total_seconds() * 1000),
629
651
# Schedule a disable() when 'timeout' has passed
630
652
if self.disable_initiator_tag is not None:
631
653
gobject.source_remove(self.disable_initiator_tag)
632
self.disable_initiator_tag = (gobject.timeout_add
633
(self.timeout_milliseconds(),
654
self.disable_initiator_tag = gobject.timeout_add(
655
int(self.timeout.total_seconds() * 1000), self.disable)
635
656
# Also start a new checker *right now*.
636
657
self.start_checker()
638
def checker_callback(self, pid, condition, command):
659
def checker_callback(self, source, condition, connection,
639
661
"""The checker has completed, so take appropriate actions."""
640
662
self.checker_callback_tag = None
641
663
self.checker = None
642
if os.WIFEXITED(condition):
643
self.last_checker_status = os.WEXITSTATUS(condition)
664
# Read return code from connection (see call_pipe)
665
returncode = connection.recv()
669
self.last_checker_status = returncode
670
self.last_checker_signal = None
644
671
if self.last_checker_status == 0:
645
672
logger.info("Checker for %(name)s succeeded",
647
674
self.checked_ok()
649
logger.info("Checker for %(name)s failed",
676
logger.info("Checker for %(name)s failed", vars(self))
652
678
self.last_checker_status = -1
679
self.last_checker_signal = -returncode
653
680
logger.warning("Checker for %(name)s crashed?",
656
684
def checked_ok(self):
657
685
"""Assert that the client has been seen, alive and well."""
658
686
self.last_checked_ok = datetime.datetime.utcnow()
659
687
self.last_checker_status = 0
688
self.last_checker_signal = None
660
689
self.bump_timeout()
662
691
def bump_timeout(self, timeout=None):
689
717
# than 'timeout' for the client to be disabled, which is as it
692
# If a checker exists, make sure it is not a zombie
694
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
695
except AttributeError:
697
except OSError as error:
698
if error.errno != errno.ECHILD:
702
logger.warning("Checker was a zombie")
703
gobject.source_remove(self.checker_callback_tag)
704
self.checker_callback(pid, status,
705
self.current_checker_command)
720
if self.checker is not None and not self.checker.is_alive():
721
logger.warning("Checker was not alive; joining")
706
724
# Start a new checker if needed
707
725
if self.checker is None:
708
726
# Escape attributes for the shell
709
escaped_attrs = dict(
710
(attr, re.escape(unicode(getattr(self, attr))))
712
self.runtime_expansions)
728
attr: re.escape(str(getattr(self, attr)))
729
for attr in self.runtime_expansions }
714
731
command = self.checker_command % escaped_attrs
715
732
except TypeError as error:
716
733
logger.error('Could not format string "%s"',
717
self.checker_command, exc_info=error)
718
return True # Try again later
734
self.checker_command,
736
return True # Try again later
719
737
self.current_checker_command = command
721
logger.info("Starting checker %r for %s",
723
# We don't need to redirect stdout and stderr, since
724
# in normal mode, that is already done by daemon(),
725
# and in debug mode we don't want to. (Stdin is
726
# always replaced by /dev/null.)
727
# The exception is when not debugging but nevertheless
728
# running in the foreground; use the previously
731
if (not self.server_settings["debug"]
732
and self.server_settings["foreground"]):
733
popen_args.update({"stdout": wnull,
735
self.checker = subprocess.Popen(command,
739
except OSError as error:
740
logger.error("Failed to start subprocess",
743
self.checker_callback_tag = (gobject.child_watch_add
745
self.checker_callback,
747
# The checker may have completed before the gobject
748
# watch was added. Check for this.
750
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
751
except OSError as error:
752
if error.errno == errno.ECHILD:
753
# This should never happen
754
logger.error("Child process vanished",
759
gobject.source_remove(self.checker_callback_tag)
760
self.checker_callback(pid, status, command)
738
logger.info("Starting checker %r for %s", command,
740
# We don't need to redirect stdout and stderr, since
741
# in normal mode, that is already done by daemon(),
742
# and in debug mode we don't want to. (Stdin is
743
# always replaced by /dev/null.)
744
# The exception is when not debugging but nevertheless
745
# running in the foreground; use the previously
747
popen_args = { "close_fds": True,
750
if (not self.server_settings["debug"]
751
and self.server_settings["foreground"]):
752
popen_args.update({"stdout": wnull,
754
pipe = multiprocessing.Pipe(duplex = False)
755
self.checker = multiprocessing.Process(
757
args = (pipe[1], subprocess.call, command),
760
self.checker_callback_tag = gobject.io_add_watch(
761
pipe[0].fileno(), gobject.IO_IN,
762
self.checker_callback, pipe[0], command)
761
763
# Re-run this periodically if run by gobject.timeout_add
833
834
"""Decorator to annotate D-Bus methods, signals or properties
837
@dbus_annotations({"org.freedesktop.DBus.Deprecated": "true",
838
"org.freedesktop.DBus.Property."
839
"EmitsChangedSignal": "false"})
836
840
@dbus_service_property("org.example.Interface", signature="b",
838
@dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
839
"org.freedesktop.DBus.Property."
840
"EmitsChangedSignal": "false"})
841
842
def Property_dbus_property(self):
842
843
return dbus.Boolean(False)
845
See also the DBusObjectWithAnnotations class.
844
848
def decorator(func):
845
849
func._dbus_annotations = annotations
850
855
class DBusPropertyException(dbus.exceptions.DBusException):
851
856
"""A base class for D-Bus property-related exceptions
853
def __unicode__(self):
854
return unicode(str(self))
857
861
class DBusPropertyAccessException(DBusPropertyException):
881
884
If called like _is_dbus_thing("method") it returns a function
882
885
suitable for use as predicate to inspect.getmembers().
884
return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
887
return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
887
890
def _get_all_dbus_things(self, thing):
888
891
"""Returns a generator of (name, attribute) pairs
890
return ((getattr(athing.__get__(self), "_dbus_name",
893
return ((getattr(athing.__get__(self), "_dbus_name", name),
892
894
athing.__get__(self))
893
895
for cls in self.__class__.__mro__
894
896
for name, athing in
895
inspect.getmembers(cls,
896
self._is_dbus_thing(thing)))
897
inspect.getmembers(cls, self._is_dbus_thing(thing)))
899
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
901
path_keyword = 'object_path',
902
connection_keyword = 'connection')
903
def Introspect(self, object_path, connection):
904
"""Overloading of standard D-Bus method.
906
Inserts annotation tags on methods and signals.
908
xmlstring = dbus.service.Object.Introspect(self, object_path,
911
document = xml.dom.minidom.parseString(xmlstring)
913
for if_tag in document.getElementsByTagName("interface"):
914
# Add annotation tags
915
for typ in ("method", "signal"):
916
for tag in if_tag.getElementsByTagName(typ):
918
for name, prop in (self.
919
_get_all_dbus_things(typ)):
920
if (name == tag.getAttribute("name")
921
and prop._dbus_interface
922
== if_tag.getAttribute("name")):
923
annots.update(getattr(
924
prop, "_dbus_annotations", {}))
925
for name, value in annots.items():
926
ann_tag = document.createElement(
928
ann_tag.setAttribute("name", name)
929
ann_tag.setAttribute("value", value)
930
tag.appendChild(ann_tag)
931
# Add interface annotation tags
932
for annotation, value in dict(
933
itertools.chain.from_iterable(
934
annotations().items()
935
for name, annotations
936
in self._get_all_dbus_things("interface")
937
if name == if_tag.getAttribute("name")
939
ann_tag = document.createElement("annotation")
940
ann_tag.setAttribute("name", annotation)
941
ann_tag.setAttribute("value", value)
942
if_tag.appendChild(ann_tag)
943
# Fix argument name for the Introspect method itself
944
if (if_tag.getAttribute("name")
945
== dbus.INTROSPECTABLE_IFACE):
946
for cn in if_tag.getElementsByTagName("method"):
947
if cn.getAttribute("name") == "Introspect":
948
for arg in cn.getElementsByTagName("arg"):
949
if (arg.getAttribute("direction")
951
arg.setAttribute("name",
953
xmlstring = document.toxml("utf-8")
955
except (AttributeError, xml.dom.DOMException,
956
xml.parsers.expat.ExpatError) as error:
957
logger.error("Failed to override Introspection method",
962
class DBusObjectWithProperties(DBusObjectWithAnnotations):
963
"""A D-Bus object with properties.
965
Classes inheriting from this can use the dbus_service_property
966
decorator to expose methods as D-Bus properties. It exposes the
967
standard Get(), Set(), and GetAll() methods on the D-Bus.
898
970
def _get_dbus_property(self, interface_name, property_name):
899
971
"""Returns a bound method if one exists which is a D-Bus
900
972
property with the specified name and interface.
902
for cls in self.__class__.__mro__:
903
for name, value in (inspect.getmembers
905
self._is_dbus_thing("property"))):
974
for cls in self.__class__.__mro__:
975
for name, value in inspect.getmembers(
976
cls, self._is_dbus_thing("property")):
906
977
if (value._dbus_name == property_name
907
978
and value._dbus_interface == interface_name):
908
979
return value.__get__(self)
910
981
# No such property
911
raise DBusPropertyNotFound(self.dbus_object_path + ":"
912
+ interface_name + "."
915
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
982
raise DBusPropertyNotFound("{}:{}.{}".format(
983
self.dbus_object_path, interface_name, property_name))
986
def _get_all_interface_names(cls):
987
"""Get a sequence of all interfaces supported by an object"""
988
return (name for name in set(getattr(getattr(x, attr),
989
"_dbus_interface", None)
990
for x in (inspect.getmro(cls))
994
@dbus.service.method(dbus.PROPERTIES_IFACE,
916
996
out_signature="v")
917
997
def Get(self, interface_name, property_name):
918
998
"""Standard D-Bus property Get() method, see D-Bus standard.
995
1087
if prop._dbus_interface
996
1088
== if_tag.getAttribute("name")):
997
1089
if_tag.appendChild(tag)
998
# Add annotation tags
999
for typ in ("method", "signal", "property"):
1000
for tag in if_tag.getElementsByTagName(typ):
1002
for name, prop in (self.
1003
_get_all_dbus_things(typ)):
1004
if (name == tag.getAttribute("name")
1005
and prop._dbus_interface
1006
== if_tag.getAttribute("name")):
1007
annots.update(getattr
1009
"_dbus_annotations",
1011
for name, value in annots.iteritems():
1012
ann_tag = document.createElement(
1014
ann_tag.setAttribute("name", name)
1015
ann_tag.setAttribute("value", value)
1016
tag.appendChild(ann_tag)
1017
# Add interface annotation tags
1018
for annotation, value in dict(
1019
itertools.chain.from_iterable(
1020
annotations().iteritems()
1021
for name, annotations in
1022
self._get_all_dbus_things("interface")
1023
if name == if_tag.getAttribute("name")
1025
ann_tag = document.createElement("annotation")
1026
ann_tag.setAttribute("name", annotation)
1027
ann_tag.setAttribute("value", value)
1028
if_tag.appendChild(ann_tag)
1090
# Add annotation tags for properties
1091
for tag in if_tag.getElementsByTagName("property"):
1093
for name, prop in self._get_all_dbus_things(
1095
if (name == tag.getAttribute("name")
1096
and prop._dbus_interface
1097
== if_tag.getAttribute("name")):
1098
annots.update(getattr(
1099
prop, "_dbus_annotations", {}))
1100
for name, value in annots.items():
1101
ann_tag = document.createElement(
1103
ann_tag.setAttribute("name", name)
1104
ann_tag.setAttribute("value", value)
1105
tag.appendChild(ann_tag)
1029
1106
# Add the names to the return values for the
1030
1107
# "org.freedesktop.DBus.Properties" methods
1031
1108
if (if_tag.getAttribute("name")
1049
1126
exc_info=error)
1050
1127
return xmlstring
1130
dbus.OBJECT_MANAGER_IFACE
1131
except AttributeError:
1132
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1134
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1135
"""A D-Bus object with an ObjectManager.
1137
Classes inheriting from this exposes the standard
1138
GetManagedObjects call and the InterfacesAdded and
1139
InterfacesRemoved signals on the standard
1140
"org.freedesktop.DBus.ObjectManager" interface.
1142
Note: No signals are sent automatically; they must be sent
1145
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1146
out_signature = "a{oa{sa{sv}}}")
1147
def GetManagedObjects(self):
1148
"""This function must be overridden"""
1149
raise NotImplementedError()
1151
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1152
signature = "oa{sa{sv}}")
1153
def InterfacesAdded(self, object_path, interfaces_and_properties):
1156
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
1157
def InterfacesRemoved(self, object_path, interfaces):
1160
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1161
out_signature = "s",
1162
path_keyword = 'object_path',
1163
connection_keyword = 'connection')
1164
def Introspect(self, object_path, connection):
1165
"""Overloading of standard D-Bus method.
1167
Override return argument name of GetManagedObjects to be
1168
"objpath_interfaces_and_properties"
1170
xmlstring = DBusObjectWithAnnotations.Introspect(self,
1174
document = xml.dom.minidom.parseString(xmlstring)
1176
for if_tag in document.getElementsByTagName("interface"):
1177
# Fix argument name for the GetManagedObjects method
1178
if (if_tag.getAttribute("name")
1179
== dbus.OBJECT_MANAGER_IFACE):
1180
for cn in if_tag.getElementsByTagName("method"):
1181
if (cn.getAttribute("name")
1182
== "GetManagedObjects"):
1183
for arg in cn.getElementsByTagName("arg"):
1184
if (arg.getAttribute("direction")
1188
"objpath_interfaces"
1190
xmlstring = document.toxml("utf-8")
1192
except (AttributeError, xml.dom.DOMException,
1193
xml.parsers.expat.ExpatError) as error:
1194
logger.error("Failed to override Introspection method",
1053
1198
def datetime_to_dbus(dt, variant_level=0):
1054
1199
"""Convert a UTC datetime.datetime() to a D-Bus type."""
1056
1201
return dbus.String("", variant_level = variant_level)
1057
return dbus.String(dt.isoformat(),
1058
variant_level=variant_level)
1202
return dbus.String(dt.isoformat(), variant_level=variant_level)
1061
1205
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1091
1236
# Ignore non-D-Bus attributes, and D-Bus attributes
1092
1237
# with the wrong interface name
1093
1238
if (not hasattr(attribute, "_dbus_interface")
1094
or not attribute._dbus_interface
1095
.startswith(orig_interface_name)):
1239
or not attribute._dbus_interface.startswith(
1240
orig_interface_name)):
1097
1242
# Create an alternate D-Bus interface name based on
1098
1243
# the current name
1099
alt_interface = (attribute._dbus_interface
1100
.replace(orig_interface_name,
1101
alt_interface_name))
1244
alt_interface = attribute._dbus_interface.replace(
1245
orig_interface_name, alt_interface_name)
1102
1246
interface_names.add(alt_interface)
1103
1247
# Is this a D-Bus signal?
1104
1248
if getattr(attribute, "_dbus_is_signal", False):
1105
# Extract the original non-method undecorated
1106
# function by black magic
1107
nonmethod_func = (dict(
1249
if sys.version_info.major == 2:
1250
# Extract the original non-method undecorated
1251
# function by black magic
1252
nonmethod_func = (dict(
1108
1253
zip(attribute.func_code.co_freevars,
1109
attribute.__closure__))["func"]
1254
attribute.__closure__))
1255
["func"].cell_contents)
1257
nonmethod_func = attribute
1111
1258
# Create a new, but exactly alike, function
1112
1259
# object, and decorate it to be a new D-Bus signal
1113
1260
# with the alternate D-Bus interface name
1114
new_function = (dbus.service.signal
1116
attribute._dbus_signature)
1117
(types.FunctionType(
1118
nonmethod_func.func_code,
1119
nonmethod_func.func_globals,
1120
nonmethod_func.func_name,
1121
nonmethod_func.func_defaults,
1122
nonmethod_func.func_closure)))
1261
if sys.version_info.major == 2:
1262
new_function = types.FunctionType(
1263
nonmethod_func.func_code,
1264
nonmethod_func.func_globals,
1265
nonmethod_func.func_name,
1266
nonmethod_func.func_defaults,
1267
nonmethod_func.func_closure)
1269
new_function = types.FunctionType(
1270
nonmethod_func.__code__,
1271
nonmethod_func.__globals__,
1272
nonmethod_func.__name__,
1273
nonmethod_func.__defaults__,
1274
nonmethod_func.__closure__)
1275
new_function = (dbus.service.signal(
1277
attribute._dbus_signature)(new_function))
1123
1278
# Copy annotations, if any
1125
new_function._dbus_annotations = (
1126
dict(attribute._dbus_annotations))
1280
new_function._dbus_annotations = dict(
1281
attribute._dbus_annotations)
1127
1282
except AttributeError:
1129
1284
# Define a creator of a function to call both the
1149
1311
# object. Decorate it to be a new D-Bus method
1150
1312
# with the alternate D-Bus interface name. Add it
1151
1313
# to the class.
1152
attr[attrname] = (dbus.service.method
1154
attribute._dbus_in_signature,
1155
attribute._dbus_out_signature)
1157
(attribute.func_code,
1158
attribute.func_globals,
1159
attribute.func_name,
1160
attribute.func_defaults,
1161
attribute.func_closure)))
1315
dbus.service.method(
1317
attribute._dbus_in_signature,
1318
attribute._dbus_out_signature)
1319
(types.FunctionType(attribute.func_code,
1320
attribute.func_globals,
1321
attribute.func_name,
1322
attribute.func_defaults,
1323
attribute.func_closure)))
1162
1324
# Copy annotations, if any
1164
attr[attrname]._dbus_annotations = (
1165
dict(attribute._dbus_annotations))
1326
attr[attrname]._dbus_annotations = dict(
1327
attribute._dbus_annotations)
1166
1328
except AttributeError:
1168
1330
# Is this a D-Bus property?
1171
1333
# object, and decorate it to be a new D-Bus
1172
1334
# property with the alternate D-Bus interface
1173
1335
# name. Add it to the class.
1174
attr[attrname] = (dbus_service_property
1176
attribute._dbus_signature,
1177
attribute._dbus_access,
1179
._dbus_get_args_options
1182
(attribute.func_code,
1183
attribute.func_globals,
1184
attribute.func_name,
1185
attribute.func_defaults,
1186
attribute.func_closure)))
1336
attr[attrname] = (dbus_service_property(
1337
alt_interface, attribute._dbus_signature,
1338
attribute._dbus_access,
1339
attribute._dbus_get_args_options
1341
(types.FunctionType(
1342
attribute.func_code,
1343
attribute.func_globals,
1344
attribute.func_name,
1345
attribute.func_defaults,
1346
attribute.func_closure)))
1187
1347
# Copy annotations, if any
1189
attr[attrname]._dbus_annotations = (
1190
dict(attribute._dbus_annotations))
1349
attr[attrname]._dbus_annotations = dict(
1350
attribute._dbus_annotations)
1191
1351
except AttributeError:
1193
1353
# Is this a D-Bus interface?
1247
1410
Client.__init__(self, *args, **kwargs)
1248
1411
# Only now, when this client is initialized, can it show up on
1250
client_object_name = unicode(self.name).translate(
1413
client_object_name = str(self.name).translate(
1251
1414
{ord("."): ord("_"),
1252
1415
ord("-"): ord("_")})
1253
self.dbus_object_path = (dbus.ObjectPath
1254
("/clients/" + client_object_name))
1416
self.dbus_object_path = dbus.ObjectPath(
1417
"/clients/" + client_object_name)
1255
1418
DBusObjectWithProperties.__init__(self, self.bus,
1256
1419
self.dbus_object_path)
1258
def notifychangeproperty(transform_func,
1259
dbus_name, type_func=lambda x: x,
1421
def notifychangeproperty(transform_func, dbus_name,
1422
type_func=lambda x: x,
1424
invalidate_only=False,
1425
_interface=_interface):
1261
1426
""" Modify a variable so that it's a property which announces
1262
1427
its changes to DBus.
1268
1433
to the D-Bus. Default: no transform
1269
1434
variant_level: D-Bus variant level. Default: 1
1271
attrname = "_{0}".format(dbus_name)
1436
attrname = "_{}".format(dbus_name)
1272
1438
def setter(self, value):
1273
1439
if hasattr(self, "dbus_object_path"):
1274
1440
if (not hasattr(self, attrname) or
1275
1441
type_func(getattr(self, attrname, None))
1276
1442
!= type_func(value)):
1277
dbus_value = transform_func(type_func(value),
1280
self.PropertyChanged(dbus.String(dbus_name),
1444
self.PropertiesChanged(
1445
_interface, dbus.Dictionary(),
1446
dbus.Array((dbus_name, )))
1448
dbus_value = transform_func(
1450
variant_level = variant_level)
1451
self.PropertyChanged(dbus.String(dbus_name),
1453
self.PropertiesChanged(
1455
dbus.Dictionary({ dbus.String(dbus_name):
1282
1458
setattr(self, attrname, value)
1284
1460
return property(lambda self: getattr(self, attrname), setter)
1301
1477
datetime_to_dbus, "LastApprovalRequest")
1302
1478
approved_by_default = notifychangeproperty(dbus.Boolean,
1303
1479
"ApprovedByDefault")
1304
approval_delay = notifychangeproperty(dbus.UInt64,
1307
timedelta_to_milliseconds)
1480
approval_delay = notifychangeproperty(
1481
dbus.UInt64, "ApprovalDelay",
1482
type_func = lambda td: td.total_seconds() * 1000)
1308
1483
approval_duration = notifychangeproperty(
1309
1484
dbus.UInt64, "ApprovalDuration",
1310
type_func = timedelta_to_milliseconds)
1485
type_func = lambda td: td.total_seconds() * 1000)
1311
1486
host = notifychangeproperty(dbus.String, "Host")
1312
timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1314
timedelta_to_milliseconds)
1487
timeout = notifychangeproperty(
1488
dbus.UInt64, "Timeout",
1489
type_func = lambda td: td.total_seconds() * 1000)
1315
1490
extended_timeout = notifychangeproperty(
1316
1491
dbus.UInt64, "ExtendedTimeout",
1317
type_func = timedelta_to_milliseconds)
1318
interval = notifychangeproperty(dbus.UInt64,
1321
timedelta_to_milliseconds)
1492
type_func = lambda td: td.total_seconds() * 1000)
1493
interval = notifychangeproperty(
1494
dbus.UInt64, "Interval",
1495
type_func = lambda td: td.total_seconds() * 1000)
1322
1496
checker_command = notifychangeproperty(dbus.String, "Checker")
1497
secret = notifychangeproperty(dbus.ByteArray, "Secret",
1498
invalidate_only=True)
1324
1500
del notifychangeproperty
1332
1508
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1333
1509
Client.__del__(self, *args, **kwargs)
1335
def checker_callback(self, pid, condition, command,
1337
self.checker_callback_tag = None
1339
if os.WIFEXITED(condition):
1340
exitstatus = os.WEXITSTATUS(condition)
1511
def checker_callback(self, source, condition,
1512
connection, command, *args, **kwargs):
1513
ret = Client.checker_callback(self, source, condition,
1514
connection, command, *args,
1516
exitstatus = self.last_checker_status
1341
1518
# Emit D-Bus signal
1342
1519
self.CheckerCompleted(dbus.Int16(exitstatus),
1343
dbus.Int64(condition),
1520
# This is specific to GNU libC
1521
dbus.Int64(exitstatus << 8),
1344
1522
dbus.String(command))
1346
1524
# Emit D-Bus signal
1347
1525
self.CheckerCompleted(dbus.Int16(-1),
1348
dbus.Int64(condition),
1527
# This is specific to GNU libC
1529
| self.last_checker_signal),
1349
1530
dbus.String(command))
1351
return Client.checker_callback(self, pid, condition, command,
1354
1533
def start_checker(self, *args, **kwargs):
1355
1534
old_checker_pid = getattr(self.checker, "pid", None)
1474
1652
self.approved_by_default = bool(value)
1476
1654
# ApprovalDelay - property
1477
@dbus_service_property(_interface, signature="t",
1655
@dbus_service_property(_interface,
1478
1657
access="readwrite")
1479
1658
def ApprovalDelay_dbus_property(self, value=None):
1480
1659
if value is None: # get
1481
return dbus.UInt64(self.approval_delay_milliseconds())
1660
return dbus.UInt64(self.approval_delay.total_seconds()
1482
1662
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1484
1664
# ApprovalDuration - property
1485
@dbus_service_property(_interface, signature="t",
1665
@dbus_service_property(_interface,
1486
1667
access="readwrite")
1487
1668
def ApprovalDuration_dbus_property(self, value=None):
1488
1669
if value is None: # get
1489
return dbus.UInt64(timedelta_to_milliseconds(
1490
self.approval_duration))
1670
return dbus.UInt64(self.approval_duration.total_seconds()
1491
1672
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1493
1674
# Name - property
1676
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1494
1677
@dbus_service_property(_interface, signature="s", access="read")
1495
1678
def Name_dbus_property(self):
1496
1679
return dbus.String(self.name)
1498
1681
# Fingerprint - property
1683
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1499
1684
@dbus_service_property(_interface, signature="s", access="read")
1500
1685
def Fingerprint_dbus_property(self):
1501
1686
return dbus.String(self.fingerprint)
1503
1688
# Host - property
1504
@dbus_service_property(_interface, signature="s",
1689
@dbus_service_property(_interface,
1505
1691
access="readwrite")
1506
1692
def Host_dbus_property(self, value=None):
1507
1693
if value is None: # get
1508
1694
return dbus.String(self.host)
1509
self.host = unicode(value)
1695
self.host = str(value)
1511
1697
# Created - property
1699
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1512
1700
@dbus_service_property(_interface, signature="s", access="read")
1513
1701
def Created_dbus_property(self):
1514
1702
return datetime_to_dbus(self.created)
1576
1766
gobject.source_remove(self.disable_initiator_tag)
1577
self.disable_initiator_tag = (
1578
gobject.timeout_add(
1579
timedelta_to_milliseconds(self.expires - now),
1767
self.disable_initiator_tag = gobject.timeout_add(
1768
int((self.expires - now).total_seconds() * 1000),
1582
1771
# ExtendedTimeout - property
1583
@dbus_service_property(_interface, signature="t",
1772
@dbus_service_property(_interface,
1584
1774
access="readwrite")
1585
1775
def ExtendedTimeout_dbus_property(self, value=None):
1586
1776
if value is None: # get
1587
return dbus.UInt64(self.extended_timeout_milliseconds())
1777
return dbus.UInt64(self.extended_timeout.total_seconds()
1588
1779
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1590
1781
# Interval - property
1591
@dbus_service_property(_interface, signature="t",
1782
@dbus_service_property(_interface,
1592
1784
access="readwrite")
1593
1785
def Interval_dbus_property(self, value=None):
1594
1786
if value is None: # get
1595
return dbus.UInt64(self.interval_milliseconds())
1787
return dbus.UInt64(self.interval.total_seconds() * 1000)
1596
1788
self.interval = datetime.timedelta(0, 0, 0, value)
1597
1789
if getattr(self, "checker_initiator_tag", None) is None:
1599
1791
if self.enabled:
1600
1792
# Reschedule checker run
1601
1793
gobject.source_remove(self.checker_initiator_tag)
1602
self.checker_initiator_tag = (gobject.timeout_add
1603
(value, self.start_checker))
1604
self.start_checker() # Start one now, too
1794
self.checker_initiator_tag = gobject.timeout_add(
1795
value, self.start_checker)
1796
self.start_checker() # Start one now, too
1606
1798
# Checker - property
1607
@dbus_service_property(_interface, signature="s",
1799
@dbus_service_property(_interface,
1608
1801
access="readwrite")
1609
1802
def Checker_dbus_property(self, value=None):
1610
1803
if value is None: # get
1611
1804
return dbus.String(self.checker_command)
1612
self.checker_command = unicode(value)
1805
self.checker_command = str(value)
1614
1807
# CheckerRunning - property
1615
@dbus_service_property(_interface, signature="b",
1808
@dbus_service_property(_interface,
1616
1810
access="readwrite")
1617
1811
def CheckerRunning_dbus_property(self, value=None):
1618
1812
if value is None: # get
1844
2043
def fingerprint(openpgp):
1845
2044
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1846
2045
# New GnuTLS "datum" with the OpenPGP public key
1847
datum = (gnutls.library.types
1848
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1851
ctypes.c_uint(len(openpgp))))
2046
datum = gnutls.library.types.gnutls_datum_t(
2047
ctypes.cast(ctypes.c_char_p(openpgp),
2048
ctypes.POINTER(ctypes.c_ubyte)),
2049
ctypes.c_uint(len(openpgp)))
1852
2050
# New empty GnuTLS certificate
1853
2051
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1854
(gnutls.library.functions
1855
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
2052
gnutls.library.functions.gnutls_openpgp_crt_init(
1856
2054
# Import the OpenPGP public key into the certificate
1857
(gnutls.library.functions
1858
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1859
gnutls.library.constants
1860
.GNUTLS_OPENPGP_FMT_RAW))
2055
gnutls.library.functions.gnutls_openpgp_crt_import(
2056
crt, ctypes.byref(datum),
2057
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
1861
2058
# Verify the self signature in the key
1862
2059
crtverify = ctypes.c_uint()
1863
(gnutls.library.functions
1864
.gnutls_openpgp_crt_verify_self(crt, 0,
1865
ctypes.byref(crtverify)))
2060
gnutls.library.functions.gnutls_openpgp_crt_verify_self(
2061
crt, 0, ctypes.byref(crtverify))
1866
2062
if crtverify.value != 0:
1867
2063
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1868
raise (gnutls.errors.CertificateSecurityError
2064
raise gnutls.errors.CertificateSecurityError(
1870
2066
# New buffer for the fingerprint
1871
2067
buf = ctypes.create_string_buffer(20)
1872
2068
buf_len = ctypes.c_size_t()
1873
2069
# Get the fingerprint from the certificate into the buffer
1874
(gnutls.library.functions
1875
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1876
ctypes.byref(buf_len)))
2070
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint(
2071
crt, ctypes.byref(buf), ctypes.byref(buf_len))
1877
2072
# Deinit the certificate
1878
2073
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1879
2074
# Convert the buffer to a Python bytestring
2047
2252
def add_pipe(self, parent_pipe, proc):
2048
2253
# Call "handle_ipc" for both data and EOF events
2049
gobject.io_add_watch(parent_pipe.fileno(),
2050
gobject.IO_IN | gobject.IO_HUP,
2051
functools.partial(self.handle_ipc,
2254
gobject.io_add_watch(
2255
parent_pipe.fileno(),
2256
gobject.IO_IN | gobject.IO_HUP,
2257
functools.partial(self.handle_ipc,
2258
parent_pipe = parent_pipe,
2056
def handle_ipc(self, source, condition, parent_pipe=None,
2057
proc = None, client_object=None):
2261
def handle_ipc(self, source, condition,
2264
client_object=None):
2058
2265
# error, or the other end of multiprocessing.Pipe has closed
2059
2266
if condition & (gobject.IO_ERR | gobject.IO_HUP):
2060
2267
# Wait for other process to exit
2147
2354
# avoid excessive use of external libraries.
2149
2356
# New type for defining tokens, syntax, and semantics all-in-one
2150
Token = collections.namedtuple("Token",
2151
("regexp", # To match token; if
2152
# "value" is not None,
2153
# must have a "group"
2155
"value", # datetime.timedelta or
2157
"followers")) # Tokens valid after
2357
Token = collections.namedtuple("Token", (
2358
"regexp", # To match token; if "value" is not None, must have
2359
# a "group" containing digits
2360
"value", # datetime.timedelta or None
2361
"followers")) # Tokens valid after this token
2159
2362
# RFC 3339 "duration" tokens, syntax, and semantics; taken from
2160
2363
# the "duration" ABNF definition in RFC 3339, Appendix A.
2161
2364
token_end = Token(re.compile(r"$"), None, frozenset())
2162
2365
token_second = Token(re.compile(r"(\d+)S"),
2163
2366
datetime.timedelta(seconds=1),
2164
frozenset((token_end,)))
2367
frozenset((token_end, )))
2165
2368
token_minute = Token(re.compile(r"(\d+)M"),
2166
2369
datetime.timedelta(minutes=1),
2167
2370
frozenset((token_second, token_end)))
2389
2596
# Override the settings from the config file with command line
2390
2597
# options, if set.
2391
2598
for option in ("interface", "address", "port", "debug",
2392
"priority", "servicename", "configdir",
2393
"use_dbus", "use_ipv6", "debuglevel", "restore",
2394
"statedir", "socket", "foreground"):
2599
"priority", "servicename", "configdir", "use_dbus",
2600
"use_ipv6", "debuglevel", "restore", "statedir",
2601
"socket", "foreground", "zeroconf"):
2395
2602
value = getattr(options, option)
2396
2603
if value is not None:
2397
2604
server_settings[option] = value
2399
2606
# Force all strings to be unicode
2400
2607
for option in server_settings.keys():
2401
if type(server_settings[option]) is str:
2402
server_settings[option] = unicode(server_settings[option])
2608
if isinstance(server_settings[option], bytes):
2609
server_settings[option] = (server_settings[option]
2403
2611
# Force all boolean options to be boolean
2404
2612
for option in ("debug", "use_dbus", "use_ipv6", "restore",
2613
"foreground", "zeroconf"):
2406
2614
server_settings[option] = bool(server_settings[option])
2407
2615
# Debug implies foreground
2408
2616
if server_settings["debug"]:
2445
2658
global mandos_dbus_service
2446
2659
mandos_dbus_service = None
2448
tcp_server = MandosServer((server_settings["address"],
2449
server_settings["port"]),
2451
interface=(server_settings["interface"]
2455
server_settings["priority"],
2457
socketfd=(server_settings["socket"]
2662
if server_settings["socket"] != "":
2663
socketfd = server_settings["socket"]
2664
tcp_server = MandosServer(
2665
(server_settings["address"], server_settings["port"]),
2667
interface=(server_settings["interface"] or None),
2669
gnutls_priority=server_settings["priority"],
2459
2672
if not foreground:
2460
2673
pidfilename = "/run/mandos.pid"
2461
2674
if not os.path.isdir("/run/."):
2462
2675
pidfilename = "/var/run/mandos.pid"
2465
pidfile = open(pidfilename, "w")
2678
pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
2466
2679
except IOError as e:
2467
2680
logger.error("Could not open file %r", pidfilename,
2524
2737
bus_name = dbus.service.BusName("se.recompile.Mandos",
2525
bus, do_not_queue=True)
2526
old_bus_name = (dbus.service.BusName
2527
("se.bsnet.fukt.Mandos", bus,
2529
except dbus.exceptions.NameExistsException as e:
2740
old_bus_name = dbus.service.BusName(
2741
"se.bsnet.fukt.Mandos", bus,
2743
except dbus.exceptions.DBusException as e:
2530
2744
logger.error("Disabling D-Bus:", exc_info=e)
2531
2745
use_dbus = False
2532
2746
server_settings["use_dbus"] = False
2533
2747
tcp_server.use_dbus = False
2534
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2535
service = AvahiServiceToSyslog(name =
2536
server_settings["servicename"],
2537
servicetype = "_mandos._tcp",
2538
protocol = protocol, bus = bus)
2539
if server_settings["interface"]:
2540
service.interface = (if_nametoindex
2541
(str(server_settings["interface"])))
2749
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2750
service = AvahiServiceToSyslog(
2751
name = server_settings["servicename"],
2752
servicetype = "_mandos._tcp",
2753
protocol = protocol,
2755
if server_settings["interface"]:
2756
service.interface = if_nametoindex(
2757
server_settings["interface"].encode("utf-8"))
2543
2759
global multiprocessing_manager
2544
2760
multiprocessing_manager = multiprocessing.Manager()
2563
2779
if server_settings["restore"]:
2565
2781
with open(stored_state_path, "rb") as stored_state:
2566
clients_data, old_client_settings = (pickle.load
2782
clients_data, old_client_settings = pickle.load(
2568
2784
os.remove(stored_state_path)
2569
2785
except IOError as e:
2570
2786
if e.errno == errno.ENOENT:
2571
logger.warning("Could not load persistent state: {0}"
2572
.format(os.strerror(e.errno)))
2787
logger.warning("Could not load persistent state:"
2788
" {}".format(os.strerror(e.errno)))
2574
2790
logger.critical("Could not load persistent state:",
2577
2793
except EOFError as e:
2578
2794
logger.warning("Could not load persistent state: "
2579
"EOFError:", exc_info=e)
2581
2798
with PGPEngine() as pgp:
2582
for client_name, client in clients_data.iteritems():
2799
for client_name, client in clients_data.items():
2583
2800
# Skip removed clients
2584
2801
if client_name not in client_settings:
2610
2827
if datetime.datetime.utcnow() >= client["expires"]:
2611
2828
if not client["last_checked_ok"]:
2612
2829
logger.warning(
2613
"disabling client {0} - Client never "
2614
"performed a successful checker"
2615
.format(client_name))
2830
"disabling client {} - Client never "
2831
"performed a successful checker".format(
2616
2833
client["enabled"] = False
2617
2834
elif client["last_checker_status"] != 0:
2618
2835
logger.warning(
2619
"disabling client {0} - Client "
2620
"last checker failed with error code {1}"
2621
.format(client_name,
2622
client["last_checker_status"]))
2836
"disabling client {} - Client last"
2837
" checker failed with error code"
2840
client["last_checker_status"]))
2623
2841
client["enabled"] = False
2625
client["expires"] = (datetime.datetime
2627
+ client["timeout"])
2843
client["expires"] = (
2844
datetime.datetime.utcnow()
2845
+ client["timeout"])
2628
2846
logger.debug("Last checker succeeded,"
2629
" keeping {0} enabled"
2630
.format(client_name))
2847
" keeping {} enabled".format(
2632
client["secret"] = (
2633
pgp.decrypt(client["encrypted_secret"],
2634
client_settings[client_name]
2850
client["secret"] = pgp.decrypt(
2851
client["encrypted_secret"],
2852
client_settings[client_name]["secret"])
2636
2853
except PGPError:
2637
2854
# If decryption fails, we use secret from new settings
2638
logger.debug("Failed to decrypt {0} old secret"
2639
.format(client_name))
2640
client["secret"] = (
2641
client_settings[client_name]["secret"])
2855
logger.debug("Failed to decrypt {} old secret".format(
2857
client["secret"] = (client_settings[client_name]
2643
2860
# Add/remove clients based on new changes made to config
2644
2861
for client_name in (set(old_client_settings)
2915
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2700
2917
@dbus.service.signal(_interface, signature="os")
2701
2918
def ClientRemoved(self, objpath, name):
2922
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2705
2924
@dbus.service.method(_interface, out_signature="ao")
2706
2925
def GetAllClients(self):
2708
return dbus.Array(c.dbus_object_path
2927
return dbus.Array(c.dbus_object_path for c in
2710
2928
tcp_server.clients.itervalues())
2930
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2712
2932
@dbus.service.method(_interface,
2713
2933
out_signature="a{oa{sv}}")
2714
2934
def GetAllClientsWithProperties(self):
2716
2936
return dbus.Dictionary(
2717
((c.dbus_object_path, c.GetAll(""))
2718
for c in tcp_server.clients.itervalues()),
2937
{ c.dbus_object_path: c.GetAll(
2938
"se.recompile.Mandos.Client")
2939
for c in tcp_server.clients.itervalues() },
2719
2940
signature="oa{sv}")
2721
2942
@dbus.service.method(_interface, in_signature="o")
2725
2946
if c.dbus_object_path == object_path:
2726
2947
del tcp_server.clients[c.name]
2727
2948
c.remove_from_connection()
2728
# Don't signal anything except ClientRemoved
2949
# Don't signal the disabling
2729
2950
c.disable(quiet=True)
2731
self.ClientRemoved(object_path, c.name)
2951
# Emit D-Bus signal for removal
2952
self.client_removed_signal(c)
2733
2954
raise KeyError(object_path)
2958
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
2959
out_signature = "a{oa{sa{sv}}}")
2960
def GetManagedObjects(self):
2962
return dbus.Dictionary(
2963
{ client.dbus_object_path:
2965
{ interface: client.GetAll(interface)
2967
client._get_all_interface_names()})
2968
for client in tcp_server.clients.values()})
2970
def client_added_signal(self, client):
2971
"""Send the new standard signal and the old signal"""
2973
# New standard signal
2974
self.InterfacesAdded(
2975
client.dbus_object_path,
2977
{ interface: client.GetAll(interface)
2979
client._get_all_interface_names()}))
2981
self.ClientAdded(client.dbus_object_path)
2983
def client_removed_signal(self, client):
2984
"""Send the new standard signal and the old signal"""
2986
# New standard signal
2987
self.InterfacesRemoved(
2988
client.dbus_object_path,
2989
client._get_all_interface_names())
2991
self.ClientRemoved(client.dbus_object_path,
2737
2994
mandos_dbus_service = MandosDBusService()
2740
2997
"Cleanup function; run on exit"
2743
3001
multiprocessing.active_children()
2774
3032
del client_settings[client.name]["secret"]
2777
with (tempfile.NamedTemporaryFile
2778
(mode='wb', suffix=".pickle", prefix='clients-',
2779
dir=os.path.dirname(stored_state_path),
2780
delete=False)) as stored_state:
3035
with tempfile.NamedTemporaryFile(
3039
dir=os.path.dirname(stored_state_path),
3040
delete=False) as stored_state:
2781
3041
pickle.dump((clients, client_settings), stored_state)
2782
tempname=stored_state.name
3042
tempname = stored_state.name
2783
3043
os.rename(tempname, stored_state_path)
2784
3044
except (IOError, OSError) as e: