80
85
except ImportError:
81
86
SO_BINDTODEVICE = None
86
#logger = logging.getLogger(u'mandos')
87
logger = logging.Logger(u'mandos')
89
stored_state_file = "clients.pickle"
91
logger = logging.getLogger()
88
92
syslogger = (logging.handlers.SysLogHandler
89
93
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
90
address = "/dev/log"))
91
syslogger.setFormatter(logging.Formatter
92
(u'Mandos [%(process)d]: %(levelname)s:'
94
logger.addHandler(syslogger)
96
console = logging.StreamHandler()
97
console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
100
logger.addHandler(console)
94
address = str("/dev/log")))
97
if_nametoindex = (ctypes.cdll.LoadLibrary
98
(ctypes.util.find_library("c"))
100
except (OSError, AttributeError):
101
def if_nametoindex(interface):
102
"Get an interface index the hard way, i.e. using fcntl()"
103
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
104
with contextlib.closing(socket.socket()) as s:
105
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
106
struct.pack(str("16s16x"),
108
interface_index = struct.unpack(str("I"),
110
return interface_index
113
def initlogger(debug, level=logging.WARNING):
114
"""init logger and add loglevel"""
116
syslogger.setFormatter(logging.Formatter
117
('Mandos [%(process)d]: %(levelname)s:'
119
logger.addHandler(syslogger)
122
console = logging.StreamHandler()
123
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
127
logger.addHandler(console)
128
logger.setLevel(level)
131
class PGPError(Exception):
132
"""Exception if encryption/decryption fails"""
136
class PGPEngine(object):
137
"""A simple class for OpenPGP symmetric encryption & decryption"""
139
self.gnupg = GnuPGInterface.GnuPG()
140
self.tempdir = tempfile.mkdtemp(prefix="mandos-")
141
self.gnupg = GnuPGInterface.GnuPG()
142
self.gnupg.options.meta_interactive = False
143
self.gnupg.options.homedir = self.tempdir
144
self.gnupg.options.extra_args.extend(['--force-mdc',
151
def __exit__ (self, exc_type, exc_value, traceback):
159
if self.tempdir is not None:
160
# Delete contents of tempdir
161
for root, dirs, files in os.walk(self.tempdir,
163
for filename in files:
164
os.remove(os.path.join(root, filename))
166
os.rmdir(os.path.join(root, dirname))
168
os.rmdir(self.tempdir)
171
def password_encode(self, password):
172
# Passphrase can not be empty and can not contain newlines or
173
# NUL bytes. So we prefix it and hex encode it.
174
return b"mandos" + binascii.hexlify(password)
176
def encrypt(self, data, password):
177
self.gnupg.passphrase = self.password_encode(password)
178
with open(os.devnull) as devnull:
180
proc = self.gnupg.run(['--symmetric'],
181
create_fhs=['stdin', 'stdout'],
182
attach_fhs={'stderr': devnull})
183
with contextlib.closing(proc.handles['stdin']) as f:
185
with contextlib.closing(proc.handles['stdout']) as f:
186
ciphertext = f.read()
190
self.gnupg.passphrase = None
193
def decrypt(self, data, password):
194
self.gnupg.passphrase = self.password_encode(password)
195
with open(os.devnull) as devnull:
197
proc = self.gnupg.run(['--decrypt'],
198
create_fhs=['stdin', 'stdout'],
199
attach_fhs={'stderr': devnull})
200
with contextlib.closing(proc.handles['stdin'] ) as f:
202
with contextlib.closing(proc.handles['stdout']) as f:
203
decrypted_plaintext = f.read()
207
self.gnupg.passphrase = None
208
return decrypted_plaintext
102
212
class AvahiError(Exception):
103
213
def __init__(self, value, *args, **kwargs):
150
260
self.group = None # our entry group
151
261
self.server = None
263
self.entry_group_state_changed_match = None
153
264
def rename(self):
154
265
"""Derived from the Avahi example code"""
155
266
if self.rename_count >= self.max_renames:
156
logger.critical(u"No suitable Zeroconf service name found"
157
u" after %i retries, exiting.",
267
logger.critical("No suitable Zeroconf service name found"
268
" after %i retries, exiting.",
158
269
self.rename_count)
159
raise AvahiServiceError(u"Too many renames")
160
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
161
logger.info(u"Changing Zeroconf service name to %r ...",
270
raise AvahiServiceError("Too many renames")
271
self.name = unicode(self.server
272
.GetAlternativeServiceName(self.name))
273
logger.info("Changing Zeroconf service name to %r ...",
163
syslogger.setFormatter(logging.Formatter
164
(u'Mandos (%s) [%%(process)d]:'
165
u' %%(levelname)s: %%(message)s'
170
except dbus.exceptions.DBusException, error:
171
logger.critical(u"DBusException: %s", error)
278
except dbus.exceptions.DBusException as error:
279
logger.critical("DBusException: %s", error)
174
282
self.rename_count += 1
175
283
def remove(self):
176
284
"""Derived from the Avahi example code"""
285
if self.entry_group_state_changed_match is not None:
286
self.entry_group_state_changed_match.remove()
287
self.entry_group_state_changed_match = None
177
288
if self.group is not None:
178
289
self.group.Reset()
180
291
"""Derived from the Avahi example code"""
181
293
if self.group is None:
182
294
self.group = dbus.Interface(
183
295
self.bus.get_object(avahi.DBUS_NAME,
184
296
self.server.EntryGroupNew()),
185
297
avahi.DBUS_INTERFACE_ENTRY_GROUP)
186
self.group.connect_to_signal('StateChanged',
188
.entry_group_state_changed)
189
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
298
self.entry_group_state_changed_match = (
299
self.group.connect_to_signal(
300
'StateChanged', self.entry_group_state_changed))
301
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
190
302
self.name, self.type)
191
303
self.group.AddService(
199
311
self.group.Commit()
200
312
def entry_group_state_changed(self, state, error):
201
313
"""Derived from the Avahi example code"""
202
logger.debug(u"Avahi entry group state change: %i", state)
314
logger.debug("Avahi entry group state change: %i", state)
204
316
if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
logger.debug(u"Zeroconf service established.")
317
logger.debug("Zeroconf service established.")
206
318
elif state == avahi.ENTRY_GROUP_COLLISION:
207
logger.warning(u"Zeroconf service name collision.")
319
logger.info("Zeroconf service name collision.")
209
321
elif state == avahi.ENTRY_GROUP_FAILURE:
210
logger.critical(u"Avahi: Error in group state changed %s",
322
logger.critical("Avahi: Error in group state changed %s",
212
raise AvahiGroupError(u"State changed: %s"
324
raise AvahiGroupError("State changed: %s"
213
325
% unicode(error))
214
326
def cleanup(self):
215
327
"""Derived from the Avahi example code"""
216
328
if self.group is not None:
331
except (dbus.exceptions.UnknownMethodException,
332
dbus.exceptions.DBusException):
218
334
self.group = None
219
def server_state_changed(self, state):
336
def server_state_changed(self, state, error=None):
220
337
"""Derived from the Avahi example code"""
221
logger.debug(u"Avahi server state change: %i", state)
222
if state == avahi.SERVER_COLLISION:
223
logger.error(u"Zeroconf server name collision")
338
logger.debug("Avahi server state change: %i", state)
339
bad_states = { avahi.SERVER_INVALID:
340
"Zeroconf server invalid",
341
avahi.SERVER_REGISTERING: None,
342
avahi.SERVER_COLLISION:
343
"Zeroconf server name collision",
344
avahi.SERVER_FAILURE:
345
"Zeroconf server failure" }
346
if state in bad_states:
347
if bad_states[state] is not None:
349
logger.error(bad_states[state])
351
logger.error(bad_states[state] + ": %r", error)
225
353
elif state == avahi.SERVER_RUNNING:
357
logger.debug("Unknown state: %r", state)
359
logger.debug("Unknown state: %r: %r", state, error)
227
360
def activate(self):
228
361
"""Derived from the Avahi example code"""
229
362
if self.server is None:
230
363
self.server = dbus.Interface(
231
364
self.bus.get_object(avahi.DBUS_NAME,
232
avahi.DBUS_PATH_SERVER),
365
avahi.DBUS_PATH_SERVER,
366
follow_name_owner_changes=True),
233
367
avahi.DBUS_INTERFACE_SERVER)
234
self.server.connect_to_signal(u"StateChanged",
368
self.server.connect_to_signal("StateChanged",
235
369
self.server_state_changed)
236
370
self.server_state_changed(self.server.GetState())
372
class AvahiServiceToSyslog(AvahiService):
374
"""Add the new name to the syslog messages"""
375
ret = AvahiService.rename(self)
376
syslogger.setFormatter(logging.Formatter
377
('Mandos (%s) [%%(process)d]:'
378
' %%(levelname)s: %%(message)s'
382
def timedelta_to_milliseconds(td):
383
"Convert a datetime.timedelta() to milliseconds"
384
return ((td.days * 24 * 60 * 60 * 1000)
385
+ (td.seconds * 1000)
386
+ (td.microseconds // 1000))
239
388
class Client(object):
240
389
"""A representation of a client host served by this server.
243
_approved: bool(); 'None' if not yet approved/disapproved
392
approved: bool(); 'None' if not yet approved/disapproved
244
393
approval_delay: datetime.timedelta(); Time to wait for approval
245
394
approval_duration: datetime.timedelta(); Duration of one approval
246
395
checker: subprocess.Popen(); a running checker process used
263
413
interval: datetime.timedelta(); How often to start a new checker
264
414
last_approval_request: datetime.datetime(); (UTC) or None
265
415
last_checked_ok: datetime.datetime(); (UTC) or None
266
last_enabled: datetime.datetime(); (UTC)
416
last_checker_status: integer between 0 and 255 reflecting exit
417
status of last checker. -1 reflects crashed
418
checker, -2 means no checker completed yet.
419
last_enabled: datetime.datetime(); (UTC) or None
267
420
name: string; from the config file, used in log messages and
268
421
D-Bus identifiers
269
422
secret: bytestring; sent verbatim (over TLS) to client
270
423
timeout: datetime.timedelta(); How long from last_checked_ok
271
424
until this client is disabled
425
extended_timeout: extra long timeout when secret has been sent
272
426
runtime_expansions: Allowed attributes for runtime expansion.
427
expires: datetime.datetime(); time (UTC) when a client will be
275
runtime_expansions = (u"approval_delay", u"approval_duration",
276
u"created", u"enabled", u"fingerprint",
277
u"host", u"interval", u"last_checked_ok",
278
u"last_enabled", u"name", u"timeout")
281
def _timedelta_to_milliseconds(td):
282
"Convert a datetime.timedelta() to milliseconds"
283
return ((td.days * 24 * 60 * 60 * 1000)
284
+ (td.seconds * 1000)
285
+ (td.microseconds // 1000))
431
runtime_expansions = ("approval_delay", "approval_duration",
432
"created", "enabled", "fingerprint",
433
"host", "interval", "last_checked_ok",
434
"last_enabled", "name", "timeout")
435
client_defaults = { "timeout": "5m",
436
"extended_timeout": "15m",
438
"checker": "fping -q -- %%(host)s",
440
"approval_delay": "0s",
441
"approval_duration": "1s",
442
"approved_by_default": "True",
287
446
def timeout_milliseconds(self):
288
447
"Return the 'timeout' attribute in milliseconds"
289
return self._timedelta_to_milliseconds(self.timeout)
448
return timedelta_to_milliseconds(self.timeout)
450
def extended_timeout_milliseconds(self):
451
"Return the 'extended_timeout' attribute in milliseconds"
452
return timedelta_to_milliseconds(self.extended_timeout)
291
454
def interval_milliseconds(self):
292
455
"Return the 'interval' attribute in milliseconds"
293
return self._timedelta_to_milliseconds(self.interval)
456
return timedelta_to_milliseconds(self.interval)
295
458
def approval_delay_milliseconds(self):
296
return self._timedelta_to_milliseconds(self.approval_delay)
298
def __init__(self, name = None, disable_hook=None, config=None):
459
return timedelta_to_milliseconds(self.approval_delay)
462
def config_parser(config):
463
"""Construct a new dict of client settings of this form:
464
{ client_name: {setting_name: value, ...}, ...}
465
with exceptions for any special settings as defined above.
466
NOTE: Must be a pure function. Must return the same result
467
value given the same arguments.
470
for client_name in config.sections():
471
section = dict(config.items(client_name))
472
client = settings[client_name] = {}
474
client["host"] = section["host"]
475
# Reformat values from string types to Python types
476
client["approved_by_default"] = config.getboolean(
477
client_name, "approved_by_default")
478
client["enabled"] = config.getboolean(client_name,
481
client["fingerprint"] = (section["fingerprint"].upper()
483
if "secret" in section:
484
client["secret"] = section["secret"].decode("base64")
485
elif "secfile" in section:
486
with open(os.path.expanduser(os.path.expandvars
487
(section["secfile"])),
489
client["secret"] = secfile.read()
491
raise TypeError("No secret or secfile for section %s"
493
client["timeout"] = string_to_delta(section["timeout"])
494
client["extended_timeout"] = string_to_delta(
495
section["extended_timeout"])
496
client["interval"] = string_to_delta(section["interval"])
497
client["approval_delay"] = string_to_delta(
498
section["approval_delay"])
499
client["approval_duration"] = string_to_delta(
500
section["approval_duration"])
501
client["checker_command"] = section["checker"]
502
client["last_approval_request"] = None
503
client["last_checked_ok"] = None
504
client["last_checker_status"] = -2
509
def __init__(self, settings, name = None):
299
510
"""Note: the 'checker' key in 'config' sets the
300
511
'checker_command' attribute and *not* the 'checker'
305
logger.debug(u"Creating client %r", self.name)
514
# adding all client settings
515
for setting, value in settings.iteritems():
516
setattr(self, setting, value)
519
if not hasattr(self, "last_enabled"):
520
self.last_enabled = datetime.datetime.utcnow()
521
if not hasattr(self, "expires"):
522
self.expires = (datetime.datetime.utcnow()
525
self.last_enabled = None
528
logger.debug("Creating client %r", self.name)
306
529
# Uppercase and remove spaces from fingerprint for later
307
530
# comparison purposes with return value from the fingerprint()
309
self.fingerprint = (config[u"fingerprint"].upper()
311
logger.debug(u" Fingerprint: %s", self.fingerprint)
312
if u"secret" in config:
313
self.secret = config[u"secret"].decode(u"base64")
314
elif u"secfile" in config:
315
with open(os.path.expanduser(os.path.expandvars
316
(config[u"secfile"])),
318
self.secret = secfile.read()
320
raise TypeError(u"No secret or secfile for client %s"
322
self.host = config.get(u"host", u"")
323
self.created = datetime.datetime.utcnow()
325
self.last_approval_request = None
326
self.last_enabled = None
327
self.last_checked_ok = None
328
self.timeout = string_to_delta(config[u"timeout"])
329
self.interval = string_to_delta(config[u"interval"])
330
self.disable_hook = disable_hook
532
logger.debug(" Fingerprint: %s", self.fingerprint)
533
self.created = settings.get("created",
534
datetime.datetime.utcnow())
536
# attributes specific for this server instance
331
537
self.checker = None
332
538
self.checker_initiator_tag = None
333
539
self.disable_initiator_tag = None
334
540
self.checker_callback_tag = None
335
self.checker_command = config[u"checker"]
336
541
self.current_checker_command = None
337
self.last_connect = None
338
self._approved = None
339
self.approved_by_default = config.get(u"approved_by_default",
341
543
self.approvals_pending = 0
342
self.approval_delay = string_to_delta(
343
config[u"approval_delay"])
344
self.approval_duration = string_to_delta(
345
config[u"approval_duration"])
346
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
544
self.changedstate = (multiprocessing_manager
545
.Condition(multiprocessing_manager
547
self.client_structure = [attr for attr in
548
self.__dict__.iterkeys()
549
if not attr.startswith("_")]
550
self.client_structure.append("client_structure")
552
for name, t in inspect.getmembers(type(self),
556
if not name.startswith("_"):
557
self.client_structure.append(name)
559
# Send notice to process children that client state has changed
348
560
def send_changedstate(self):
349
self.changedstate.acquire()
350
self.changedstate.notify_all()
351
self.changedstate.release()
561
with self.changedstate:
562
self.changedstate.notify_all()
353
564
def enable(self):
354
565
"""Start this client's checker and timeout hooks"""
355
if getattr(self, u"enabled", False):
566
if getattr(self, "enabled", False):
356
567
# Already enabled
358
569
self.send_changedstate()
570
self.expires = datetime.datetime.utcnow() + self.timeout
359
572
self.last_enabled = datetime.datetime.utcnow()
575
def disable(self, quiet=True):
576
"""Disable this client."""
577
if not getattr(self, "enabled", False):
580
self.send_changedstate()
582
logger.info("Disabling client %s", self.name)
583
if getattr(self, "disable_initiator_tag", False):
584
gobject.source_remove(self.disable_initiator_tag)
585
self.disable_initiator_tag = None
587
if getattr(self, "checker_initiator_tag", False):
588
gobject.source_remove(self.checker_initiator_tag)
589
self.checker_initiator_tag = None
592
# Do not run this again if called by a gobject.timeout_add
598
def init_checker(self):
360
599
# Schedule a new checker to be started an 'interval' from now,
361
600
# and every interval from then on.
362
601
self.checker_initiator_tag = (gobject.timeout_add
366
605
self.disable_initiator_tag = (gobject.timeout_add
367
606
(self.timeout_milliseconds(),
370
608
# Also start a new checker *right now*.
371
609
self.start_checker()
373
def disable(self, quiet=True):
374
"""Disable this client."""
375
if not getattr(self, "enabled", False):
378
self.send_changedstate()
380
logger.info(u"Disabling client %s", self.name)
381
if getattr(self, u"disable_initiator_tag", False):
382
gobject.source_remove(self.disable_initiator_tag)
383
self.disable_initiator_tag = None
384
if getattr(self, u"checker_initiator_tag", False):
385
gobject.source_remove(self.checker_initiator_tag)
386
self.checker_initiator_tag = None
388
if self.disable_hook:
389
self.disable_hook(self)
391
# Do not run this again if called by a gobject.timeout_add
395
self.disable_hook = None
398
611
def checker_callback(self, pid, condition, command):
399
612
"""The checker has completed, so take appropriate actions."""
400
613
self.checker_callback_tag = None
401
614
self.checker = None
402
615
if os.WIFEXITED(condition):
403
exitstatus = os.WEXITSTATUS(condition)
405
logger.info(u"Checker for %(name)s succeeded",
616
self.last_checker_status = os.WEXITSTATUS(condition)
617
if self.last_checker_status == 0:
618
logger.info("Checker for %(name)s succeeded",
407
620
self.checked_ok()
409
logger.info(u"Checker for %(name)s failed",
622
logger.info("Checker for %(name)s failed",
412
logger.warning(u"Checker for %(name)s crashed?",
625
self.last_checker_status = -1
626
logger.warning("Checker for %(name)s crashed?",
415
629
def checked_ok(self):
416
"""Bump up the timeout for this client.
418
This should only be called when the client has been seen,
630
"""Assert that the client has been seen, alive and well."""
421
631
self.last_checked_ok = datetime.datetime.utcnow()
422
gobject.source_remove(self.disable_initiator_tag)
423
self.disable_initiator_tag = (gobject.timeout_add
424
(self.timeout_milliseconds(),
632
self.last_checker_status = 0
635
def bump_timeout(self, timeout=None):
636
"""Bump up the timeout for this client."""
638
timeout = self.timeout
639
if self.disable_initiator_tag is not None:
640
gobject.source_remove(self.disable_initiator_tag)
641
if getattr(self, "enabled", False):
642
self.disable_initiator_tag = (gobject.timeout_add
643
(timedelta_to_milliseconds
644
(timeout), self.disable))
645
self.expires = datetime.datetime.utcnow() + timeout
427
647
def need_approval(self):
428
648
self.last_approval_request = datetime.datetime.utcnow()
581
802
def _is_dbus_property(obj):
582
return getattr(obj, u"_dbus_is_property", False)
803
return getattr(obj, "_dbus_is_property", False)
584
805
def _get_all_dbus_properties(self):
585
806
"""Returns a generator of (name, attribute) pairs
587
return ((prop._dbus_name, prop)
808
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
809
for cls in self.__class__.__mro__
588
810
for name, prop in
589
inspect.getmembers(self, self._is_dbus_property))
811
inspect.getmembers(cls, self._is_dbus_property))
591
813
def _get_dbus_property(self, interface_name, property_name):
592
814
"""Returns a bound method if one exists which is a D-Bus
593
815
property with the specified name and interface.
595
for name in (property_name,
596
property_name + u"_dbus_property"):
597
prop = getattr(self, name, None)
599
or not self._is_dbus_property(prop)
600
or prop._dbus_name != property_name
601
or (interface_name and prop._dbus_interface
602
and interface_name != prop._dbus_interface)):
817
for cls in self.__class__.__mro__:
818
for name, value in (inspect.getmembers
819
(cls, self._is_dbus_property)):
820
if (value._dbus_name == property_name
821
and value._dbus_interface == interface_name):
822
return value.__get__(self)
605
824
# No such property
606
raise DBusPropertyNotFound(self.dbus_object_path + u":"
607
+ interface_name + u"."
825
raise DBusPropertyNotFound(self.dbus_object_path + ":"
826
+ interface_name + "."
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
829
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
612
831
def Get(self, interface_name, property_name):
613
832
"""Standard D-Bus property Get() method, see D-Bus standard.
615
834
prop = self._get_dbus_property(interface_name, property_name)
616
if prop._dbus_access == u"write":
835
if prop._dbus_access == "write":
617
836
raise DBusPropertyAccessException(property_name)
619
if not hasattr(value, u"variant_level"):
838
if not hasattr(value, "variant_level"):
621
840
return type(value)(value, variant_level=value.variant_level+1)
623
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
842
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
624
843
def Set(self, interface_name, property_name, value):
625
844
"""Standard D-Bus property Set() method, see D-Bus standard.
627
846
prop = self._get_dbus_property(interface_name, property_name)
628
if prop._dbus_access == u"read":
847
if prop._dbus_access == "read":
629
848
raise DBusPropertyAccessException(property_name)
630
if prop._dbus_get_args_options[u"byte_arrays"]:
849
if prop._dbus_get_args_options["byte_arrays"]:
631
850
# The byte_arrays option is not supported yet on
632
851
# signatures other than "ay".
633
if prop._dbus_signature != u"ay":
852
if prop._dbus_signature != "ay":
635
value = dbus.ByteArray(''.join(unichr(byte)
854
value = dbus.ByteArray(b''.join(chr(byte)
639
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
640
out_signature=u"a{sv}")
858
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
859
out_signature="a{sv}")
641
860
def GetAll(self, interface_name):
642
861
"""Standard D-Bus property GetAll() method, see D-Bus
645
864
Note: Will not include properties with access="write".
648
867
for name, prop in self._get_all_dbus_properties():
649
868
if (interface_name
650
869
and interface_name != prop._dbus_interface):
651
870
# Interface non-empty but did not match
653
872
# Ignore write-only properties
654
if prop._dbus_access == u"write":
873
if prop._dbus_access == "write":
657
if not hasattr(value, u"variant_level"):
876
if not hasattr(value, "variant_level"):
877
properties[name] = value
660
all[name] = type(value)(value, variant_level=
661
value.variant_level+1)
662
return dbus.Dictionary(all, signature=u"sv")
879
properties[name] = type(value)(value, variant_level=
880
value.variant_level+1)
881
return dbus.Dictionary(properties, signature="sv")
664
883
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
666
885
path_keyword='object_path',
667
886
connection_keyword='connection')
668
887
def Introspect(self, object_path, connection):
674
893
document = xml.dom.minidom.parseString(xmlstring)
675
894
def make_tag(document, name, prop):
676
e = document.createElement(u"property")
677
e.setAttribute(u"name", name)
678
e.setAttribute(u"type", prop._dbus_signature)
679
e.setAttribute(u"access", prop._dbus_access)
895
e = document.createElement("property")
896
e.setAttribute("name", name)
897
e.setAttribute("type", prop._dbus_signature)
898
e.setAttribute("access", prop._dbus_access)
681
for if_tag in document.getElementsByTagName(u"interface"):
900
for if_tag in document.getElementsByTagName("interface"):
682
901
for tag in (make_tag(document, name, prop)
684
903
in self._get_all_dbus_properties()
685
904
if prop._dbus_interface
686
== if_tag.getAttribute(u"name")):
905
== if_tag.getAttribute("name")):
687
906
if_tag.appendChild(tag)
688
907
# Add the names to the return values for the
689
908
# "org.freedesktop.DBus.Properties" methods
690
if (if_tag.getAttribute(u"name")
691
== u"org.freedesktop.DBus.Properties"):
692
for cn in if_tag.getElementsByTagName(u"method"):
693
if cn.getAttribute(u"name") == u"Get":
694
for arg in cn.getElementsByTagName(u"arg"):
695
if (arg.getAttribute(u"direction")
697
arg.setAttribute(u"name", u"value")
698
elif cn.getAttribute(u"name") == u"GetAll":
699
for arg in cn.getElementsByTagName(u"arg"):
700
if (arg.getAttribute(u"direction")
702
arg.setAttribute(u"name", u"props")
703
xmlstring = document.toxml(u"utf-8")
909
if (if_tag.getAttribute("name")
910
== "org.freedesktop.DBus.Properties"):
911
for cn in if_tag.getElementsByTagName("method"):
912
if cn.getAttribute("name") == "Get":
913
for arg in cn.getElementsByTagName("arg"):
914
if (arg.getAttribute("direction")
916
arg.setAttribute("name", "value")
917
elif cn.getAttribute("name") == "GetAll":
918
for arg in cn.getElementsByTagName("arg"):
919
if (arg.getAttribute("direction")
921
arg.setAttribute("name", "props")
922
xmlstring = document.toxml("utf-8")
704
923
document.unlink()
705
924
except (AttributeError, xml.dom.DOMException,
706
xml.parsers.expat.ExpatError), error:
707
logger.error(u"Failed to override Introspection method",
925
xml.parsers.expat.ExpatError) as error:
926
logger.error("Failed to override Introspection method",
931
def datetime_to_dbus (dt, variant_level=0):
932
"""Convert a UTC datetime.datetime() to a D-Bus type."""
934
return dbus.String("", variant_level = variant_level)
935
return dbus.String(dt.isoformat(),
936
variant_level=variant_level)
939
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
941
"""Applied to an empty subclass of a D-Bus object, this metaclass
942
will add additional D-Bus attributes matching a certain pattern.
944
def __new__(mcs, name, bases, attr):
945
# Go through all the base classes which could have D-Bus
946
# methods, signals, or properties in them
947
for base in (b for b in bases
948
if issubclass(b, dbus.service.Object)):
949
# Go though all attributes of the base class
950
for attrname, attribute in inspect.getmembers(base):
951
# Ignore non-D-Bus attributes, and D-Bus attributes
952
# with the wrong interface name
953
if (not hasattr(attribute, "_dbus_interface")
954
or not attribute._dbus_interface
955
.startswith("se.recompile.Mandos")):
957
# Create an alternate D-Bus interface name based on
959
alt_interface = (attribute._dbus_interface
960
.replace("se.recompile.Mandos",
961
"se.bsnet.fukt.Mandos"))
962
# Is this a D-Bus signal?
963
if getattr(attribute, "_dbus_is_signal", False):
964
# Extract the original non-method function by
966
nonmethod_func = (dict(
967
zip(attribute.func_code.co_freevars,
968
attribute.__closure__))["func"]
970
# Create a new, but exactly alike, function
971
# object, and decorate it to be a new D-Bus signal
972
# with the alternate D-Bus interface name
973
new_function = (dbus.service.signal
975
attribute._dbus_signature)
977
nonmethod_func.func_code,
978
nonmethod_func.func_globals,
979
nonmethod_func.func_name,
980
nonmethod_func.func_defaults,
981
nonmethod_func.func_closure)))
982
# Define a creator of a function to call both the
983
# old and new functions, so both the old and new
984
# signals gets sent when the function is called
985
def fixscope(func1, func2):
986
"""This function is a scope container to pass
987
func1 and func2 to the "call_both" function
988
outside of its arguments"""
989
def call_both(*args, **kwargs):
990
"""This function will emit two D-Bus
991
signals by calling func1 and func2"""
992
func1(*args, **kwargs)
993
func2(*args, **kwargs)
995
# Create the "call_both" function and add it to
997
attr[attrname] = fixscope(attribute,
999
# Is this a D-Bus method?
1000
elif getattr(attribute, "_dbus_is_method", False):
1001
# Create a new, but exactly alike, function
1002
# object. Decorate it to be a new D-Bus method
1003
# with the alternate D-Bus interface name. Add it
1005
attr[attrname] = (dbus.service.method
1007
attribute._dbus_in_signature,
1008
attribute._dbus_out_signature)
1010
(attribute.func_code,
1011
attribute.func_globals,
1012
attribute.func_name,
1013
attribute.func_defaults,
1014
attribute.func_closure)))
1015
# Is this a D-Bus property?
1016
elif getattr(attribute, "_dbus_is_property", False):
1017
# Create a new, but exactly alike, function
1018
# object, and decorate it to be a new D-Bus
1019
# property with the alternate D-Bus interface
1020
# name. Add it to the class.
1021
attr[attrname] = (dbus_service_property
1023
attribute._dbus_signature,
1024
attribute._dbus_access,
1026
._dbus_get_args_options
1029
(attribute.func_code,
1030
attribute.func_globals,
1031
attribute.func_name,
1032
attribute.func_defaults,
1033
attribute.func_closure)))
1034
return type.__new__(mcs, name, bases, attr)
712
1037
class ClientDBus(Client, DBusObjectWithProperties):
713
1038
"""A Client class using D-Bus
720
1045
runtime_expansions = (Client.runtime_expansions
721
+ (u"dbus_object_path",))
1046
+ ("dbus_object_path",))
723
1048
# dbus.service.Object doesn't use super(), so we can't either.
725
1050
def __init__(self, bus = None, *args, **kwargs):
726
self._approvals_pending = 0
728
1052
Client.__init__(self, *args, **kwargs)
729
1053
# Only now, when this client is initialized, can it show up on
731
1055
client_object_name = unicode(self.name).translate(
732
{ord(u"."): ord(u"_"),
733
ord(u"-"): ord(u"_")})
1056
{ord("."): ord("_"),
1057
ord("-"): ord("_")})
734
1058
self.dbus_object_path = (dbus.ObjectPath
735
(u"/clients/" + client_object_name))
1059
("/clients/" + client_object_name))
736
1060
DBusObjectWithProperties.__init__(self, self.bus,
737
1061
self.dbus_object_path)
739
def _get_approvals_pending(self):
740
return self._approvals_pending
741
def _set_approvals_pending(self, value):
742
old_value = self._approvals_pending
743
self._approvals_pending = value
745
if (hasattr(self, "dbus_object_path")
746
and bval is not bool(old_value)):
747
dbus_bool = dbus.Boolean(bval, variant_level=1)
748
self.PropertyChanged(dbus.String(u"ApprovalPending"),
751
approvals_pending = property(_get_approvals_pending,
752
_set_approvals_pending)
753
del _get_approvals_pending, _set_approvals_pending
756
def _datetime_to_dbus(dt, variant_level=0):
757
"""Convert a UTC datetime.datetime() to a D-Bus type."""
758
return dbus.String(dt.isoformat(),
759
variant_level=variant_level)
762
oldstate = getattr(self, u"enabled", False)
763
r = Client.enable(self)
764
if oldstate != self.enabled:
766
self.PropertyChanged(dbus.String(u"Enabled"),
767
dbus.Boolean(True, variant_level=1))
768
self.PropertyChanged(
769
dbus.String(u"LastEnabled"),
770
self._datetime_to_dbus(self.last_enabled,
774
def disable(self, quiet = False):
775
oldstate = getattr(self, u"enabled", False)
776
r = Client.disable(self, quiet=quiet)
777
if not quiet and oldstate != self.enabled:
779
self.PropertyChanged(dbus.String(u"Enabled"),
780
dbus.Boolean(False, variant_level=1))
1063
def notifychangeproperty(transform_func,
1064
dbus_name, type_func=lambda x: x,
1066
""" Modify a variable so that it's a property which announces
1067
its changes to DBus.
1069
transform_fun: Function that takes a value and a variant_level
1070
and transforms it to a D-Bus type.
1071
dbus_name: D-Bus name of the variable
1072
type_func: Function that transform the value before sending it
1073
to the D-Bus. Default: no transform
1074
variant_level: D-Bus variant level. Default: 1
1076
attrname = "_{0}".format(dbus_name)
1077
def setter(self, value):
1078
if hasattr(self, "dbus_object_path"):
1079
if (not hasattr(self, attrname) or
1080
type_func(getattr(self, attrname, None))
1081
!= type_func(value)):
1082
dbus_value = transform_func(type_func(value),
1085
self.PropertyChanged(dbus.String(dbus_name),
1087
setattr(self, attrname, value)
1089
return property(lambda self: getattr(self, attrname), setter)
1092
expires = notifychangeproperty(datetime_to_dbus, "Expires")
1093
approvals_pending = notifychangeproperty(dbus.Boolean,
1096
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1097
last_enabled = notifychangeproperty(datetime_to_dbus,
1099
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
1100
type_func = lambda checker:
1101
checker is not None)
1102
last_checked_ok = notifychangeproperty(datetime_to_dbus,
1104
last_checker_status = notifychangeproperty(dbus.Int16,
1105
"LastCheckerStatus")
1106
last_approval_request = notifychangeproperty(
1107
datetime_to_dbus, "LastApprovalRequest")
1108
approved_by_default = notifychangeproperty(dbus.Boolean,
1109
"ApprovedByDefault")
1110
approval_delay = notifychangeproperty(dbus.UInt64,
1113
timedelta_to_milliseconds)
1114
approval_duration = notifychangeproperty(
1115
dbus.UInt64, "ApprovalDuration",
1116
type_func = timedelta_to_milliseconds)
1117
host = notifychangeproperty(dbus.String, "Host")
1118
timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1120
timedelta_to_milliseconds)
1121
extended_timeout = notifychangeproperty(
1122
dbus.UInt64, "ExtendedTimeout",
1123
type_func = timedelta_to_milliseconds)
1124
interval = notifychangeproperty(dbus.UInt64,
1127
timedelta_to_milliseconds)
1128
checker_command = notifychangeproperty(dbus.String, "Checker")
1130
del notifychangeproperty
783
1132
def __del__(self, *args, **kwargs):
785
1134
self.remove_from_connection()
786
1135
except LookupError:
788
if hasattr(DBusObjectWithProperties, u"__del__"):
1137
if hasattr(DBusObjectWithProperties, "__del__"):
789
1138
DBusObjectWithProperties.__del__(self, *args, **kwargs)
790
1139
Client.__del__(self, *args, **kwargs)
841
1169
and old_checker_pid != self.checker.pid):
842
1170
# Emit D-Bus signal
843
1171
self.CheckerStarted(self.current_checker_command)
844
self.PropertyChanged(
845
dbus.String(u"CheckerRunning"),
846
dbus.Boolean(True, variant_level=1))
849
def stop_checker(self, *args, **kwargs):
850
old_checker = getattr(self, u"checker", None)
851
r = Client.stop_checker(self, *args, **kwargs)
852
if (old_checker is not None
853
and getattr(self, u"checker", None) is None):
854
self.PropertyChanged(dbus.String(u"CheckerRunning"),
855
dbus.Boolean(False, variant_level=1))
858
1174
def _reset_approved(self):
859
self._approved = None
1175
self.approved = None
862
1178
def approve(self, value=True):
863
1179
self.send_changedstate()
864
self._approved = value
865
gobject.timeout_add(self._timedelta_to_milliseconds
1180
self.approved = value
1181
gobject.timeout_add(timedelta_to_milliseconds
866
1182
(self.approval_duration),
867
1183
self._reset_approved)
870
1186
## D-Bus methods, signals & properties
871
_interface = u"se.bsnet.fukt.Mandos.Client"
1187
_interface = "se.recompile.Mandos.Client"
875
1191
# CheckerCompleted - signal
876
@dbus.service.signal(_interface, signature=u"nxs")
1192
@dbus.service.signal(_interface, signature="nxs")
877
1193
def CheckerCompleted(self, exitcode, waitstatus, command):
881
1197
# CheckerStarted - signal
882
@dbus.service.signal(_interface, signature=u"s")
1198
@dbus.service.signal(_interface, signature="s")
883
1199
def CheckerStarted(self, command):
887
1203
# PropertyChanged - signal
888
@dbus.service.signal(_interface, signature=u"sv")
1204
@dbus.service.signal(_interface, signature="sv")
889
1205
def PropertyChanged(self, property, value):
951
1267
# ApprovalPending - property
952
@dbus_service_property(_interface, signature=u"b", access=u"read")
1268
@dbus_service_property(_interface, signature="b", access="read")
953
1269
def ApprovalPending_dbus_property(self):
954
1270
return dbus.Boolean(bool(self.approvals_pending))
956
1272
# ApprovedByDefault - property
957
@dbus_service_property(_interface, signature=u"b",
1273
@dbus_service_property(_interface, signature="b",
959
1275
def ApprovedByDefault_dbus_property(self, value=None):
960
1276
if value is None: # get
961
1277
return dbus.Boolean(self.approved_by_default)
962
1278
self.approved_by_default = bool(value)
964
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
965
dbus.Boolean(value, variant_level=1))
967
1280
# ApprovalDelay - property
968
@dbus_service_property(_interface, signature=u"t",
1281
@dbus_service_property(_interface, signature="t",
970
1283
def ApprovalDelay_dbus_property(self, value=None):
971
1284
if value is None: # get
972
1285
return dbus.UInt64(self.approval_delay_milliseconds())
973
1286
self.approval_delay = datetime.timedelta(0, 0, 0, value)
975
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
976
dbus.UInt64(value, variant_level=1))
978
1288
# ApprovalDuration - property
979
@dbus_service_property(_interface, signature=u"t",
1289
@dbus_service_property(_interface, signature="t",
981
1291
def ApprovalDuration_dbus_property(self, value=None):
982
1292
if value is None: # get
983
return dbus.UInt64(self._timedelta_to_milliseconds(
1293
return dbus.UInt64(timedelta_to_milliseconds(
984
1294
self.approval_duration))
985
1295
self.approval_duration = datetime.timedelta(0, 0, 0, value)
987
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
988
dbus.UInt64(value, variant_level=1))
990
1297
# Name - property
991
@dbus_service_property(_interface, signature=u"s", access=u"read")
1298
@dbus_service_property(_interface, signature="s", access="read")
992
1299
def Name_dbus_property(self):
993
1300
return dbus.String(self.name)
995
1302
# Fingerprint - property
996
@dbus_service_property(_interface, signature=u"s", access=u"read")
1303
@dbus_service_property(_interface, signature="s", access="read")
997
1304
def Fingerprint_dbus_property(self):
998
1305
return dbus.String(self.fingerprint)
1000
1307
# Host - property
1001
@dbus_service_property(_interface, signature=u"s",
1002
access=u"readwrite")
1308
@dbus_service_property(_interface, signature="s",
1003
1310
def Host_dbus_property(self, value=None):
1004
1311
if value is None: # get
1005
1312
return dbus.String(self.host)
1008
self.PropertyChanged(dbus.String(u"Host"),
1009
dbus.String(value, variant_level=1))
1313
self.host = unicode(value)
1011
1315
# Created - property
1012
@dbus_service_property(_interface, signature=u"s", access=u"read")
1316
@dbus_service_property(_interface, signature="s", access="read")
1013
1317
def Created_dbus_property(self):
1014
return dbus.String(self._datetime_to_dbus(self.created))
1318
return datetime_to_dbus(self.created)
1016
1320
# LastEnabled - property
1017
@dbus_service_property(_interface, signature=u"s", access=u"read")
1321
@dbus_service_property(_interface, signature="s", access="read")
1018
1322
def LastEnabled_dbus_property(self):
1019
if self.last_enabled is None:
1020
return dbus.String(u"")
1021
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1323
return datetime_to_dbus(self.last_enabled)
1023
1325
# Enabled - property
1024
@dbus_service_property(_interface, signature=u"b",
1025
access=u"readwrite")
1326
@dbus_service_property(_interface, signature="b",
1026
1328
def Enabled_dbus_property(self, value=None):
1027
1329
if value is None: # get
1028
1330
return dbus.Boolean(self.enabled)
1034
1336
# LastCheckedOK - property
1035
@dbus_service_property(_interface, signature=u"s",
1036
access=u"readwrite")
1337
@dbus_service_property(_interface, signature="s",
1037
1339
def LastCheckedOK_dbus_property(self, value=None):
1038
1340
if value is not None:
1039
1341
self.checked_ok()
1041
if self.last_checked_ok is None:
1042
return dbus.String(u"")
1043
return dbus.String(self._datetime_to_dbus(self
1343
return datetime_to_dbus(self.last_checked_ok)
1345
# LastCheckerStatus - property
1346
@dbus_service_property(_interface, signature="n",
1348
def LastCheckerStatus_dbus_property(self):
1349
return dbus.Int16(self.last_checker_status)
1351
# Expires - property
1352
@dbus_service_property(_interface, signature="s", access="read")
1353
def Expires_dbus_property(self):
1354
return datetime_to_dbus(self.expires)
1046
1356
# LastApprovalRequest - property
1047
@dbus_service_property(_interface, signature=u"s", access=u"read")
1357
@dbus_service_property(_interface, signature="s", access="read")
1048
1358
def LastApprovalRequest_dbus_property(self):
1049
if self.last_approval_request is None:
1050
return dbus.String(u"")
1051
return dbus.String(self.
1052
_datetime_to_dbus(self
1053
.last_approval_request))
1359
return datetime_to_dbus(self.last_approval_request)
1055
1361
# Timeout - property
1056
@dbus_service_property(_interface, signature=u"t",
1057
access=u"readwrite")
1362
@dbus_service_property(_interface, signature="t",
1058
1364
def Timeout_dbus_property(self, value=None):
1059
1365
if value is None: # get
1060
1366
return dbus.UInt64(self.timeout_milliseconds())
1061
1367
self.timeout = datetime.timedelta(0, 0, 0, value)
1063
self.PropertyChanged(dbus.String(u"Timeout"),
1064
dbus.UInt64(value, variant_level=1))
1065
if getattr(self, u"disable_initiator_tag", None) is None:
1067
1368
# Reschedule timeout
1068
gobject.source_remove(self.disable_initiator_tag)
1069
self.disable_initiator_tag = None
1070
time_to_die = (self.
1071
_timedelta_to_milliseconds((self
1076
if time_to_die <= 0:
1077
# The timeout has passed
1080
self.disable_initiator_tag = (gobject.timeout_add
1081
(time_to_die, self.disable))
1370
now = datetime.datetime.utcnow()
1371
time_to_die = timedelta_to_milliseconds(
1372
(self.last_checked_ok + self.timeout) - now)
1373
if time_to_die <= 0:
1374
# The timeout has passed
1377
self.expires = (now +
1378
datetime.timedelta(milliseconds =
1380
if (getattr(self, "disable_initiator_tag", None)
1383
gobject.source_remove(self.disable_initiator_tag)
1384
self.disable_initiator_tag = (gobject.timeout_add
1388
# ExtendedTimeout - property
1389
@dbus_service_property(_interface, signature="t",
1391
def ExtendedTimeout_dbus_property(self, value=None):
1392
if value is None: # get
1393
return dbus.UInt64(self.extended_timeout_milliseconds())
1394
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1083
1396
# Interval - property
1084
@dbus_service_property(_interface, signature=u"t",
1085
access=u"readwrite")
1397
@dbus_service_property(_interface, signature="t",
1086
1399
def Interval_dbus_property(self, value=None):
1087
1400
if value is None: # get
1088
1401
return dbus.UInt64(self.interval_milliseconds())
1089
1402
self.interval = datetime.timedelta(0, 0, 0, value)
1091
self.PropertyChanged(dbus.String(u"Interval"),
1092
dbus.UInt64(value, variant_level=1))
1093
if getattr(self, u"checker_initiator_tag", None) is None:
1403
if getattr(self, "checker_initiator_tag", None) is None:
1095
# Reschedule checker run
1096
gobject.source_remove(self.checker_initiator_tag)
1097
self.checker_initiator_tag = (gobject.timeout_add
1098
(value, self.start_checker))
1099
self.start_checker() # Start one now, too
1406
# Reschedule checker run
1407
gobject.source_remove(self.checker_initiator_tag)
1408
self.checker_initiator_tag = (gobject.timeout_add
1409
(value, self.start_checker))
1410
self.start_checker() # Start one now, too
1101
1412
# Checker - property
1102
@dbus_service_property(_interface, signature=u"s",
1103
access=u"readwrite")
1413
@dbus_service_property(_interface, signature="s",
1104
1415
def Checker_dbus_property(self, value=None):
1105
1416
if value is None: # get
1106
1417
return dbus.String(self.checker_command)
1107
self.checker_command = value
1109
self.PropertyChanged(dbus.String(u"Checker"),
1110
dbus.String(self.checker_command,
1418
self.checker_command = unicode(value)
1113
1420
# CheckerRunning - property
1114
@dbus_service_property(_interface, signature=u"b",
1115
access=u"readwrite")
1421
@dbus_service_property(_interface, signature="b",
1116
1423
def CheckerRunning_dbus_property(self, value=None):
1117
1424
if value is None: # get
1118
1425
return dbus.Boolean(self.checker is not None)
1170
1481
def handle(self):
1171
1482
with contextlib.closing(self.server.child_pipe) as child_pipe:
1172
logger.info(u"TCP connection from: %s",
1483
logger.info("TCP connection from: %s",
1173
1484
unicode(self.client_address))
1174
logger.debug(u"Pipe FD: %d",
1485
logger.debug("Pipe FD: %d",
1175
1486
self.server.child_pipe.fileno())
1177
1488
session = (gnutls.connection
1178
1489
.ClientSession(self.request,
1179
1490
gnutls.connection
1180
1491
.X509Credentials()))
1182
1493
# Note: gnutls.connection.X509Credentials is really a
1183
1494
# generic GnuTLS certificate credentials object so long as
1184
1495
# no X.509 keys are added to it. Therefore, we can use it
1185
1496
# here despite using OpenPGP certificates.
1187
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1188
# u"+AES-256-CBC", u"+SHA1",
1189
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1498
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1499
# "+AES-256-CBC", "+SHA1",
1500
# "+COMP-NULL", "+CTYPE-OPENPGP",
1191
1502
# Use a fallback default, since this MUST be set.
1192
1503
priority = self.server.gnutls_priority
1193
1504
if priority is None:
1194
priority = u"NORMAL"
1195
1506
(gnutls.library.functions
1196
1507
.gnutls_priority_set_direct(session._c_object,
1197
1508
priority, None))
1199
1510
# Start communication using the Mandos protocol
1200
1511
# Get protocol number
1201
1512
line = self.request.makefile().readline()
1202
logger.debug(u"Protocol version: %r", line)
1513
logger.debug("Protocol version: %r", line)
1204
1515
if int(line.strip().split()[0]) > 1:
1205
1516
raise RuntimeError
1206
except (ValueError, IndexError, RuntimeError), error:
1207
logger.error(u"Unknown protocol version: %s", error)
1517
except (ValueError, IndexError, RuntimeError) as error:
1518
logger.error("Unknown protocol version: %s", error)
1210
1521
# Start GnuTLS connection
1212
1523
session.handshake()
1213
except gnutls.errors.GNUTLSError, error:
1214
logger.warning(u"Handshake failed: %s", error)
1524
except gnutls.errors.GNUTLSError as error:
1525
logger.warning("Handshake failed: %s", error)
1215
1526
# Do not run session.bye() here: the session is not
1216
1527
# established. Just abandon the request.
1218
logger.debug(u"Handshake succeeded")
1529
logger.debug("Handshake succeeded")
1220
1531
approval_required = False
1223
1534
fpr = self.fingerprint(self.peer_certificate
1225
except (TypeError, gnutls.errors.GNUTLSError), error:
1226
logger.warning(u"Bad certificate: %s", error)
1537
gnutls.errors.GNUTLSError) as error:
1538
logger.warning("Bad certificate: %s", error)
1228
logger.debug(u"Fingerprint: %s", fpr)
1540
logger.debug("Fingerprint: %s", fpr)
1231
1543
client = ProxyClient(child_pipe, fpr,
1232
1544
self.client_address)
1536
1860
fpr = request[1]
1537
1861
address = request[2]
1539
for c in self.clients:
1863
for c in self.clients.itervalues():
1540
1864
if c.fingerprint == fpr:
1544
logger.warning(u"Client not found for fingerprint: %s, ad"
1545
u"dress: %s", fpr, address)
1868
logger.info("Client not found for fingerprint: %s, ad"
1869
"dress: %s", fpr, address)
1546
1870
if self.use_dbus:
1547
1871
# Emit D-Bus signal
1548
mandos_dbus_service.ClientNotFound(fpr, address[0])
1872
mandos_dbus_service.ClientNotFound(fpr,
1549
1874
parent_pipe.send(False)
1552
1877
gobject.io_add_watch(parent_pipe.fileno(),
1553
1878
gobject.IO_IN | gobject.IO_HUP,
1554
1879
functools.partial(self.handle_ipc,
1555
parent_pipe = parent_pipe,
1556
client_object = client))
1557
1885
parent_pipe.send(True)
1558
# remove the old hook in favor of the new above hook on same fileno
1886
# remove the old hook in favor of the new above hook on
1560
1889
if command == 'funcall':
1561
1890
funcname = request[1]
1562
1891
args = request[2]
1563
1892
kwargs = request[3]
1565
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1894
parent_pipe.send(('data', getattr(client_object,
1567
1898
if command == 'getattr':
1568
1899
attrname = request[1]
1569
1900
if callable(client_object.__getattribute__(attrname)):
1570
1901
parent_pipe.send(('function',))
1572
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1903
parent_pipe.send(('data', client_object
1904
.__getattribute__(attrname)))
1574
1906
if command == 'setattr':
1575
1907
attrname = request[1]
1576
1908
value = request[2]
1577
1909
setattr(client_object, attrname, value)
1582
1914
def string_to_delta(interval):
1583
1915
"""Parse a string and return a datetime.timedelta
1585
>>> string_to_delta(u'7d')
1917
>>> string_to_delta('7d')
1586
1918
datetime.timedelta(7)
1587
>>> string_to_delta(u'60s')
1919
>>> string_to_delta('60s')
1588
1920
datetime.timedelta(0, 60)
1589
>>> string_to_delta(u'60m')
1921
>>> string_to_delta('60m')
1590
1922
datetime.timedelta(0, 3600)
1591
>>> string_to_delta(u'24h')
1923
>>> string_to_delta('24h')
1592
1924
datetime.timedelta(1)
1593
>>> string_to_delta(u'1w')
1925
>>> string_to_delta('1w')
1594
1926
datetime.timedelta(7)
1595
>>> string_to_delta(u'5m 30s')
1927
>>> string_to_delta('5m 30s')
1596
1928
datetime.timedelta(0, 330)
1598
1930
timevalue = datetime.timedelta(0)
1601
1933
suffix = unicode(s[-1])
1602
1934
value = int(s[:-1])
1604
1936
delta = datetime.timedelta(value)
1605
elif suffix == u"s":
1606
1938
delta = datetime.timedelta(0, value)
1607
elif suffix == u"m":
1608
1940
delta = datetime.timedelta(0, 0, 0, 0, value)
1609
elif suffix == u"h":
1610
1942
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1611
elif suffix == u"w":
1612
1944
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1614
raise ValueError(u"Unknown suffix %r" % suffix)
1615
except (ValueError, IndexError), e:
1616
raise ValueError(e.message)
1946
raise ValueError("Unknown suffix %r" % suffix)
1947
except (ValueError, IndexError) as e:
1948
raise ValueError(*(e.args))
1617
1949
timevalue += delta
1618
1950
return timevalue
1621
def if_nametoindex(interface):
1622
"""Call the C function if_nametoindex(), or equivalent
1624
Note: This function cannot accept a unicode string."""
1625
global if_nametoindex
1627
if_nametoindex = (ctypes.cdll.LoadLibrary
1628
(ctypes.util.find_library(u"c"))
1630
except (OSError, AttributeError):
1631
logger.warning(u"Doing if_nametoindex the hard way")
1632
def if_nametoindex(interface):
1633
"Get an interface index the hard way, i.e. using fcntl()"
1634
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1635
with contextlib.closing(socket.socket()) as s:
1636
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1637
struct.pack(str(u"16s16x"),
1639
interface_index = struct.unpack(str(u"I"),
1641
return interface_index
1642
return if_nametoindex(interface)
1645
1953
def daemon(nochdir = False, noclose = False):
1646
1954
"""See daemon(3). Standard BSD Unix function.
1672
1980
##################################################################
1673
1981
# Parsing of options, both command line and config file
1675
parser = optparse.OptionParser(version = "%%prog %s" % version)
1676
parser.add_option("-i", u"--interface", type=u"string",
1677
metavar="IF", help=u"Bind to interface IF")
1678
parser.add_option("-a", u"--address", type=u"string",
1679
help=u"Address to listen for requests on")
1680
parser.add_option("-p", u"--port", type=u"int",
1681
help=u"Port number to receive requests on")
1682
parser.add_option("--check", action=u"store_true",
1683
help=u"Run self-test")
1684
parser.add_option("--debug", action=u"store_true",
1685
help=u"Debug mode; run in foreground and log to"
1687
parser.add_option("--debuglevel", type=u"string", metavar="LEVEL",
1688
help=u"Debug level for stdout output")
1689
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1690
u" priority string (see GnuTLS documentation)")
1691
parser.add_option("--servicename", type=u"string",
1692
metavar=u"NAME", help=u"Zeroconf service name")
1693
parser.add_option("--configdir", type=u"string",
1694
default=u"/etc/mandos", metavar=u"DIR",
1695
help=u"Directory to search for configuration"
1697
parser.add_option("--no-dbus", action=u"store_false",
1698
dest=u"use_dbus", help=u"Do not provide D-Bus"
1699
u" system bus interface")
1700
parser.add_option("--no-ipv6", action=u"store_false",
1701
dest=u"use_ipv6", help=u"Do not use IPv6")
1702
options = parser.parse_args()[0]
1983
parser = argparse.ArgumentParser()
1984
parser.add_argument("-v", "--version", action="version",
1985
version = "%%(prog)s %s" % version,
1986
help="show version number and exit")
1987
parser.add_argument("-i", "--interface", metavar="IF",
1988
help="Bind to interface IF")
1989
parser.add_argument("-a", "--address",
1990
help="Address to listen for requests on")
1991
parser.add_argument("-p", "--port", type=int,
1992
help="Port number to receive requests on")
1993
parser.add_argument("--check", action="store_true",
1994
help="Run self-test")
1995
parser.add_argument("--debug", action="store_true",
1996
help="Debug mode; run in foreground and log"
1998
parser.add_argument("--debuglevel", metavar="LEVEL",
1999
help="Debug level for stdout output")
2000
parser.add_argument("--priority", help="GnuTLS"
2001
" priority string (see GnuTLS documentation)")
2002
parser.add_argument("--servicename",
2003
metavar="NAME", help="Zeroconf service name")
2004
parser.add_argument("--configdir",
2005
default="/etc/mandos", metavar="DIR",
2006
help="Directory to search for configuration"
2008
parser.add_argument("--no-dbus", action="store_false",
2009
dest="use_dbus", help="Do not provide D-Bus"
2010
" system bus interface")
2011
parser.add_argument("--no-ipv6", action="store_false",
2012
dest="use_ipv6", help="Do not use IPv6")
2013
parser.add_argument("--no-restore", action="store_false",
2014
dest="restore", help="Do not restore stored"
2016
parser.add_argument("--statedir", metavar="DIR",
2017
help="Directory to save/restore state in")
2019
options = parser.parse_args()
1704
2021
if options.check:
1709
2026
# Default values for config file for server-global settings
1710
server_defaults = { u"interface": u"",
1715
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1716
u"servicename": u"Mandos",
1717
u"use_dbus": u"True",
1718
u"use_ipv6": u"True",
2027
server_defaults = { "interface": "",
2032
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2033
"servicename": "Mandos",
2038
"statedir": "/var/lib/mandos"
1722
2041
# Parse config file for server-global settings
1723
2042
server_config = configparser.SafeConfigParser(server_defaults)
1724
2043
del server_defaults
1725
2044
server_config.read(os.path.join(options.configdir,
1727
2046
# Convert the SafeConfigParser object to a dict
1728
2047
server_settings = server_config.defaults()
1729
2048
# Use the appropriate methods on the non-string config options
1730
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1731
server_settings[option] = server_config.getboolean(u"DEFAULT",
2049
for option in ("debug", "use_dbus", "use_ipv6"):
2050
server_settings[option] = server_config.getboolean("DEFAULT",
1733
2052
if server_settings["port"]:
1734
server_settings["port"] = server_config.getint(u"DEFAULT",
2053
server_settings["port"] = server_config.getint("DEFAULT",
1736
2055
del server_config
1738
2057
# Override the settings from the config file with command line
1739
2058
# options, if set.
1740
for option in (u"interface", u"address", u"port", u"debug",
1741
u"priority", u"servicename", u"configdir",
1742
u"use_dbus", u"use_ipv6", u"debuglevel"):
2059
for option in ("interface", "address", "port", "debug",
2060
"priority", "servicename", "configdir",
2061
"use_dbus", "use_ipv6", "debuglevel", "restore",
1743
2063
value = getattr(options, option)
1744
2064
if value is not None:
1745
2065
server_settings[option] = value
1753
2073
##################################################################
1755
2075
# For convenience
1756
debug = server_settings[u"debug"]
1757
debuglevel = server_settings[u"debuglevel"]
1758
use_dbus = server_settings[u"use_dbus"]
1759
use_ipv6 = server_settings[u"use_ipv6"]
1761
if server_settings[u"servicename"] != u"Mandos":
2076
debug = server_settings["debug"]
2077
debuglevel = server_settings["debuglevel"]
2078
use_dbus = server_settings["use_dbus"]
2079
use_ipv6 = server_settings["use_ipv6"]
2080
stored_state_path = os.path.join(server_settings["statedir"],
2084
initlogger(debug, logging.DEBUG)
2089
level = getattr(logging, debuglevel.upper())
2090
initlogger(debug, level)
2092
if server_settings["servicename"] != "Mandos":
1762
2093
syslogger.setFormatter(logging.Formatter
1763
(u'Mandos (%s) [%%(process)d]:'
1764
u' %%(levelname)s: %%(message)s'
1765
% server_settings[u"servicename"]))
2094
('Mandos (%s) [%%(process)d]:'
2095
' %%(levelname)s: %%(message)s'
2096
% server_settings["servicename"]))
1767
2098
# Parse config file with clients
1768
client_defaults = { u"timeout": u"1h",
1770
u"checker": u"fping -q -- %%(host)s",
1772
u"approval_delay": u"0s",
1773
u"approval_duration": u"1s",
1775
client_config = configparser.SafeConfigParser(client_defaults)
1776
client_config.read(os.path.join(server_settings[u"configdir"],
2099
client_config = configparser.SafeConfigParser(Client
2101
client_config.read(os.path.join(server_settings["configdir"],
1779
2104
global mandos_dbus_service
1780
2105
mandos_dbus_service = None
1782
tcp_server = MandosServer((server_settings[u"address"],
1783
server_settings[u"port"]),
2107
tcp_server = MandosServer((server_settings["address"],
2108
server_settings["port"]),
1785
interface=(server_settings[u"interface"]
2110
interface=(server_settings["interface"]
1787
2112
use_ipv6=use_ipv6,
1788
2113
gnutls_priority=
1789
server_settings[u"priority"],
2114
server_settings["priority"],
1790
2115
use_dbus=use_dbus)
1792
pidfilename = u"/var/run/mandos.pid"
2117
pidfilename = "/var/run/mandos.pid"
1794
pidfile = open(pidfilename, u"w")
2119
pidfile = open(pidfilename, "w")
1795
2120
except IOError:
1796
logger.error(u"Could not open file %r", pidfilename)
2121
logger.error("Could not open file %r", pidfilename)
1799
uid = pwd.getpwnam(u"_mandos").pw_uid
1800
gid = pwd.getpwnam(u"_mandos").pw_gid
2124
uid = pwd.getpwnam("_mandos").pw_uid
2125
gid = pwd.getpwnam("_mandos").pw_gid
1801
2126
except KeyError:
1803
uid = pwd.getpwnam(u"mandos").pw_uid
1804
gid = pwd.getpwnam(u"mandos").pw_gid
2128
uid = pwd.getpwnam("mandos").pw_uid
2129
gid = pwd.getpwnam("mandos").pw_gid
1805
2130
except KeyError:
1807
uid = pwd.getpwnam(u"nobody").pw_uid
1808
gid = pwd.getpwnam(u"nobody").pw_gid
2132
uid = pwd.getpwnam("nobody").pw_uid
2133
gid = pwd.getpwnam("nobody").pw_gid
1809
2134
except KeyError:
1815
except OSError, error:
2140
except OSError as error:
1816
2141
if error[0] != errno.EPERM:
1819
if not debug and not debuglevel:
1820
syslogger.setLevel(logging.WARNING)
1821
console.setLevel(logging.WARNING)
1823
level = getattr(logging, debuglevel.upper())
1824
syslogger.setLevel(level)
1825
console.setLevel(level)
1828
2145
# Enable all possible GnuTLS debugging
1856
2176
# End of Avahi example code
1859
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
2179
bus_name = dbus.service.BusName("se.recompile.Mandos",
1860
2180
bus, do_not_queue=True)
1861
except dbus.exceptions.NameExistsException, e:
1862
logger.error(unicode(e) + u", disabling D-Bus")
2181
old_bus_name = (dbus.service.BusName
2182
("se.bsnet.fukt.Mandos", bus,
2184
except dbus.exceptions.NameExistsException as e:
2185
logger.error(unicode(e) + ", disabling D-Bus")
1863
2186
use_dbus = False
1864
server_settings[u"use_dbus"] = False
2187
server_settings["use_dbus"] = False
1865
2188
tcp_server.use_dbus = False
1866
2189
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1867
service = AvahiService(name = server_settings[u"servicename"],
1868
servicetype = u"_mandos._tcp",
1869
protocol = protocol, bus = bus)
2190
service = AvahiServiceToSyslog(name =
2191
server_settings["servicename"],
2192
servicetype = "_mandos._tcp",
2193
protocol = protocol, bus = bus)
1870
2194
if server_settings["interface"]:
1871
2195
service.interface = (if_nametoindex
1872
(str(server_settings[u"interface"])))
1875
# Close all input and output, do double fork, etc.
2196
(str(server_settings["interface"])))
1878
2198
global multiprocessing_manager
1879
2199
multiprocessing_manager = multiprocessing.Manager()
1881
2201
client_class = Client
1883
client_class = functools.partial(ClientDBus, bus = bus)
1884
def client_config_items(config, section):
1885
special_settings = {
1886
"approved_by_default":
1887
lambda: config.getboolean(section,
1888
"approved_by_default"),
1890
for name, value in config.items(section):
2203
client_class = functools.partial(ClientDBusTransitional,
2206
client_settings = Client.config_parser(client_config)
2207
old_client_settings = {}
2210
# Get client data and settings from last running state.
2211
if server_settings["restore"]:
2213
with open(stored_state_path, "rb") as stored_state:
2214
clients_data, old_client_settings = (pickle.load
2216
os.remove(stored_state_path)
2217
except IOError as e:
2218
logger.warning("Could not load persistent state: {0}"
2220
if e.errno != errno.ENOENT:
2222
except EOFError as e:
2223
logger.warning("Could not load persistent state: "
2224
"EOFError: {0}".format(e))
2226
with PGPEngine() as pgp:
2227
for client_name, client in clients_data.iteritems():
2228
# Decide which value to use after restoring saved state.
2229
# We have three different values: Old config file,
2230
# new config file, and saved state.
2231
# New config value takes precedence if it differs from old
2232
# config value, otherwise use saved state.
2233
for name, value in client_settings[client_name].items():
2235
# For each value in new config, check if it
2236
# differs from the old config value (Except for
2237
# the "secret" attribute)
2238
if (name != "secret" and
2239
value != old_client_settings[client_name]
2241
client[name] = value
2245
# Clients who has passed its expire date can still be
2246
# enabled if its last checker was successful. Clients
2247
# whose checker succeeded before we stored its state is
2248
# assumed to have successfully run all checkers during
2250
if client["enabled"]:
2251
if datetime.datetime.utcnow() >= client["expires"]:
2252
if not client["last_checked_ok"]:
2254
"disabling client {0} - Client never "
2255
"performed a successful checker"
2256
.format(client_name))
2257
client["enabled"] = False
2258
elif client["last_checker_status"] != 0:
2260
"disabling client {0} - Client "
2261
"last checker failed with error code {1}"
2262
.format(client_name,
2263
client["last_checker_status"]))
2264
client["enabled"] = False
2266
client["expires"] = (datetime.datetime
2268
+ client["timeout"])
2269
logger.debug("Last checker succeeded,"
2270
" keeping {0} enabled"
2271
.format(client_name))
1892
yield (name, special_settings[name]())
1896
tcp_server.clients.update(set(
1897
client_class(name = section,
1898
config= dict(client_config_items(
1899
client_config, section)))
1900
for section in client_config.sections()))
2273
client["secret"] = (
2274
pgp.decrypt(client["encrypted_secret"],
2275
client_settings[client_name]
2278
# If decryption fails, we use secret from new settings
2279
logger.debug("Failed to decrypt {0} old secret"
2280
.format(client_name))
2281
client["secret"] = (
2282
client_settings[client_name]["secret"])
2285
# Add/remove clients based on new changes made to config
2286
for client_name in (set(old_client_settings)
2287
- set(client_settings)):
2288
del clients_data[client_name]
2289
for client_name in (set(client_settings)
2290
- set(old_client_settings)):
2291
clients_data[client_name] = client_settings[client_name]
2293
# Create all client objects
2294
for client_name, client in clients_data.iteritems():
2295
tcp_server.clients[client_name] = client_class(
2296
name = client_name, settings = client)
1901
2298
if not tcp_server.clients:
1902
logger.warning(u"No clients defined")
2299
logger.warning("No clients defined")
1907
2304
pid = os.getpid()
1908
pidfile.write(str(pid) + "\n")
2305
pidfile.write(str(pid) + "\n".encode("utf-8"))
1910
2307
except IOError:
1911
logger.error(u"Could not write to file %r with PID %d",
2308
logger.error("Could not write to file %r with PID %d",
1912
2309
pidfilename, pid)
1913
2310
except NameError:
1914
2311
# "pidfile" was never created
1916
2313
del pidfilename
1918
2314
signal.signal(signal.SIGINT, signal.SIG_IGN)
1920
2316
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1921
2317
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1924
2320
class MandosDBusService(dbus.service.Object):
1925
2321
"""A D-Bus proxy object"""
1926
2322
def __init__(self):
1927
dbus.service.Object.__init__(self, bus, u"/")
1928
_interface = u"se.bsnet.fukt.Mandos"
2323
dbus.service.Object.__init__(self, bus, "/")
2324
_interface = "se.recompile.Mandos"
1930
@dbus.service.signal(_interface, signature=u"o")
2326
@dbus.service.signal(_interface, signature="o")
1931
2327
def ClientAdded(self, objpath):
1935
@dbus.service.signal(_interface, signature=u"ss")
2331
@dbus.service.signal(_interface, signature="ss")
1936
2332
def ClientNotFound(self, fingerprint, address):
1940
@dbus.service.signal(_interface, signature=u"os")
2336
@dbus.service.signal(_interface, signature="os")
1941
2337
def ClientRemoved(self, objpath, name):
1945
@dbus.service.method(_interface, out_signature=u"ao")
2341
@dbus.service.method(_interface, out_signature="ao")
1946
2342
def GetAllClients(self):
1948
2344
return dbus.Array(c.dbus_object_path
1949
for c in tcp_server.clients)
2346
tcp_server.clients.itervalues())
1951
2348
@dbus.service.method(_interface,
1952
out_signature=u"a{oa{sv}}")
2349
out_signature="a{oa{sv}}")
1953
2350
def GetAllClientsWithProperties(self):
1955
2352
return dbus.Dictionary(
1956
((c.dbus_object_path, c.GetAll(u""))
1957
for c in tcp_server.clients),
1958
signature=u"oa{sv}")
2353
((c.dbus_object_path, c.GetAll(""))
2354
for c in tcp_server.clients.itervalues()),
1960
@dbus.service.method(_interface, in_signature=u"o")
2357
@dbus.service.method(_interface, in_signature="o")
1961
2358
def RemoveClient(self, object_path):
1963
for c in tcp_server.clients:
2360
for c in tcp_server.clients.itervalues():
1964
2361
if c.dbus_object_path == object_path:
1965
tcp_server.clients.remove(c)
2362
del tcp_server.clients[c.name]
1966
2363
c.remove_from_connection()
1967
2364
# Don't signal anything except ClientRemoved
1968
2365
c.disable(quiet=True)
1976
mandos_dbus_service = MandosDBusService()
2373
class MandosDBusServiceTransitional(MandosDBusService):
2374
__metaclass__ = AlternateDBusNamesMetaclass
2375
mandos_dbus_service = MandosDBusServiceTransitional()
1979
2378
"Cleanup function; run on exit"
1980
2379
service.cleanup()
2381
multiprocessing.active_children()
2382
if not (tcp_server.clients or client_settings):
2385
# Store client before exiting. Secrets are encrypted with key
2386
# based on what config file has. If config file is
2387
# removed/edited, old secret will thus be unrecovable.
2389
with PGPEngine() as pgp:
2390
for client in tcp_server.clients.itervalues():
2391
key = client_settings[client.name]["secret"]
2392
client.encrypted_secret = pgp.encrypt(client.secret,
2396
# A list of attributes that can not be pickled
2398
exclude = set(("bus", "changedstate", "secret",
2400
for name, typ in (inspect.getmembers
2401
(dbus.service.Object)):
2404
client_dict["encrypted_secret"] = (client
2406
for attr in client.client_structure:
2407
if attr not in exclude:
2408
client_dict[attr] = getattr(client, attr)
2410
clients[client.name] = client_dict
2411
del client_settings[client.name]["secret"]
2414
tempfd, tempname = tempfile.mkstemp(suffix=".pickle",
2417
(stored_state_path))
2418
with os.fdopen(tempfd, "wb") as stored_state:
2419
pickle.dump((clients, client_settings), stored_state)
2420
os.rename(tempname, stored_state_path)
2421
except (IOError, OSError) as e:
2422
logger.warning("Could not save persistent state: {0}"
2429
if e.errno not in set((errno.ENOENT, errno.EACCES,
2433
# Delete all clients, and settings from config
1982
2434
while tcp_server.clients:
1983
client = tcp_server.clients.pop()
2435
name, client = tcp_server.clients.popitem()
1985
2437
client.remove_from_connection()
1986
client.disable_hook = None
1987
2438
# Don't signal anything except ClientRemoved
1988
2439
client.disable(quiet=True)
1990
2441
# Emit D-Bus signal
1991
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2442
mandos_dbus_service.ClientRemoved(client
2445
client_settings.clear()
1994
2447
atexit.register(cleanup)
1996
for client in tcp_server.clients:
2449
for client in tcp_server.clients.itervalues():
1998
2451
# Emit D-Bus signal
1999
2452
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2453
# Need to initiate checking of clients
2455
client.init_checker()
2002
2457
tcp_server.enable()
2003
2458
tcp_server.server_activate()