76
85
except ImportError:
77
86
SO_BINDTODEVICE = None
82
logger = logging.Logger(u'mandos')
89
stored_state_file = "clients.pickle"
91
logger = logging.getLogger()
83
92
syslogger = (logging.handlers.SysLogHandler
84
93
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
85
address = "/dev/log"))
86
syslogger.setFormatter(logging.Formatter
87
(u'Mandos [%(process)d]: %(levelname)s:'
89
logger.addHandler(syslogger)
91
console = logging.StreamHandler()
92
console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
95
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
97
212
class AvahiError(Exception):
98
213
def __init__(self, value, *args, **kwargs):
188
311
self.group.Commit()
189
312
def entry_group_state_changed(self, state, error):
190
313
"""Derived from the Avahi example code"""
191
logger.debug(u"Avahi state change: %i", state)
314
logger.debug("Avahi entry group state change: %i", state)
193
316
if state == avahi.ENTRY_GROUP_ESTABLISHED:
194
logger.debug(u"Zeroconf service established.")
317
logger.debug("Zeroconf service established.")
195
318
elif state == avahi.ENTRY_GROUP_COLLISION:
196
logger.warning(u"Zeroconf service name collision.")
319
logger.info("Zeroconf service name collision.")
198
321
elif state == avahi.ENTRY_GROUP_FAILURE:
199
logger.critical(u"Avahi: Error in group state changed %s",
322
logger.critical("Avahi: Error in group state changed %s",
201
raise AvahiGroupError(u"State changed: %s"
324
raise AvahiGroupError("State changed: %s"
202
325
% unicode(error))
203
326
def cleanup(self):
204
327
"""Derived from the Avahi example code"""
205
328
if self.group is not None:
331
except (dbus.exceptions.UnknownMethodException,
332
dbus.exceptions.DBusException):
207
334
self.group = None
208
def server_state_changed(self, state):
336
def server_state_changed(self, state, error=None):
209
337
"""Derived from the Avahi example code"""
210
if state == avahi.SERVER_COLLISION:
211
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)
213
353
elif state == avahi.SERVER_RUNNING:
357
logger.debug("Unknown state: %r", state)
359
logger.debug("Unknown state: %r: %r", state, error)
215
360
def activate(self):
216
361
"""Derived from the Avahi example code"""
217
362
if self.server is None:
218
363
self.server = dbus.Interface(
219
364
self.bus.get_object(avahi.DBUS_NAME,
220
avahi.DBUS_PATH_SERVER),
365
avahi.DBUS_PATH_SERVER,
366
follow_name_owner_changes=True),
221
367
avahi.DBUS_INTERFACE_SERVER)
222
self.server.connect_to_signal(u"StateChanged",
368
self.server.connect_to_signal("StateChanged",
223
369
self.server_state_changed)
224
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))
227
388
class Client(object):
228
389
"""A representation of a client host served by this server.
231
name: string; from the config file, used in log messages and
233
fingerprint: string (40 or 32 hexadecimal digits); used to
234
uniquely identify the client
235
secret: bytestring; sent verbatim (over TLS) to client
236
host: string; available for use by the checker command
237
created: datetime.datetime(); (UTC) object creation
238
last_enabled: datetime.datetime(); (UTC)
240
last_checked_ok: datetime.datetime(); (UTC) or None
241
timeout: datetime.timedelta(); How long from last_checked_ok
242
until this client is invalid
243
interval: datetime.timedelta(); How often to start a new checker
244
disable_hook: If set, called by disable() as disable_hook(self)
392
approved: bool(); 'None' if not yet approved/disapproved
393
approval_delay: datetime.timedelta(); Time to wait for approval
394
approval_duration: datetime.timedelta(); Duration of one approval
245
395
checker: subprocess.Popen(); a running checker process used
246
396
to see if the client lives.
247
397
'None' if no process is running.
248
checker_initiator_tag: a gobject event source tag, or None
249
disable_initiator_tag: - '' -
250
checker_callback_tag: - '' -
251
checker_command: string; External command which is run to check if
252
client lives. %() expansions are done at
398
checker_callback_tag: a gobject event source tag, or None
399
checker_command: string; External command which is run to check
400
if client lives. %() expansions are done at
253
401
runtime with vars(self) as dict, so that for
254
402
instance %(name)s can be used in the command.
403
checker_initiator_tag: a gobject event source tag, or None
404
created: datetime.datetime(); (UTC) object creation
405
client_structure: Object describing what attributes a client has
406
and is used for storing the client at exit
255
407
current_checker_command: string; current running checker_command
408
disable_initiator_tag: a gobject event source tag, or None
410
fingerprint: string (40 or 32 hexadecimal digits); used to
411
uniquely identify the client
412
host: string; available for use by the checker command
413
interval: datetime.timedelta(); How often to start a new checker
414
last_approval_request: datetime.datetime(); (UTC) or None
415
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
420
name: string; from the config file, used in log messages and
422
secret: bytestring; sent verbatim (over TLS) to client
423
timeout: datetime.timedelta(); How long from last_checked_ok
424
until this client is disabled
425
extended_timeout: extra long timeout when secret has been sent
426
runtime_expansions: Allowed attributes for runtime expansion.
427
expires: datetime.datetime(); time (UTC) when a client will be
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.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",
265
446
def timeout_milliseconds(self):
266
447
"Return the 'timeout' attribute in milliseconds"
267
return self._datetime_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)
269
454
def interval_milliseconds(self):
270
455
"Return the 'interval' attribute in milliseconds"
271
return self._datetime_to_milliseconds(self.interval)
456
return timedelta_to_milliseconds(self.interval)
273
def __init__(self, name = None, disable_hook=None, config=None):
458
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):
274
510
"""Note: the 'checker' key in 'config' sets the
275
511
'checker_command' attribute and *not* the 'checker'
280
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)
281
529
# Uppercase and remove spaces from fingerprint for later
282
530
# comparison purposes with return value from the fingerprint()
284
self.fingerprint = (config[u"fingerprint"].upper()
286
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
290
with closing(open(os.path.expanduser
292
(config[u"secfile"])))) as secfile:
293
self.secret = secfile.read()
295
raise TypeError(u"No secret or secfile for client %s"
297
self.host = config.get(u"host", u"")
298
self.created = datetime.datetime.utcnow()
300
self.last_enabled = None
301
self.last_checked_ok = None
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
304
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
305
537
self.checker = None
306
538
self.checker_initiator_tag = None
307
539
self.disable_initiator_tag = None
308
540
self.checker_callback_tag = None
309
self.checker_command = config[u"checker"]
310
541
self.current_checker_command = None
311
self.last_connect = None
543
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)
559
# Send notice to process children that client state has changed
560
def send_changedstate(self):
561
with self.changedstate:
562
self.changedstate.notify_all()
313
564
def enable(self):
314
565
"""Start this client's checker and timeout hooks"""
566
if getattr(self, "enabled", False):
569
self.send_changedstate()
570
self.expires = datetime.datetime.utcnow() + self.timeout
315
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):
316
599
# Schedule a new checker to be started an 'interval' from now,
317
600
# and every interval from then on.
318
601
self.checker_initiator_tag = (gobject.timeout_add
319
602
(self.interval_milliseconds(),
320
603
self.start_checker))
321
# Also start a new checker *right now*.
323
604
# Schedule a disable() when 'timeout' has passed
324
605
self.disable_initiator_tag = (gobject.timeout_add
325
606
(self.timeout_milliseconds(),
330
"""Disable this client."""
331
if not getattr(self, "enabled", False):
333
logger.info(u"Disabling client %s", self.name)
334
if getattr(self, u"disable_initiator_tag", False):
335
gobject.source_remove(self.disable_initiator_tag)
336
self.disable_initiator_tag = None
337
if getattr(self, u"checker_initiator_tag", False):
338
gobject.source_remove(self.checker_initiator_tag)
339
self.checker_initiator_tag = None
341
if self.disable_hook:
342
self.disable_hook(self)
344
# Do not run this again if called by a gobject.timeout_add
348
self.disable_hook = None
608
# Also start a new checker *right now*.
351
611
def checker_callback(self, pid, condition, command):
352
612
"""The checker has completed, so take appropriate actions."""
353
613
self.checker_callback_tag = None
354
614
self.checker = None
355
615
if os.WIFEXITED(condition):
356
exitstatus = os.WEXITSTATUS(condition)
358
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",
360
620
self.checked_ok()
362
logger.info(u"Checker for %(name)s failed",
622
logger.info("Checker for %(name)s failed",
365
logger.warning(u"Checker for %(name)s crashed?",
625
self.last_checker_status = -1
626
logger.warning("Checker for %(name)s crashed?",
368
629
def checked_ok(self):
369
"""Bump up the timeout for this client.
371
This should only be called when the client has been seen,
630
"""Assert that the client has been seen, alive and well."""
374
631
self.last_checked_ok = datetime.datetime.utcnow()
375
gobject.source_remove(self.disable_initiator_tag)
376
self.disable_initiator_tag = (gobject.timeout_add
377
(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
647
def need_approval(self):
648
self.last_approval_request = datetime.datetime.utcnow()
380
650
def start_checker(self):
381
651
"""Start a new checker subprocess if one is not running.
450
727
if self.checker_callback_tag:
451
728
gobject.source_remove(self.checker_callback_tag)
452
729
self.checker_callback_tag = None
453
if getattr(self, u"checker", None) is None:
730
if getattr(self, "checker", None) is None:
455
logger.debug(u"Stopping checker for %(name)s", vars(self))
732
logger.debug("Stopping checker for %(name)s", vars(self))
457
734
os.kill(self.checker.pid, signal.SIGTERM)
459
736
#if self.checker.poll() is None:
460
737
# os.kill(self.checker.pid, signal.SIGKILL)
461
except OSError, error:
738
except OSError as error:
462
739
if error.errno != errno.ESRCH: # No such process
464
741
self.checker = None
466
def still_valid(self):
467
"""Has the timeout not yet passed for this client?"""
468
if not getattr(self, u"enabled", False):
470
now = datetime.datetime.utcnow()
471
if self.last_checked_ok is None:
472
return now < (self.created + self.timeout)
474
return now < (self.last_checked_ok + self.timeout)
477
class ClientDBus(Client, dbus.service.Object):
744
def dbus_service_property(dbus_interface, signature="v",
745
access="readwrite", byte_arrays=False):
746
"""Decorators for marking methods of a DBusObjectWithProperties to
747
become properties on the D-Bus.
749
The decorated method will be called with no arguments by "Get"
750
and with one argument by "Set".
752
The parameters, where they are supported, are the same as
753
dbus.service.method, except there is only "signature", since the
754
type from Get() and the type sent to Set() is the same.
756
# Encoding deeply encoded byte arrays is not supported yet by the
757
# "Set" method, so we fail early here:
758
if byte_arrays and signature != "ay":
759
raise ValueError("Byte arrays not supported for non-'ay'"
760
" signature %r" % signature)
762
func._dbus_is_property = True
763
func._dbus_interface = dbus_interface
764
func._dbus_signature = signature
765
func._dbus_access = access
766
func._dbus_name = func.__name__
767
if func._dbus_name.endswith("_dbus_property"):
768
func._dbus_name = func._dbus_name[:-14]
769
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
774
class DBusPropertyException(dbus.exceptions.DBusException):
775
"""A base class for D-Bus property-related exceptions
777
def __unicode__(self):
778
return unicode(str(self))
781
class DBusPropertyAccessException(DBusPropertyException):
782
"""A property's access permissions disallows an operation.
787
class DBusPropertyNotFound(DBusPropertyException):
788
"""An attempt was made to access a non-existing property.
793
class DBusObjectWithProperties(dbus.service.Object):
794
"""A D-Bus object with properties.
796
Classes inheriting from this can use the dbus_service_property
797
decorator to expose methods as D-Bus properties. It exposes the
798
standard Get(), Set(), and GetAll() methods on the D-Bus.
802
def _is_dbus_property(obj):
803
return getattr(obj, "_dbus_is_property", False)
805
def _get_all_dbus_properties(self):
806
"""Returns a generator of (name, attribute) pairs
808
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
809
for cls in self.__class__.__mro__
811
inspect.getmembers(cls, self._is_dbus_property))
813
def _get_dbus_property(self, interface_name, property_name):
814
"""Returns a bound method if one exists which is a D-Bus
815
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)
825
raise DBusPropertyNotFound(self.dbus_object_path + ":"
826
+ interface_name + "."
829
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
831
def Get(self, interface_name, property_name):
832
"""Standard D-Bus property Get() method, see D-Bus standard.
834
prop = self._get_dbus_property(interface_name, property_name)
835
if prop._dbus_access == "write":
836
raise DBusPropertyAccessException(property_name)
838
if not hasattr(value, "variant_level"):
840
return type(value)(value, variant_level=value.variant_level+1)
842
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
843
def Set(self, interface_name, property_name, value):
844
"""Standard D-Bus property Set() method, see D-Bus standard.
846
prop = self._get_dbus_property(interface_name, property_name)
847
if prop._dbus_access == "read":
848
raise DBusPropertyAccessException(property_name)
849
if prop._dbus_get_args_options["byte_arrays"]:
850
# The byte_arrays option is not supported yet on
851
# signatures other than "ay".
852
if prop._dbus_signature != "ay":
854
value = dbus.ByteArray(b''.join(chr(byte)
858
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
859
out_signature="a{sv}")
860
def GetAll(self, interface_name):
861
"""Standard D-Bus property GetAll() method, see D-Bus
864
Note: Will not include properties with access="write".
867
for name, prop in self._get_all_dbus_properties():
869
and interface_name != prop._dbus_interface):
870
# Interface non-empty but did not match
872
# Ignore write-only properties
873
if prop._dbus_access == "write":
876
if not hasattr(value, "variant_level"):
877
properties[name] = value
879
properties[name] = type(value)(value, variant_level=
880
value.variant_level+1)
881
return dbus.Dictionary(properties, signature="sv")
883
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
885
path_keyword='object_path',
886
connection_keyword='connection')
887
def Introspect(self, object_path, connection):
888
"""Standard D-Bus method, overloaded to insert property tags.
890
xmlstring = dbus.service.Object.Introspect(self, object_path,
893
document = xml.dom.minidom.parseString(xmlstring)
894
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)
900
for if_tag in document.getElementsByTagName("interface"):
901
for tag in (make_tag(document, name, prop)
903
in self._get_all_dbus_properties()
904
if prop._dbus_interface
905
== if_tag.getAttribute("name")):
906
if_tag.appendChild(tag)
907
# Add the names to the return values for the
908
# "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")
924
except (AttributeError, xml.dom.DOMException,
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)
1037
class ClientDBus(Client, DBusObjectWithProperties):
478
1038
"""A Client class using D-Bus
481
1041
dbus_object_path: dbus.ObjectPath
482
1042
bus: dbus.SystemBus()
1045
runtime_expansions = (Client.runtime_expansions
1046
+ ("dbus_object_path",))
484
1048
# dbus.service.Object doesn't use super(), so we can't either.
486
1050
def __init__(self, bus = None, *args, **kwargs):
488
1052
Client.__init__(self, *args, **kwargs)
489
1053
# Only now, when this client is initialized, can it show up on
1055
client_object_name = unicode(self.name).translate(
1056
{ord("."): ord("_"),
1057
ord("-"): ord("_")})
491
1058
self.dbus_object_path = (dbus.ObjectPath
493
+ self.name.replace(u".", u"_")))
494
dbus.service.Object.__init__(self, self.bus,
495
self.dbus_object_path)
498
def _datetime_to_dbus(dt, variant_level=0):
499
"""Convert a UTC datetime.datetime() to a D-Bus type."""
500
return dbus.String(dt.isoformat(),
501
variant_level=variant_level)
504
oldstate = getattr(self, u"enabled", False)
505
r = Client.enable(self)
506
if oldstate != self.enabled:
508
self.PropertyChanged(dbus.String(u"enabled"),
509
dbus.Boolean(True, variant_level=1))
510
self.PropertyChanged(
511
dbus.String(u"last_enabled"),
512
self._datetime_to_dbus(self.last_enabled,
516
def disable(self, signal = True):
517
oldstate = getattr(self, u"enabled", False)
518
r = Client.disable(self)
519
if signal and oldstate != self.enabled:
521
self.PropertyChanged(dbus.String(u"enabled"),
522
dbus.Boolean(False, variant_level=1))
1059
("/clients/" + client_object_name))
1060
DBusObjectWithProperties.__init__(self, self.bus,
1061
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
525
1132
def __del__(self, *args, **kwargs):
527
1134
self.remove_from_connection()
528
1135
except LookupError:
530
if hasattr(dbus.service.Object, u"__del__"):
531
dbus.service.Object.__del__(self, *args, **kwargs)
1137
if hasattr(DBusObjectWithProperties, "__del__"):
1138
DBusObjectWithProperties.__del__(self, *args, **kwargs)
532
1139
Client.__del__(self, *args, **kwargs)
534
1141
def checker_callback(self, pid, condition, command,
535
1142
*args, **kwargs):
536
1143
self.checker_callback_tag = None
537
1144
self.checker = None
539
self.PropertyChanged(dbus.String(u"checker_running"),
540
dbus.Boolean(False, variant_level=1))
541
1145
if os.WIFEXITED(condition):
542
1146
exitstatus = os.WEXITSTATUS(condition)
543
1147
# Emit D-Bus signal
574
1169
and old_checker_pid != self.checker.pid):
575
1170
# Emit D-Bus signal
576
1171
self.CheckerStarted(self.current_checker_command)
577
self.PropertyChanged(
578
dbus.String(u"checker_running"),
579
dbus.Boolean(True, variant_level=1))
582
def stop_checker(self, *args, **kwargs):
583
old_checker = getattr(self, u"checker", None)
584
r = Client.stop_checker(self, *args, **kwargs)
585
if (old_checker is not None
586
and getattr(self, u"checker", None) is None):
587
self.PropertyChanged(dbus.String(u"checker_running"),
588
dbus.Boolean(False, variant_level=1))
591
## D-Bus methods & signals
592
_interface = u"se.bsnet.fukt.Mandos.Client"
595
@dbus.service.method(_interface)
597
return self.checked_ok()
1174
def _reset_approved(self):
1175
self.approved = None
1178
def approve(self, value=True):
1179
self.send_changedstate()
1180
self.approved = value
1181
gobject.timeout_add(timedelta_to_milliseconds
1182
(self.approval_duration),
1183
self._reset_approved)
1186
## D-Bus methods, signals & properties
1187
_interface = "se.recompile.Mandos.Client"
599
1191
# CheckerCompleted - signal
600
@dbus.service.signal(_interface, signature=u"nxs")
1192
@dbus.service.signal(_interface, signature="nxs")
601
1193
def CheckerCompleted(self, exitcode, waitstatus, command):
605
1197
# CheckerStarted - signal
606
@dbus.service.signal(_interface, signature=u"s")
1198
@dbus.service.signal(_interface, signature="s")
607
1199
def CheckerStarted(self, command):
611
# GetAllProperties - method
612
@dbus.service.method(_interface, out_signature=u"a{sv}")
613
def GetAllProperties(self):
615
return dbus.Dictionary({
616
dbus.String(u"name"):
617
dbus.String(self.name, variant_level=1),
618
dbus.String(u"fingerprint"):
619
dbus.String(self.fingerprint, variant_level=1),
620
dbus.String(u"host"):
621
dbus.String(self.host, variant_level=1),
622
dbus.String(u"created"):
623
self._datetime_to_dbus(self.created,
625
dbus.String(u"last_enabled"):
626
(self._datetime_to_dbus(self.last_enabled,
628
if self.last_enabled is not None
629
else dbus.Boolean(False, variant_level=1)),
630
dbus.String(u"enabled"):
631
dbus.Boolean(self.enabled, variant_level=1),
632
dbus.String(u"last_checked_ok"):
633
(self._datetime_to_dbus(self.last_checked_ok,
635
if self.last_checked_ok is not None
636
else dbus.Boolean (False, variant_level=1)),
637
dbus.String(u"timeout"):
638
dbus.UInt64(self.timeout_milliseconds(),
640
dbus.String(u"interval"):
641
dbus.UInt64(self.interval_milliseconds(),
643
dbus.String(u"checker"):
644
dbus.String(self.checker_command,
646
dbus.String(u"checker_running"):
647
dbus.Boolean(self.checker is not None,
649
dbus.String(u"object_path"):
650
dbus.ObjectPath(self.dbus_object_path,
654
# IsStillValid - method
655
@dbus.service.method(_interface, out_signature=u"b")
656
def IsStillValid(self):
657
return self.still_valid()
659
1203
# PropertyChanged - signal
660
@dbus.service.signal(_interface, signature=u"sv")
1204
@dbus.service.signal(_interface, signature="sv")
661
1205
def PropertyChanged(self, property, value):
665
# ReceivedSecret - signal
1209
# GotSecret - signal
666
1210
@dbus.service.signal(_interface)
667
def ReceivedSecret(self):
1211
def GotSecret(self):
1213
Is sent after a successful transfer of secret from the Mandos
1214
server to mandos-client
671
1218
# Rejected - signal
672
@dbus.service.signal(_interface)
1219
@dbus.service.signal(_interface, signature="s")
1220
def Rejected(self, reason):
677
# SetChecker - method
678
@dbus.service.method(_interface, in_signature=u"s")
679
def SetChecker(self, checker):
680
"D-Bus setter method"
681
self.checker_command = checker
683
self.PropertyChanged(dbus.String(u"checker"),
684
dbus.String(self.checker_command,
688
@dbus.service.method(_interface, in_signature=u"s")
689
def SetHost(self, host):
690
"D-Bus setter method"
693
self.PropertyChanged(dbus.String(u"host"),
694
dbus.String(self.host, variant_level=1))
696
# SetInterval - method
697
@dbus.service.method(_interface, in_signature=u"t")
698
def SetInterval(self, milliseconds):
699
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
701
self.PropertyChanged(dbus.String(u"interval"),
702
(dbus.UInt64(self.interval_milliseconds(),
706
@dbus.service.method(_interface, in_signature=u"ay",
708
def SetSecret(self, secret):
709
"D-Bus setter method"
710
self.secret = str(secret)
712
# SetTimeout - method
713
@dbus.service.method(_interface, in_signature=u"t")
714
def SetTimeout(self, milliseconds):
715
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
717
self.PropertyChanged(dbus.String(u"timeout"),
718
(dbus.UInt64(self.timeout_milliseconds(),
1224
# NeedApproval - signal
1225
@dbus.service.signal(_interface, signature="tb")
1226
def NeedApproval(self, timeout, default):
1228
return self.need_approval()
1233
@dbus.service.method(_interface, in_signature="b")
1234
def Approve(self, value):
1237
# CheckedOK - method
1238
@dbus.service.method(_interface)
1239
def CheckedOK(self):
721
1242
# Enable - method
722
1243
@dbus.service.method(_interface)
741
1262
def StopChecker(self):
742
1263
self.stop_checker()
1267
# ApprovalPending - property
1268
@dbus_service_property(_interface, signature="b", access="read")
1269
def ApprovalPending_dbus_property(self):
1270
return dbus.Boolean(bool(self.approvals_pending))
1272
# ApprovedByDefault - property
1273
@dbus_service_property(_interface, signature="b",
1275
def ApprovedByDefault_dbus_property(self, value=None):
1276
if value is None: # get
1277
return dbus.Boolean(self.approved_by_default)
1278
self.approved_by_default = bool(value)
1280
# ApprovalDelay - property
1281
@dbus_service_property(_interface, signature="t",
1283
def ApprovalDelay_dbus_property(self, value=None):
1284
if value is None: # get
1285
return dbus.UInt64(self.approval_delay_milliseconds())
1286
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1288
# ApprovalDuration - property
1289
@dbus_service_property(_interface, signature="t",
1291
def ApprovalDuration_dbus_property(self, value=None):
1292
if value is None: # get
1293
return dbus.UInt64(timedelta_to_milliseconds(
1294
self.approval_duration))
1295
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1298
@dbus_service_property(_interface, signature="s", access="read")
1299
def Name_dbus_property(self):
1300
return dbus.String(self.name)
1302
# Fingerprint - property
1303
@dbus_service_property(_interface, signature="s", access="read")
1304
def Fingerprint_dbus_property(self):
1305
return dbus.String(self.fingerprint)
1308
@dbus_service_property(_interface, signature="s",
1310
def Host_dbus_property(self, value=None):
1311
if value is None: # get
1312
return dbus.String(self.host)
1313
self.host = unicode(value)
1315
# Created - property
1316
@dbus_service_property(_interface, signature="s", access="read")
1317
def Created_dbus_property(self):
1318
return datetime_to_dbus(self.created)
1320
# LastEnabled - property
1321
@dbus_service_property(_interface, signature="s", access="read")
1322
def LastEnabled_dbus_property(self):
1323
return datetime_to_dbus(self.last_enabled)
1325
# Enabled - property
1326
@dbus_service_property(_interface, signature="b",
1328
def Enabled_dbus_property(self, value=None):
1329
if value is None: # get
1330
return dbus.Boolean(self.enabled)
1336
# LastCheckedOK - property
1337
@dbus_service_property(_interface, signature="s",
1339
def LastCheckedOK_dbus_property(self, value=None):
1340
if value is not None:
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)
1356
# LastApprovalRequest - property
1357
@dbus_service_property(_interface, signature="s", access="read")
1358
def LastApprovalRequest_dbus_property(self):
1359
return datetime_to_dbus(self.last_approval_request)
1361
# Timeout - property
1362
@dbus_service_property(_interface, signature="t",
1364
def Timeout_dbus_property(self, value=None):
1365
if value is None: # get
1366
return dbus.UInt64(self.timeout_milliseconds())
1367
self.timeout = datetime.timedelta(0, 0, 0, value)
1368
# 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)
1396
# Interval - property
1397
@dbus_service_property(_interface, signature="t",
1399
def Interval_dbus_property(self, value=None):
1400
if value is None: # get
1401
return dbus.UInt64(self.interval_milliseconds())
1402
self.interval = datetime.timedelta(0, 0, 0, value)
1403
if getattr(self, "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
1412
# Checker - property
1413
@dbus_service_property(_interface, signature="s",
1415
def Checker_dbus_property(self, value=None):
1416
if value is None: # get
1417
return dbus.String(self.checker_command)
1418
self.checker_command = unicode(value)
1420
# CheckerRunning - property
1421
@dbus_service_property(_interface, signature="b",
1423
def CheckerRunning_dbus_property(self, value=None):
1424
if value is None: # get
1425
return dbus.Boolean(self.checker is not None)
1427
self.start_checker()
1431
# ObjectPath - property
1432
@dbus_service_property(_interface, signature="o", access="read")
1433
def ObjectPath_dbus_property(self):
1434
return self.dbus_object_path # is already a dbus.ObjectPath
1437
@dbus_service_property(_interface, signature="ay",
1438
access="write", byte_arrays=True)
1439
def Secret_dbus_property(self, value):
1440
self.secret = str(value)
1445
class ProxyClient(object):
1446
def __init__(self, child_pipe, fpr, address):
1447
self._pipe = child_pipe
1448
self._pipe.send(('init', fpr, address))
1449
if not self._pipe.recv():
1452
def __getattribute__(self, name):
1454
return super(ProxyClient, self).__getattribute__(name)
1455
self._pipe.send(('getattr', name))
1456
data = self._pipe.recv()
1457
if data[0] == 'data':
1459
if data[0] == 'function':
1460
def func(*args, **kwargs):
1461
self._pipe.send(('funcall', name, args, kwargs))
1462
return self._pipe.recv()[1]
1465
def __setattr__(self, name, value):
1467
return super(ProxyClient, self).__setattr__(name, value)
1468
self._pipe.send(('setattr', name, value))
1471
class ClientDBusTransitional(ClientDBus):
1472
__metaclass__ = AlternateDBusNamesMetaclass
747
1475
class ClientHandler(socketserver.BaseRequestHandler, object):
748
1476
"""A class to handle client connections.
751
1479
Note: This will run in its own forked process."""
753
1481
def handle(self):
754
logger.info(u"TCP connection from: %s",
755
unicode(self.client_address))
756
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
757
# Open IPC pipe to parent process
758
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1482
with contextlib.closing(self.server.child_pipe) as child_pipe:
1483
logger.info("TCP connection from: %s",
1484
unicode(self.client_address))
1485
logger.debug("Pipe FD: %d",
1486
self.server.child_pipe.fileno())
759
1488
session = (gnutls.connection
760
1489
.ClientSession(self.request,
761
1490
gnutls.connection
762
1491
.X509Credentials()))
764
line = self.request.makefile().readline()
765
logger.debug(u"Protocol version: %r", line)
767
if int(line.strip().split()[0]) > 1:
769
except (ValueError, IndexError, RuntimeError), error:
770
logger.error(u"Unknown protocol version: %s", error)
773
1493
# Note: gnutls.connection.X509Credentials is really a
774
1494
# generic GnuTLS certificate credentials object so long as
775
1495
# no X.509 keys are added to it. Therefore, we can use it
776
1496
# here despite using OpenPGP certificates.
778
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
779
# u"+AES-256-CBC", u"+SHA1",
780
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1498
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1499
# "+AES-256-CBC", "+SHA1",
1500
# "+COMP-NULL", "+CTYPE-OPENPGP",
782
1502
# Use a fallback default, since this MUST be set.
783
1503
priority = self.server.gnutls_priority
784
1504
if priority is None:
786
1506
(gnutls.library.functions
787
1507
.gnutls_priority_set_direct(session._c_object,
788
1508
priority, None))
1510
# Start communication using the Mandos protocol
1511
# Get protocol number
1512
line = self.request.makefile().readline()
1513
logger.debug("Protocol version: %r", line)
1515
if int(line.strip().split()[0]) > 1:
1517
except (ValueError, IndexError, RuntimeError) as error:
1518
logger.error("Unknown protocol version: %s", error)
1521
# Start GnuTLS connection
791
1523
session.handshake()
792
except gnutls.errors.GNUTLSError, error:
793
logger.warning(u"Handshake failed: %s", error)
1524
except gnutls.errors.GNUTLSError as error:
1525
logger.warning("Handshake failed: %s", error)
794
1526
# Do not run session.bye() here: the session is not
795
1527
# established. Just abandon the request.
797
logger.debug(u"Handshake succeeded")
1529
logger.debug("Handshake succeeded")
1531
approval_required = False
799
fpr = self.fingerprint(self.peer_certificate(session))
800
except (TypeError, gnutls.errors.GNUTLSError), error:
801
logger.warning(u"Bad certificate: %s", error)
804
logger.debug(u"Fingerprint: %s", fpr)
1534
fpr = self.fingerprint(self.peer_certificate
1537
gnutls.errors.GNUTLSError) as error:
1538
logger.warning("Bad certificate: %s", error)
1540
logger.debug("Fingerprint: %s", fpr)
1543
client = ProxyClient(child_pipe, fpr,
1544
self.client_address)
1548
if client.approval_delay:
1549
delay = client.approval_delay
1550
client.approvals_pending += 1
1551
approval_required = True
1554
if not client.enabled:
1555
logger.info("Client %s is disabled",
1557
if self.server.use_dbus:
1559
client.Rejected("Disabled")
1562
if client.approved or not client.approval_delay:
1563
#We are approved or approval is disabled
1565
elif client.approved is None:
1566
logger.info("Client %s needs approval",
1568
if self.server.use_dbus:
1570
client.NeedApproval(
1571
client.approval_delay_milliseconds(),
1572
client.approved_by_default)
1574
logger.warning("Client %s was not approved",
1576
if self.server.use_dbus:
1578
client.Rejected("Denied")
1581
#wait until timeout or approved
1582
time = datetime.datetime.now()
1583
client.changedstate.acquire()
1584
(client.changedstate.wait
1585
(float(client.timedelta_to_milliseconds(delay)
1587
client.changedstate.release()
1588
time2 = datetime.datetime.now()
1589
if (time2 - time) >= delay:
1590
if not client.approved_by_default:
1591
logger.warning("Client %s timed out while"
1592
" waiting for approval",
1594
if self.server.use_dbus:
1596
client.Rejected("Approval timed out")
1601
delay -= time2 - time
1604
while sent_size < len(client.secret):
1606
sent = session.send(client.secret[sent_size:])
1607
except gnutls.errors.GNUTLSError as error:
1608
logger.warning("gnutls send failed")
1610
logger.debug("Sent: %d, remaining: %d",
1611
sent, len(client.secret)
1612
- (sent_size + sent))
1615
logger.info("Sending secret to %s", client.name)
1616
# bump the timeout using extended_timeout
1617
client.bump_timeout(client.extended_timeout)
1618
if self.server.use_dbus:
806
for c in self.server.clients:
807
if c.fingerprint == fpr:
811
ipc.write(u"NOTFOUND %s\n" % fpr)
814
# Have to check if client.still_valid(), since it is
815
# possible that the client timed out while establishing
816
# the GnuTLS session.
817
if not client.still_valid():
818
ipc.write(u"INVALID %s\n" % client.name)
821
ipc.write(u"SENDING %s\n" % client.name)
823
while sent_size < len(client.secret):
824
sent = session.send(client.secret[sent_size:])
825
logger.debug(u"Sent: %d, remaining: %d",
826
sent, len(client.secret)
827
- (sent_size + sent))
1623
if approval_required:
1624
client.approvals_pending -= 1
1627
except gnutls.errors.GNUTLSError as error:
1628
logger.warning("GnuTLS bye failed")
832
1631
def peer_certificate(session):
1020
1846
for cond, name in
1021
1847
condition_names.iteritems()
1022
1848
if cond & condition)
1023
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1026
# Turn the pipe file descriptor into a Python file object
1027
if source not in file_objects:
1028
file_objects[source] = os.fdopen(source, u"r", 1)
1030
# Read a line from the file object
1031
cmdline = file_objects[source].readline()
1032
if not cmdline: # Empty line means end of file
1033
# close the IPC pipe
1034
file_objects[source].close()
1035
del file_objects[source]
1037
# Stop calling this function
1040
logger.debug(u"IPC command: %r", cmdline)
1042
# Parse and act on command
1043
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1045
if cmd == u"NOTFOUND":
1046
logger.warning(u"Client not found for fingerprint: %s",
1050
mandos_dbus_service.ClientNotFound(args)
1051
elif cmd == u"INVALID":
1052
for client in self.clients:
1053
if client.name == args:
1054
logger.warning(u"Client %s is invalid", args)
1060
logger.error(u"Unknown client %s is invalid", args)
1061
elif cmd == u"SENDING":
1062
for client in self.clients:
1063
if client.name == args:
1064
logger.info(u"Sending secret to %s", client.name)
1068
client.ReceivedSecret()
1071
logger.error(u"Sending secret to unknown client %s",
1074
logger.error(u"Unknown IPC command: %r", cmdline)
1076
# Keep calling this function
1849
# error, or the other end of multiprocessing.Pipe has closed
1850
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1851
# Wait for other process to exit
1855
# Read a request from the child
1856
request = parent_pipe.recv()
1857
command = request[0]
1859
if command == 'init':
1861
address = request[2]
1863
for c in self.clients.itervalues():
1864
if c.fingerprint == fpr:
1868
logger.info("Client not found for fingerprint: %s, ad"
1869
"dress: %s", fpr, address)
1872
mandos_dbus_service.ClientNotFound(fpr,
1874
parent_pipe.send(False)
1877
gobject.io_add_watch(parent_pipe.fileno(),
1878
gobject.IO_IN | gobject.IO_HUP,
1879
functools.partial(self.handle_ipc,
1885
parent_pipe.send(True)
1886
# remove the old hook in favor of the new above hook on
1889
if command == 'funcall':
1890
funcname = request[1]
1894
parent_pipe.send(('data', getattr(client_object,
1898
if command == 'getattr':
1899
attrname = request[1]
1900
if callable(client_object.__getattribute__(attrname)):
1901
parent_pipe.send(('function',))
1903
parent_pipe.send(('data', client_object
1904
.__getattribute__(attrname)))
1906
if command == 'setattr':
1907
attrname = request[1]
1909
setattr(client_object, attrname, value)
1080
1914
def string_to_delta(interval):
1081
1915
"""Parse a string and return a datetime.timedelta
1083
>>> string_to_delta(u'7d')
1917
>>> string_to_delta('7d')
1084
1918
datetime.timedelta(7)
1085
>>> string_to_delta(u'60s')
1919
>>> string_to_delta('60s')
1086
1920
datetime.timedelta(0, 60)
1087
>>> string_to_delta(u'60m')
1921
>>> string_to_delta('60m')
1088
1922
datetime.timedelta(0, 3600)
1089
>>> string_to_delta(u'24h')
1923
>>> string_to_delta('24h')
1090
1924
datetime.timedelta(1)
1091
>>> string_to_delta(u'1w')
1925
>>> string_to_delta('1w')
1092
1926
datetime.timedelta(7)
1093
>>> string_to_delta(u'5m 30s')
1927
>>> string_to_delta('5m 30s')
1094
1928
datetime.timedelta(0, 330)
1096
1930
timevalue = datetime.timedelta(0)
1169
######################################################################
1980
##################################################################
1170
1981
# Parsing of options, both command line and config file
1172
parser = optparse.OptionParser(version = "%%prog %s" % version)
1173
parser.add_option("-i", u"--interface", type=u"string",
1174
metavar="IF", help=u"Bind to interface IF")
1175
parser.add_option("-a", u"--address", type=u"string",
1176
help=u"Address to listen for requests on")
1177
parser.add_option("-p", u"--port", type=u"int",
1178
help=u"Port number to receive requests on")
1179
parser.add_option("--check", action=u"store_true",
1180
help=u"Run self-test")
1181
parser.add_option("--debug", action=u"store_true",
1182
help=u"Debug mode; run in foreground and log to"
1184
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1185
u" priority string (see GnuTLS documentation)")
1186
parser.add_option("--servicename", type=u"string",
1187
metavar=u"NAME", help=u"Zeroconf service name")
1188
parser.add_option("--configdir", type=u"string",
1189
default=u"/etc/mandos", metavar=u"DIR",
1190
help=u"Directory to search for configuration"
1192
parser.add_option("--no-dbus", action=u"store_false",
1193
dest=u"use_dbus", help=u"Do not provide D-Bus"
1194
u" system bus interface")
1195
parser.add_option("--no-ipv6", action=u"store_false",
1196
dest=u"use_ipv6", help=u"Do not use IPv6")
1197
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()
1199
2021
if options.check:
1247
2073
##################################################################
1249
2075
# For convenience
1250
debug = server_settings[u"debug"]
1251
use_dbus = server_settings[u"use_dbus"]
1252
use_ipv6 = server_settings[u"use_ipv6"]
1255
syslogger.setLevel(logging.WARNING)
1256
console.setLevel(logging.WARNING)
1258
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":
1259
2093
syslogger.setFormatter(logging.Formatter
1260
(u'Mandos (%s) [%%(process)d]:'
1261
u' %%(levelname)s: %%(message)s'
1262
% server_settings[u"servicename"]))
2094
('Mandos (%s) [%%(process)d]:'
2095
' %%(levelname)s: %%(message)s'
2096
% server_settings["servicename"]))
1264
2098
# Parse config file with clients
1265
client_defaults = { u"timeout": u"1h",
1267
u"checker": u"fping -q -- %%(host)s",
1270
client_config = configparser.SafeConfigParser(client_defaults)
1271
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"],
1274
2104
global mandos_dbus_service
1275
2105
mandos_dbus_service = None
1278
tcp_server = MandosServer((server_settings[u"address"],
1279
server_settings[u"port"]),
2107
tcp_server = MandosServer((server_settings["address"],
2108
server_settings["port"]),
1281
interface=server_settings[u"interface"],
2110
interface=(server_settings["interface"]
1282
2112
use_ipv6=use_ipv6,
1284
2113
gnutls_priority=
1285
server_settings[u"priority"],
2114
server_settings["priority"],
1286
2115
use_dbus=use_dbus)
1287
pidfilename = u"/var/run/mandos.pid"
1289
pidfile = open(pidfilename, u"w")
1291
logger.error(u"Could not open file %r", pidfilename)
2117
pidfilename = "/var/run/mandos.pid"
2119
pidfile = open(pidfilename, "w")
2121
logger.error("Could not open file %r", pidfilename)
1294
uid = pwd.getpwnam(u"_mandos").pw_uid
1295
gid = pwd.getpwnam(u"_mandos").pw_gid
2124
uid = pwd.getpwnam("_mandos").pw_uid
2125
gid = pwd.getpwnam("_mandos").pw_gid
1296
2126
except KeyError:
1298
uid = pwd.getpwnam(u"mandos").pw_uid
1299
gid = pwd.getpwnam(u"mandos").pw_gid
2128
uid = pwd.getpwnam("mandos").pw_uid
2129
gid = pwd.getpwnam("mandos").pw_gid
1300
2130
except KeyError:
1302
uid = pwd.getpwnam(u"nobody").pw_uid
1303
gid = pwd.getpwnam(u"nobody").pw_gid
2132
uid = pwd.getpwnam("nobody").pw_uid
2133
gid = pwd.getpwnam("nobody").pw_gid
1304
2134
except KeyError:
1310
except OSError, error:
2140
except OSError as error:
1311
2141
if error[0] != errno.EPERM:
1314
# Enable all possible GnuTLS debugging
2145
# Enable all possible GnuTLS debugging
1316
2147
# "Use a log level over 10 to enable all debugging options."
1317
2148
# - GnuTLS manual
1318
2149
gnutls.library.functions.gnutls_global_set_log_level(11)
1320
2151
@gnutls.library.types.gnutls_log_func
1321
2152
def debug_gnutls(level, string):
1322
logger.debug(u"GnuTLS: %s", string[:-1])
2153
logger.debug("GnuTLS: %s", string[:-1])
1324
2155
(gnutls.library.functions
1325
2156
.gnutls_global_set_log_function(debug_gnutls))
2158
# Redirect stdin so all checkers get /dev/null
2159
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2160
os.dup2(null, sys.stdin.fileno())
2164
# Need to fork before connecting to D-Bus
2166
# Close all input and output, do double fork, etc.
2169
gobject.threads_init()
1327
2171
global main_loop
1328
2172
# From the Avahi example code
1331
2175
bus = dbus.SystemBus()
1332
2176
# End of Avahi example code
1334
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
2179
bus_name = dbus.service.BusName("se.recompile.Mandos",
2180
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")
2187
server_settings["use_dbus"] = False
2188
tcp_server.use_dbus = False
1335
2189
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1336
service = AvahiService(name = server_settings[u"servicename"],
1337
servicetype = u"_mandos._tcp",
1338
protocol = protocol, bus = bus)
2190
service = AvahiServiceToSyslog(name =
2191
server_settings["servicename"],
2192
servicetype = "_mandos._tcp",
2193
protocol = protocol, bus = bus)
1339
2194
if server_settings["interface"]:
1340
2195
service.interface = (if_nametoindex
1341
(str(server_settings[u"interface"])))
2196
(str(server_settings["interface"])))
2198
global multiprocessing_manager
2199
multiprocessing_manager = multiprocessing.Manager()
1343
2201
client_class = Client
1345
client_class = functools.partial(ClientDBus, bus = bus)
1347
client_class(name = section,
1348
config= dict(client_config.items(section)))
1349
for section in client_config.sections()))
1351
logger.warning(u"No clients defined")
1354
# Redirect stdin so all checkers get /dev/null
1355
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1356
os.dup2(null, sys.stdin.fileno())
1360
# No console logging
1361
logger.removeHandler(console)
1362
# Close all input and output, do double fork, etc.
1366
with closing(pidfile):
1368
pidfile.write(str(pid) + "\n")
1371
logger.error(u"Could not write to file %r with PID %d",
1374
# "pidfile" was never created
1379
"Cleanup function; run on exit"
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))
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)
2298
if not tcp_server.clients:
2299
logger.warning("No clients defined")
1383
client = clients.pop()
1384
client.disable_hook = None
1387
atexit.register(cleanup)
2305
pidfile.write(str(pid) + "\n".encode("utf-8"))
2308
logger.error("Could not write to file %r with PID %d",
2311
# "pidfile" was never created
1390
2314
signal.signal(signal.SIGINT, signal.SIG_IGN)
1391
2316
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1392
2317
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1395
2320
class MandosDBusService(dbus.service.Object):
1396
2321
"""A D-Bus proxy object"""
1397
2322
def __init__(self):
1398
dbus.service.Object.__init__(self, bus, u"/")
1399
_interface = u"se.bsnet.fukt.Mandos"
1401
@dbus.service.signal(_interface, signature=u"oa{sv}")
1402
def ClientAdded(self, objpath, properties):
1406
@dbus.service.signal(_interface, signature=u"s")
1407
def ClientNotFound(self, fingerprint):
1411
@dbus.service.signal(_interface, signature=u"os")
2323
dbus.service.Object.__init__(self, bus, "/")
2324
_interface = "se.recompile.Mandos"
2326
@dbus.service.signal(_interface, signature="o")
2327
def ClientAdded(self, objpath):
2331
@dbus.service.signal(_interface, signature="ss")
2332
def ClientNotFound(self, fingerprint, address):
2336
@dbus.service.signal(_interface, signature="os")
1412
2337
def ClientRemoved(self, objpath, name):
1416
@dbus.service.method(_interface, out_signature=u"ao")
2341
@dbus.service.method(_interface, out_signature="ao")
1417
2342
def GetAllClients(self):
1419
return dbus.Array(c.dbus_object_path for c in clients)
2344
return dbus.Array(c.dbus_object_path
2346
tcp_server.clients.itervalues())
1421
2348
@dbus.service.method(_interface,
1422
out_signature=u"a{oa{sv}}")
2349
out_signature="a{oa{sv}}")
1423
2350
def GetAllClientsWithProperties(self):
1425
2352
return dbus.Dictionary(
1426
((c.dbus_object_path, c.GetAllProperties())
1428
signature=u"oa{sv}")
2353
((c.dbus_object_path, c.GetAll(""))
2354
for c in tcp_server.clients.itervalues()),
1430
@dbus.service.method(_interface, in_signature=u"o")
2357
@dbus.service.method(_interface, in_signature="o")
1431
2358
def RemoveClient(self, object_path):
2360
for c in tcp_server.clients.itervalues():
1434
2361
if c.dbus_object_path == object_path:
2362
del tcp_server.clients[c.name]
1436
2363
c.remove_from_connection()
1437
2364
# Don't signal anything except ClientRemoved
1438
c.disable(signal=False)
2365
c.disable(quiet=True)
1439
2366
# Emit D-Bus signal
1440
2367
self.ClientRemoved(object_path, c.name)
2369
raise KeyError(object_path)
1446
mandos_dbus_service = MandosDBusService()
1448
for client in clients:
2373
class MandosDBusServiceTransitional(MandosDBusService):
2374
__metaclass__ = AlternateDBusNamesMetaclass
2375
mandos_dbus_service = MandosDBusServiceTransitional()
2378
"Cleanup function; run on exit"
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
while tcp_server.clients:
2435
name, client = tcp_server.clients.popitem()
2437
client.remove_from_connection()
2438
# Don't signal anything except ClientRemoved
2439
client.disable(quiet=True)
2442
mandos_dbus_service.ClientRemoved(client
2445
client_settings.clear()
2447
atexit.register(cleanup)
2449
for client in tcp_server.clients.itervalues():
1450
2451
# Emit D-Bus signal
1451
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1452
client.GetAllProperties())
2452
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2453
# Need to initiate checking of clients
2455
client.init_checker()
1455
2457
tcp_server.enable()
1456
2458
tcp_server.server_activate()