85
80
except ImportError:
86
81
SO_BINDTODEVICE = None
89
stored_state_file = "clients.pickle"
91
logger = logging.getLogger()
86
#logger = logging.getLogger(u'mandos')
87
logger = logging.Logger(u'mandos')
92
88
syslogger = (logging.handlers.SysLogHandler
93
89
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
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
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)
212
102
class AvahiError(Exception):
213
103
def __init__(self, value, *args, **kwargs):
260
150
self.group = None # our entry group
261
151
self.server = None
263
self.entry_group_state_changed_match = None
264
153
def rename(self):
265
154
"""Derived from the Avahi example code"""
266
155
if self.rename_count >= self.max_renames:
267
logger.critical("No suitable Zeroconf service name found"
268
" after %i retries, exiting.",
156
logger.critical(u"No suitable Zeroconf service name found"
157
u" after %i retries, exiting.",
269
158
self.rename_count)
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 ...",
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 ...",
163
syslogger.setFormatter(logging.Formatter
164
(u'Mandos (%s) [%%(process)d]:'
165
u' %%(levelname)s: %%(message)s'
278
except dbus.exceptions.DBusException as error:
279
logger.critical("DBusException: %s", error)
170
except dbus.exceptions.DBusException, error:
171
logger.critical(u"DBusException: %s", error)
282
174
self.rename_count += 1
283
175
def remove(self):
284
176
"""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
288
177
if self.group is not None:
289
178
self.group.Reset()
291
180
"""Derived from the Avahi example code"""
293
181
if self.group is None:
294
182
self.group = dbus.Interface(
295
183
self.bus.get_object(avahi.DBUS_NAME,
296
184
self.server.EntryGroupNew()),
297
185
avahi.DBUS_INTERFACE_ENTRY_GROUP)
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' ...",
186
self.group.connect_to_signal('StateChanged',
188
.entry_group_state_changed)
189
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
302
190
self.name, self.type)
303
191
self.group.AddService(
311
199
self.group.Commit()
312
200
def entry_group_state_changed(self, state, error):
313
201
"""Derived from the Avahi example code"""
314
logger.debug("Avahi entry group state change: %i", state)
202
logger.debug(u"Avahi entry group state change: %i", state)
316
204
if state == avahi.ENTRY_GROUP_ESTABLISHED:
317
logger.debug("Zeroconf service established.")
205
logger.debug(u"Zeroconf service established.")
318
206
elif state == avahi.ENTRY_GROUP_COLLISION:
319
logger.info("Zeroconf service name collision.")
207
logger.warning(u"Zeroconf service name collision.")
321
209
elif state == avahi.ENTRY_GROUP_FAILURE:
322
logger.critical("Avahi: Error in group state changed %s",
210
logger.critical(u"Avahi: Error in group state changed %s",
324
raise AvahiGroupError("State changed: %s"
212
raise AvahiGroupError(u"State changed: %s"
325
213
% unicode(error))
326
214
def cleanup(self):
327
215
"""Derived from the Avahi example code"""
328
216
if self.group is not None:
331
except (dbus.exceptions.UnknownMethodException,
332
dbus.exceptions.DBusException):
334
218
self.group = None
336
def server_state_changed(self, state, error=None):
219
def server_state_changed(self, state):
337
220
"""Derived from the Avahi example code"""
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)
221
logger.debug(u"Avahi server state change: %i", state)
222
if state == avahi.SERVER_COLLISION:
223
logger.error(u"Zeroconf server name collision")
353
225
elif state == avahi.SERVER_RUNNING:
357
logger.debug("Unknown state: %r", state)
359
logger.debug("Unknown state: %r: %r", state, error)
360
227
def activate(self):
361
228
"""Derived from the Avahi example code"""
362
229
if self.server is None:
363
230
self.server = dbus.Interface(
364
231
self.bus.get_object(avahi.DBUS_NAME,
365
avahi.DBUS_PATH_SERVER,
366
follow_name_owner_changes=True),
232
avahi.DBUS_PATH_SERVER),
367
233
avahi.DBUS_INTERFACE_SERVER)
368
self.server.connect_to_signal("StateChanged",
234
self.server.connect_to_signal(u"StateChanged",
369
235
self.server_state_changed)
370
236
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))
388
239
class Client(object):
389
240
"""A representation of a client host served by this server.
392
approved: bool(); 'None' if not yet approved/disapproved
243
_approved: bool(); 'None' if not yet approved/disapproved
393
244
approval_delay: datetime.timedelta(); Time to wait for approval
394
245
approval_duration: datetime.timedelta(); Duration of one approval
395
246
checker: subprocess.Popen(); a running checker process used
413
263
interval: datetime.timedelta(); How often to start a new checker
414
264
last_approval_request: datetime.datetime(); (UTC) or None
415
265
last_checked_ok: datetime.datetime(); (UTC) or None
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
266
last_enabled: datetime.datetime(); (UTC)
420
267
name: string; from the config file, used in log messages and
421
268
D-Bus identifiers
422
269
secret: bytestring; sent verbatim (over TLS) to client
423
270
timeout: datetime.timedelta(); How long from last_checked_ok
424
271
until this client is disabled
425
extended_timeout: extra long timeout when secret has been sent
426
272
runtime_expansions: Allowed attributes for runtime expansion.
427
expires: datetime.datetime(); time (UTC) when a client will be
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",
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))
446
287
def timeout_milliseconds(self):
447
288
"Return the 'timeout' attribute in milliseconds"
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)
289
return self._timedelta_to_milliseconds(self.timeout)
454
291
def interval_milliseconds(self):
455
292
"Return the 'interval' attribute in milliseconds"
456
return timedelta_to_milliseconds(self.interval)
293
return self._timedelta_to_milliseconds(self.interval)
458
295
def approval_delay_milliseconds(self):
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):
296
return self._timedelta_to_milliseconds(self.approval_delay)
298
def __init__(self, name = None, disable_hook=None, config=None):
510
299
"""Note: the 'checker' key in 'config' sets the
511
300
'checker_command' attribute and *not* the 'checker'
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)
305
logger.debug(u"Creating client %r", self.name)
529
306
# Uppercase and remove spaces from fingerprint for later
530
307
# comparison purposes with return value from the fingerprint()
532
logger.debug(" Fingerprint: %s", self.fingerprint)
533
self.created = settings.get("created",
534
datetime.datetime.utcnow())
536
# attributes specific for this server instance
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
537
331
self.checker = None
538
332
self.checker_initiator_tag = None
539
333
self.disable_initiator_tag = None
540
334
self.checker_callback_tag = None
335
self.checker_command = config[u"checker"]
541
336
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",
543
341
self.approvals_pending = 0
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)
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())
559
# Send notice to process children that client state has changed
560
348
def send_changedstate(self):
561
with self.changedstate:
562
self.changedstate.notify_all()
349
self.changedstate.acquire()
350
self.changedstate.notify_all()
351
self.changedstate.release()
564
353
def enable(self):
565
354
"""Start this client's checker and timeout hooks"""
566
if getattr(self, "enabled", False):
355
if getattr(self, u"enabled", False):
567
356
# Already enabled
569
358
self.send_changedstate()
570
self.expires = datetime.datetime.utcnow() + self.timeout
572
359
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):
599
360
# Schedule a new checker to be started an 'interval' from now,
600
361
# and every interval from then on.
601
362
self.checker_initiator_tag = (gobject.timeout_add
605
366
self.disable_initiator_tag = (gobject.timeout_add
606
367
(self.timeout_milliseconds(),
608
370
# Also start a new checker *right now*.
609
371
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
611
398
def checker_callback(self, pid, condition, command):
612
399
"""The checker has completed, so take appropriate actions."""
613
400
self.checker_callback_tag = None
614
401
self.checker = None
615
402
if os.WIFEXITED(condition):
616
self.last_checker_status = os.WEXITSTATUS(condition)
617
if self.last_checker_status == 0:
618
logger.info("Checker for %(name)s succeeded",
403
exitstatus = os.WEXITSTATUS(condition)
405
logger.info(u"Checker for %(name)s succeeded",
620
407
self.checked_ok()
622
logger.info("Checker for %(name)s failed",
409
logger.info(u"Checker for %(name)s failed",
625
self.last_checker_status = -1
626
logger.warning("Checker for %(name)s crashed?",
412
logger.warning(u"Checker for %(name)s crashed?",
629
415
def checked_ok(self):
630
"""Assert that the client has been seen, alive and well."""
416
"""Bump up the timeout for this client.
418
This should only be called when the client has been seen,
631
421
self.last_checked_ok = datetime.datetime.utcnow()
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
422
gobject.source_remove(self.disable_initiator_tag)
423
self.disable_initiator_tag = (gobject.timeout_add
424
(self.timeout_milliseconds(),
647
427
def need_approval(self):
648
428
self.last_approval_request = datetime.datetime.utcnow()
802
581
def _is_dbus_property(obj):
803
return getattr(obj, "_dbus_is_property", False)
582
return getattr(obj, u"_dbus_is_property", False)
805
584
def _get_all_dbus_properties(self):
806
585
"""Returns a generator of (name, attribute) pairs
808
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
809
for cls in self.__class__.__mro__
587
return ((prop._dbus_name, prop)
810
588
for name, prop in
811
inspect.getmembers(cls, self._is_dbus_property))
589
inspect.getmembers(self, self._is_dbus_property))
813
591
def _get_dbus_property(self, interface_name, property_name):
814
592
"""Returns a bound method if one exists which is a D-Bus
815
593
property with the specified name and 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)
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)):
824
605
# No such property
825
raise DBusPropertyNotFound(self.dbus_object_path + ":"
826
+ interface_name + "."
606
raise DBusPropertyNotFound(self.dbus_object_path + u":"
607
+ interface_name + u"."
829
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
831
612
def Get(self, interface_name, property_name):
832
613
"""Standard D-Bus property Get() method, see D-Bus standard.
834
615
prop = self._get_dbus_property(interface_name, property_name)
835
if prop._dbus_access == "write":
616
if prop._dbus_access == u"write":
836
617
raise DBusPropertyAccessException(property_name)
838
if not hasattr(value, "variant_level"):
619
if not hasattr(value, u"variant_level"):
840
621
return type(value)(value, variant_level=value.variant_level+1)
842
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
623
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
843
624
def Set(self, interface_name, property_name, value):
844
625
"""Standard D-Bus property Set() method, see D-Bus standard.
846
627
prop = self._get_dbus_property(interface_name, property_name)
847
if prop._dbus_access == "read":
628
if prop._dbus_access == u"read":
848
629
raise DBusPropertyAccessException(property_name)
849
if prop._dbus_get_args_options["byte_arrays"]:
630
if prop._dbus_get_args_options[u"byte_arrays"]:
850
631
# The byte_arrays option is not supported yet on
851
632
# signatures other than "ay".
852
if prop._dbus_signature != "ay":
633
if prop._dbus_signature != u"ay":
854
value = dbus.ByteArray(b''.join(chr(byte)
635
value = dbus.ByteArray(''.join(unichr(byte)
858
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
859
out_signature="a{sv}")
639
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
640
out_signature=u"a{sv}")
860
641
def GetAll(self, interface_name):
861
642
"""Standard D-Bus property GetAll() method, see D-Bus
864
645
Note: Will not include properties with access="write".
867
648
for name, prop in self._get_all_dbus_properties():
868
649
if (interface_name
869
650
and interface_name != prop._dbus_interface):
870
651
# Interface non-empty but did not match
872
653
# Ignore write-only properties
873
if prop._dbus_access == "write":
654
if prop._dbus_access == u"write":
876
if not hasattr(value, "variant_level"):
877
properties[name] = value
657
if not hasattr(value, u"variant_level"):
879
properties[name] = type(value)(value, variant_level=
880
value.variant_level+1)
881
return dbus.Dictionary(properties, signature="sv")
660
all[name] = type(value)(value, variant_level=
661
value.variant_level+1)
662
return dbus.Dictionary(all, signature=u"sv")
883
664
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
885
666
path_keyword='object_path',
886
667
connection_keyword='connection')
887
668
def Introspect(self, object_path, connection):
893
674
document = xml.dom.minidom.parseString(xmlstring)
894
675
def make_tag(document, name, prop):
895
e = document.createElement("property")
896
e.setAttribute("name", name)
897
e.setAttribute("type", prop._dbus_signature)
898
e.setAttribute("access", prop._dbus_access)
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)
900
for if_tag in document.getElementsByTagName("interface"):
681
for if_tag in document.getElementsByTagName(u"interface"):
901
682
for tag in (make_tag(document, name, prop)
903
684
in self._get_all_dbus_properties()
904
685
if prop._dbus_interface
905
== if_tag.getAttribute("name")):
686
== if_tag.getAttribute(u"name")):
906
687
if_tag.appendChild(tag)
907
688
# Add the names to the return values for the
908
689
# "org.freedesktop.DBus.Properties" methods
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")
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")
923
704
document.unlink()
924
705
except (AttributeError, xml.dom.DOMException,
925
xml.parsers.expat.ExpatError) as error:
926
logger.error("Failed to override Introspection method",
706
xml.parsers.expat.ExpatError), error:
707
logger.error(u"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)
1037
712
class ClientDBus(Client, DBusObjectWithProperties):
1038
713
"""A Client class using D-Bus
1045
720
runtime_expansions = (Client.runtime_expansions
1046
+ ("dbus_object_path",))
721
+ (u"dbus_object_path",))
1048
723
# dbus.service.Object doesn't use super(), so we can't either.
1050
725
def __init__(self, bus = None, *args, **kwargs):
726
self._approvals_pending = 0
1052
728
Client.__init__(self, *args, **kwargs)
1053
729
# Only now, when this client is initialized, can it show up on
1055
731
client_object_name = unicode(self.name).translate(
1056
{ord("."): ord("_"),
1057
ord("-"): ord("_")})
732
{ord(u"."): ord(u"_"),
733
ord(u"-"): ord(u"_")})
1058
734
self.dbus_object_path = (dbus.ObjectPath
1059
("/clients/" + client_object_name))
735
(u"/clients/" + client_object_name))
1060
736
DBusObjectWithProperties.__init__(self, self.bus,
1061
737
self.dbus_object_path)
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
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))
1132
783
def __del__(self, *args, **kwargs):
1134
785
self.remove_from_connection()
1135
786
except LookupError:
1137
if hasattr(DBusObjectWithProperties, "__del__"):
788
if hasattr(DBusObjectWithProperties, u"__del__"):
1138
789
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1139
790
Client.__del__(self, *args, **kwargs)
1169
841
and old_checker_pid != self.checker.pid):
1170
842
# Emit D-Bus signal
1171
843
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))
1174
858
def _reset_approved(self):
1175
self.approved = None
859
self._approved = None
1178
862
def approve(self, value=True):
1179
863
self.send_changedstate()
1180
self.approved = value
1181
gobject.timeout_add(timedelta_to_milliseconds
864
self._approved = value
865
gobject.timeout_add(self._timedelta_to_milliseconds
1182
866
(self.approval_duration),
1183
867
self._reset_approved)
1186
870
## D-Bus methods, signals & properties
1187
_interface = "se.recompile.Mandos.Client"
871
_interface = u"se.bsnet.fukt.Mandos.Client"
1191
875
# CheckerCompleted - signal
1192
@dbus.service.signal(_interface, signature="nxs")
876
@dbus.service.signal(_interface, signature=u"nxs")
1193
877
def CheckerCompleted(self, exitcode, waitstatus, command):
1197
881
# CheckerStarted - signal
1198
@dbus.service.signal(_interface, signature="s")
882
@dbus.service.signal(_interface, signature=u"s")
1199
883
def CheckerStarted(self, command):
1203
887
# PropertyChanged - signal
1204
@dbus.service.signal(_interface, signature="sv")
888
@dbus.service.signal(_interface, signature=u"sv")
1205
889
def PropertyChanged(self, property, value):
1267
951
# ApprovalPending - property
1268
@dbus_service_property(_interface, signature="b", access="read")
952
@dbus_service_property(_interface, signature=u"b", access=u"read")
1269
953
def ApprovalPending_dbus_property(self):
1270
954
return dbus.Boolean(bool(self.approvals_pending))
1272
956
# ApprovedByDefault - property
1273
@dbus_service_property(_interface, signature="b",
957
@dbus_service_property(_interface, signature=u"b",
1275
959
def ApprovedByDefault_dbus_property(self, value=None):
1276
960
if value is None: # get
1277
961
return dbus.Boolean(self.approved_by_default)
1278
962
self.approved_by_default = bool(value)
964
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
965
dbus.Boolean(value, variant_level=1))
1280
967
# ApprovalDelay - property
1281
@dbus_service_property(_interface, signature="t",
968
@dbus_service_property(_interface, signature=u"t",
1283
970
def ApprovalDelay_dbus_property(self, value=None):
1284
971
if value is None: # get
1285
972
return dbus.UInt64(self.approval_delay_milliseconds())
1286
973
self.approval_delay = datetime.timedelta(0, 0, 0, value)
975
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
976
dbus.UInt64(value, variant_level=1))
1288
978
# ApprovalDuration - property
1289
@dbus_service_property(_interface, signature="t",
979
@dbus_service_property(_interface, signature=u"t",
1291
981
def ApprovalDuration_dbus_property(self, value=None):
1292
982
if value is None: # get
1293
return dbus.UInt64(timedelta_to_milliseconds(
983
return dbus.UInt64(self._timedelta_to_milliseconds(
1294
984
self.approval_duration))
1295
985
self.approval_duration = datetime.timedelta(0, 0, 0, value)
987
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
988
dbus.UInt64(value, variant_level=1))
1297
990
# Name - property
1298
@dbus_service_property(_interface, signature="s", access="read")
991
@dbus_service_property(_interface, signature=u"s", access=u"read")
1299
992
def Name_dbus_property(self):
1300
993
return dbus.String(self.name)
1302
995
# Fingerprint - property
1303
@dbus_service_property(_interface, signature="s", access="read")
996
@dbus_service_property(_interface, signature=u"s", access=u"read")
1304
997
def Fingerprint_dbus_property(self):
1305
998
return dbus.String(self.fingerprint)
1307
1000
# Host - property
1308
@dbus_service_property(_interface, signature="s",
1001
@dbus_service_property(_interface, signature=u"s",
1002
access=u"readwrite")
1310
1003
def Host_dbus_property(self, value=None):
1311
1004
if value is None: # get
1312
1005
return dbus.String(self.host)
1313
self.host = unicode(value)
1008
self.PropertyChanged(dbus.String(u"Host"),
1009
dbus.String(value, variant_level=1))
1315
1011
# Created - property
1316
@dbus_service_property(_interface, signature="s", access="read")
1012
@dbus_service_property(_interface, signature=u"s", access=u"read")
1317
1013
def Created_dbus_property(self):
1318
return datetime_to_dbus(self.created)
1014
return dbus.String(self._datetime_to_dbus(self.created))
1320
1016
# LastEnabled - property
1321
@dbus_service_property(_interface, signature="s", access="read")
1017
@dbus_service_property(_interface, signature=u"s", access=u"read")
1322
1018
def LastEnabled_dbus_property(self):
1323
return datetime_to_dbus(self.last_enabled)
1019
if self.last_enabled is None:
1020
return dbus.String(u"")
1021
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1325
1023
# Enabled - property
1326
@dbus_service_property(_interface, signature="b",
1024
@dbus_service_property(_interface, signature=u"b",
1025
access=u"readwrite")
1328
1026
def Enabled_dbus_property(self, value=None):
1329
1027
if value is None: # get
1330
1028
return dbus.Boolean(self.enabled)
1336
1034
# LastCheckedOK - property
1337
@dbus_service_property(_interface, signature="s",
1035
@dbus_service_property(_interface, signature=u"s",
1036
access=u"readwrite")
1339
1037
def LastCheckedOK_dbus_property(self, value=None):
1340
1038
if value is not None:
1341
1039
self.checked_ok()
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)
1041
if self.last_checked_ok is None:
1042
return dbus.String(u"")
1043
return dbus.String(self._datetime_to_dbus(self
1356
1046
# LastApprovalRequest - property
1357
@dbus_service_property(_interface, signature="s", access="read")
1047
@dbus_service_property(_interface, signature=u"s", access=u"read")
1358
1048
def LastApprovalRequest_dbus_property(self):
1359
return datetime_to_dbus(self.last_approval_request)
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))
1361
1055
# Timeout - property
1362
@dbus_service_property(_interface, signature="t",
1056
@dbus_service_property(_interface, signature=u"t",
1057
access=u"readwrite")
1364
1058
def Timeout_dbus_property(self, value=None):
1365
1059
if value is None: # get
1366
1060
return dbus.UInt64(self.timeout_milliseconds())
1367
1061
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:
1368
1067
# Reschedule timeout
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)
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))
1396
1083
# Interval - property
1397
@dbus_service_property(_interface, signature="t",
1084
@dbus_service_property(_interface, signature=u"t",
1085
access=u"readwrite")
1399
1086
def Interval_dbus_property(self, value=None):
1400
1087
if value is None: # get
1401
1088
return dbus.UInt64(self.interval_milliseconds())
1402
1089
self.interval = datetime.timedelta(0, 0, 0, value)
1403
if getattr(self, "checker_initiator_tag", None) is None:
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:
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
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
1412
1101
# Checker - property
1413
@dbus_service_property(_interface, signature="s",
1102
@dbus_service_property(_interface, signature=u"s",
1103
access=u"readwrite")
1415
1104
def Checker_dbus_property(self, value=None):
1416
1105
if value is None: # get
1417
1106
return dbus.String(self.checker_command)
1418
self.checker_command = unicode(value)
1107
self.checker_command = value
1109
self.PropertyChanged(dbus.String(u"Checker"),
1110
dbus.String(self.checker_command,
1420
1113
# CheckerRunning - property
1421
@dbus_service_property(_interface, signature="b",
1114
@dbus_service_property(_interface, signature=u"b",
1115
access=u"readwrite")
1423
1116
def CheckerRunning_dbus_property(self, value=None):
1424
1117
if value is None: # get
1425
1118
return dbus.Boolean(self.checker is not None)
1481
1170
def handle(self):
1482
1171
with contextlib.closing(self.server.child_pipe) as child_pipe:
1483
logger.info("TCP connection from: %s",
1172
logger.info(u"TCP connection from: %s",
1484
1173
unicode(self.client_address))
1485
logger.debug("Pipe FD: %d",
1174
logger.debug(u"Pipe FD: %d",
1486
1175
self.server.child_pipe.fileno())
1488
1177
session = (gnutls.connection
1489
1178
.ClientSession(self.request,
1490
1179
gnutls.connection
1491
1180
.X509Credentials()))
1493
1182
# Note: gnutls.connection.X509Credentials is really a
1494
1183
# generic GnuTLS certificate credentials object so long as
1495
1184
# no X.509 keys are added to it. Therefore, we can use it
1496
1185
# here despite using OpenPGP certificates.
1498
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1499
# "+AES-256-CBC", "+SHA1",
1500
# "+COMP-NULL", "+CTYPE-OPENPGP",
1187
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1188
# u"+AES-256-CBC", u"+SHA1",
1189
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1502
1191
# Use a fallback default, since this MUST be set.
1503
1192
priority = self.server.gnutls_priority
1504
1193
if priority is None:
1194
priority = u"NORMAL"
1506
1195
(gnutls.library.functions
1507
1196
.gnutls_priority_set_direct(session._c_object,
1508
1197
priority, None))
1510
1199
# Start communication using the Mandos protocol
1511
1200
# Get protocol number
1512
1201
line = self.request.makefile().readline()
1513
logger.debug("Protocol version: %r", line)
1202
logger.debug(u"Protocol version: %r", line)
1515
1204
if int(line.strip().split()[0]) > 1:
1516
1205
raise RuntimeError
1517
except (ValueError, IndexError, RuntimeError) as error:
1518
logger.error("Unknown protocol version: %s", error)
1206
except (ValueError, IndexError, RuntimeError), error:
1207
logger.error(u"Unknown protocol version: %s", error)
1521
1210
# Start GnuTLS connection
1523
1212
session.handshake()
1524
except gnutls.errors.GNUTLSError as error:
1525
logger.warning("Handshake failed: %s", error)
1213
except gnutls.errors.GNUTLSError, error:
1214
logger.warning(u"Handshake failed: %s", error)
1526
1215
# Do not run session.bye() here: the session is not
1527
1216
# established. Just abandon the request.
1529
logger.debug("Handshake succeeded")
1218
logger.debug(u"Handshake succeeded")
1531
1220
approval_required = False
1534
1223
fpr = self.fingerprint(self.peer_certificate
1537
gnutls.errors.GNUTLSError) as error:
1538
logger.warning("Bad certificate: %s", error)
1225
except (TypeError, gnutls.errors.GNUTLSError), error:
1226
logger.warning(u"Bad certificate: %s", error)
1540
logger.debug("Fingerprint: %s", fpr)
1228
logger.debug(u"Fingerprint: %s", fpr)
1543
1231
client = ProxyClient(child_pipe, fpr,
1544
1232
self.client_address)
1860
1536
fpr = request[1]
1861
1537
address = request[2]
1863
for c in self.clients.itervalues():
1539
for c in self.clients:
1864
1540
if c.fingerprint == fpr:
1868
logger.info("Client not found for fingerprint: %s, ad"
1869
"dress: %s", fpr, address)
1544
logger.warning(u"Client not found for fingerprint: %s, ad"
1545
u"dress: %s", fpr, address)
1870
1546
if self.use_dbus:
1871
1547
# Emit D-Bus signal
1872
mandos_dbus_service.ClientNotFound(fpr,
1548
mandos_dbus_service.ClientNotFound(fpr, address[0])
1874
1549
parent_pipe.send(False)
1877
1552
gobject.io_add_watch(parent_pipe.fileno(),
1878
1553
gobject.IO_IN | gobject.IO_HUP,
1879
1554
functools.partial(self.handle_ipc,
1555
parent_pipe = parent_pipe,
1556
client_object = client))
1885
1557
parent_pipe.send(True)
1886
# remove the old hook in favor of the new above hook on
1558
# remove the old hook in favor of the new above hook on same fileno
1889
1560
if command == 'funcall':
1890
1561
funcname = request[1]
1891
1562
args = request[2]
1892
1563
kwargs = request[3]
1894
parent_pipe.send(('data', getattr(client_object,
1565
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1898
1567
if command == 'getattr':
1899
1568
attrname = request[1]
1900
1569
if callable(client_object.__getattribute__(attrname)):
1901
1570
parent_pipe.send(('function',))
1903
parent_pipe.send(('data', client_object
1904
.__getattribute__(attrname)))
1572
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1906
1574
if command == 'setattr':
1907
1575
attrname = request[1]
1908
1576
value = request[2]
1909
1577
setattr(client_object, attrname, value)
1914
1582
def string_to_delta(interval):
1915
1583
"""Parse a string and return a datetime.timedelta
1917
>>> string_to_delta('7d')
1585
>>> string_to_delta(u'7d')
1918
1586
datetime.timedelta(7)
1919
>>> string_to_delta('60s')
1587
>>> string_to_delta(u'60s')
1920
1588
datetime.timedelta(0, 60)
1921
>>> string_to_delta('60m')
1589
>>> string_to_delta(u'60m')
1922
1590
datetime.timedelta(0, 3600)
1923
>>> string_to_delta('24h')
1591
>>> string_to_delta(u'24h')
1924
1592
datetime.timedelta(1)
1925
>>> string_to_delta('1w')
1593
>>> string_to_delta(u'1w')
1926
1594
datetime.timedelta(7)
1927
>>> string_to_delta('5m 30s')
1595
>>> string_to_delta(u'5m 30s')
1928
1596
datetime.timedelta(0, 330)
1930
1598
timevalue = datetime.timedelta(0)
1933
1601
suffix = unicode(s[-1])
1934
1602
value = int(s[:-1])
1936
1604
delta = datetime.timedelta(value)
1605
elif suffix == u"s":
1938
1606
delta = datetime.timedelta(0, value)
1607
elif suffix == u"m":
1940
1608
delta = datetime.timedelta(0, 0, 0, 0, value)
1609
elif suffix == u"h":
1942
1610
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1611
elif suffix == u"w":
1944
1612
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1946
raise ValueError("Unknown suffix %r" % suffix)
1947
except (ValueError, IndexError) as e:
1948
raise ValueError(*(e.args))
1614
raise ValueError(u"Unknown suffix %r" % suffix)
1615
except (ValueError, IndexError), e:
1616
raise ValueError(e.message)
1949
1617
timevalue += delta
1950
1618
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)
1953
1645
def daemon(nochdir = False, noclose = False):
1954
1646
"""See daemon(3). Standard BSD Unix function.
1980
1672
##################################################################
1981
1673
# Parsing of options, both command line and config file
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()
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]
2021
1704
if options.check:
2026
1709
# Default values for config file for server-global settings
2027
server_defaults = { "interface": "",
2032
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2033
"servicename": "Mandos",
2038
"statedir": "/var/lib/mandos"
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",
2041
1722
# Parse config file for server-global settings
2042
1723
server_config = configparser.SafeConfigParser(server_defaults)
2043
1724
del server_defaults
2044
1725
server_config.read(os.path.join(options.configdir,
2046
1727
# Convert the SafeConfigParser object to a dict
2047
1728
server_settings = server_config.defaults()
2048
1729
# Use the appropriate methods on the non-string config options
2049
for option in ("debug", "use_dbus", "use_ipv6"):
2050
server_settings[option] = server_config.getboolean("DEFAULT",
1730
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1731
server_settings[option] = server_config.getboolean(u"DEFAULT",
2052
1733
if server_settings["port"]:
2053
server_settings["port"] = server_config.getint("DEFAULT",
1734
server_settings["port"] = server_config.getint(u"DEFAULT",
2055
1736
del server_config
2057
1738
# Override the settings from the config file with command line
2058
1739
# options, if set.
2059
for option in ("interface", "address", "port", "debug",
2060
"priority", "servicename", "configdir",
2061
"use_dbus", "use_ipv6", "debuglevel", "restore",
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"):
2063
1743
value = getattr(options, option)
2064
1744
if value is not None:
2065
1745
server_settings[option] = value
2073
1753
##################################################################
2075
1755
# For convenience
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":
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":
2093
1762
syslogger.setFormatter(logging.Formatter
2094
('Mandos (%s) [%%(process)d]:'
2095
' %%(levelname)s: %%(message)s'
2096
% server_settings["servicename"]))
1763
(u'Mandos (%s) [%%(process)d]:'
1764
u' %%(levelname)s: %%(message)s'
1765
% server_settings[u"servicename"]))
2098
1767
# Parse config file with clients
2099
client_config = configparser.SafeConfigParser(Client
2101
client_config.read(os.path.join(server_settings["configdir"],
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"],
2104
1779
global mandos_dbus_service
2105
1780
mandos_dbus_service = None
2107
tcp_server = MandosServer((server_settings["address"],
2108
server_settings["port"]),
1782
tcp_server = MandosServer((server_settings[u"address"],
1783
server_settings[u"port"]),
2110
interface=(server_settings["interface"]
1785
interface=(server_settings[u"interface"]
2112
1787
use_ipv6=use_ipv6,
2113
1788
gnutls_priority=
2114
server_settings["priority"],
1789
server_settings[u"priority"],
2115
1790
use_dbus=use_dbus)
2117
pidfilename = "/var/run/mandos.pid"
1792
pidfilename = u"/var/run/mandos.pid"
2119
pidfile = open(pidfilename, "w")
1794
pidfile = open(pidfilename, u"w")
2120
1795
except IOError:
2121
logger.error("Could not open file %r", pidfilename)
1796
logger.error(u"Could not open file %r", pidfilename)
2124
uid = pwd.getpwnam("_mandos").pw_uid
2125
gid = pwd.getpwnam("_mandos").pw_gid
1799
uid = pwd.getpwnam(u"_mandos").pw_uid
1800
gid = pwd.getpwnam(u"_mandos").pw_gid
2126
1801
except KeyError:
2128
uid = pwd.getpwnam("mandos").pw_uid
2129
gid = pwd.getpwnam("mandos").pw_gid
1803
uid = pwd.getpwnam(u"mandos").pw_uid
1804
gid = pwd.getpwnam(u"mandos").pw_gid
2130
1805
except KeyError:
2132
uid = pwd.getpwnam("nobody").pw_uid
2133
gid = pwd.getpwnam("nobody").pw_gid
1807
uid = pwd.getpwnam(u"nobody").pw_uid
1808
gid = pwd.getpwnam(u"nobody").pw_gid
2134
1809
except KeyError:
2140
except OSError as error:
1815
except OSError, error:
2141
1816
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)
2145
1828
# Enable all possible GnuTLS debugging
2176
1860
# End of Avahi example code
2179
bus_name = dbus.service.BusName("se.recompile.Mandos",
1863
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
2180
1864
bus, do_not_queue=True)
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")
1865
except dbus.exceptions.NameExistsException, e:
1866
logger.error(unicode(e) + u", disabling D-Bus")
2186
1867
use_dbus = False
2187
server_settings["use_dbus"] = False
1868
server_settings[u"use_dbus"] = False
2188
1869
tcp_server.use_dbus = False
2189
1870
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2190
service = AvahiServiceToSyslog(name =
2191
server_settings["servicename"],
2192
servicetype = "_mandos._tcp",
2193
protocol = protocol, bus = bus)
1871
service = AvahiService(name = server_settings[u"servicename"],
1872
servicetype = u"_mandos._tcp",
1873
protocol = protocol, bus = bus)
2194
1874
if server_settings["interface"]:
2195
1875
service.interface = (if_nametoindex
2196
(str(server_settings["interface"])))
1876
(str(server_settings[u"interface"])))
2198
1878
global multiprocessing_manager
2199
1879
multiprocessing_manager = multiprocessing.Manager()
2201
1881
client_class = Client
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))
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):
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)
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()))
2298
1901
if not tcp_server.clients:
2299
logger.warning("No clients defined")
1902
logger.warning(u"No clients defined")
2304
1907
pid = os.getpid()
2305
pidfile.write(str(pid) + "\n".encode("utf-8"))
1908
pidfile.write(str(pid) + "\n")
2307
1910
except IOError:
2308
logger.error("Could not write to file %r with PID %d",
1911
logger.error(u"Could not write to file %r with PID %d",
2309
1912
pidfilename, pid)
2310
1913
except NameError:
2311
1914
# "pidfile" was never created
2313
1916
del pidfilename
2314
1918
signal.signal(signal.SIGINT, signal.SIG_IGN)
2316
1920
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2317
1921
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2320
1924
class MandosDBusService(dbus.service.Object):
2321
1925
"""A D-Bus proxy object"""
2322
1926
def __init__(self):
2323
dbus.service.Object.__init__(self, bus, "/")
2324
_interface = "se.recompile.Mandos"
1927
dbus.service.Object.__init__(self, bus, u"/")
1928
_interface = u"se.bsnet.fukt.Mandos"
2326
@dbus.service.signal(_interface, signature="o")
1930
@dbus.service.signal(_interface, signature=u"o")
2327
1931
def ClientAdded(self, objpath):
2331
@dbus.service.signal(_interface, signature="ss")
1935
@dbus.service.signal(_interface, signature=u"ss")
2332
1936
def ClientNotFound(self, fingerprint, address):
2336
@dbus.service.signal(_interface, signature="os")
1940
@dbus.service.signal(_interface, signature=u"os")
2337
1941
def ClientRemoved(self, objpath, name):
2341
@dbus.service.method(_interface, out_signature="ao")
1945
@dbus.service.method(_interface, out_signature=u"ao")
2342
1946
def GetAllClients(self):
2344
1948
return dbus.Array(c.dbus_object_path
2346
tcp_server.clients.itervalues())
1949
for c in tcp_server.clients)
2348
1951
@dbus.service.method(_interface,
2349
out_signature="a{oa{sv}}")
1952
out_signature=u"a{oa{sv}}")
2350
1953
def GetAllClientsWithProperties(self):
2352
1955
return dbus.Dictionary(
2353
((c.dbus_object_path, c.GetAll(""))
2354
for c in tcp_server.clients.itervalues()),
1956
((c.dbus_object_path, c.GetAll(u""))
1957
for c in tcp_server.clients),
1958
signature=u"oa{sv}")
2357
@dbus.service.method(_interface, in_signature="o")
1960
@dbus.service.method(_interface, in_signature=u"o")
2358
1961
def RemoveClient(self, object_path):
2360
for c in tcp_server.clients.itervalues():
1963
for c in tcp_server.clients:
2361
1964
if c.dbus_object_path == object_path:
2362
del tcp_server.clients[c.name]
1965
tcp_server.clients.remove(c)
2363
1966
c.remove_from_connection()
2364
1967
# Don't signal anything except ClientRemoved
2365
1968
c.disable(quiet=True)
2373
class MandosDBusServiceTransitional(MandosDBusService):
2374
__metaclass__ = AlternateDBusNamesMetaclass
2375
mandos_dbus_service = MandosDBusServiceTransitional()
1976
mandos_dbus_service = MandosDBusService()
2378
1979
"Cleanup function; run on exit"
2379
1980
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
2434
1982
while tcp_server.clients:
2435
name, client = tcp_server.clients.popitem()
1983
client = tcp_server.clients.pop()
2437
1985
client.remove_from_connection()
1986
client.disable_hook = None
2438
1987
# Don't signal anything except ClientRemoved
2439
1988
client.disable(quiet=True)
2441
1990
# Emit D-Bus signal
2442
mandos_dbus_service.ClientRemoved(client
1991
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2445
client_settings.clear()
2447
1994
atexit.register(cleanup)
2449
for client in tcp_server.clients.itervalues():
1996
for client in tcp_server.clients:
2451
1998
# Emit D-Bus signal
2452
1999
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2453
# Need to initiate checking of clients
2455
client.init_checker()
2457
2002
tcp_server.enable()
2458
2003
tcp_server.server_activate()