290
298
if u"secret" in config:
291
299
self.secret = config[u"secret"].decode(u"base64")
292
300
elif u"secfile" in config:
293
with closing(open(os.path.expanduser
295
(config[u"secfile"])))) as secfile:
301
with open(os.path.expanduser(os.path.expandvars
302
(config[u"secfile"])),
296
304
self.secret = secfile.read()
306
#XXX Need to allow secret on demand!
298
307
raise TypeError(u"No secret or secfile for client %s"
300
309
self.host = config.get(u"host", u"")
312
321
self.checker_command = config[u"checker"]
313
322
self.current_checker_command = None
314
323
self.last_connect = None
324
self.approvals_pending = 0
325
self._approved = None
326
self.approved_by_default = config.get(u"approved_by_default",
328
self.approved_delay = string_to_delta(
329
config[u"approved_delay"])
330
self.approved_duration = string_to_delta(
331
config[u"approved_duration"])
332
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
334
def send_changedstate(self):
335
self.changedstate.acquire()
336
self.changedstate.notify_all()
337
self.changedstate.release()
316
339
def enable(self):
317
340
"""Start this client's checker and timeout hooks"""
318
341
if getattr(self, u"enabled", False):
319
342
# Already enabled
344
self.send_changedstate()
321
345
self.last_enabled = datetime.datetime.utcnow()
322
346
# Schedule a new checker to be started an 'interval' from now,
323
347
# and every interval from then on.
324
348
self.checker_initiator_tag = (gobject.timeout_add
325
349
(self.interval_milliseconds(),
326
350
self.start_checker))
327
# Also start a new checker *right now*.
329
351
# Schedule a disable() when 'timeout' has passed
330
352
self.disable_initiator_tag = (gobject.timeout_add
331
353
(self.timeout_milliseconds(),
333
355
self.enabled = True
356
# Also start a new checker *right now*.
359
def disable(self, quiet=True):
336
360
"""Disable this client."""
337
361
if not getattr(self, "enabled", False):
339
logger.info(u"Disabling client %s", self.name)
364
self.send_changedstate()
366
logger.info(u"Disabling client %s", self.name)
340
367
if getattr(self, u"disable_initiator_tag", False):
341
368
gobject.source_remove(self.disable_initiator_tag)
342
369
self.disable_initiator_tag = None
394
421
# client would inevitably timeout, since no checker would get
395
422
# a chance to run to completion. If we instead leave running
396
423
# checkers alone, the checker would have to take more time
397
# than 'timeout' for the client to be declared invalid, which
398
# is as it should be.
424
# than 'timeout' for the client to be disabled, which is as it
400
427
# If a checker exists, make sure it is not a zombie
401
if self.checker is not None:
402
429
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
430
except (AttributeError, OSError), error:
431
if (isinstance(error, OSError)
432
and error.errno != errno.ECHILD):
404
436
logger.warning(u"Checker was a zombie")
405
437
gobject.source_remove(self.checker_callback_tag)
461
493
logger.debug(u"Stopping checker for %(name)s", vars(self))
463
495
os.kill(self.checker.pid, signal.SIGTERM)
465
497
#if self.checker.poll() is None:
466
498
# os.kill(self.checker.pid, signal.SIGKILL)
467
499
except OSError, error:
468
500
if error.errno != errno.ESRCH: # No such process
470
502
self.checker = None
472
def still_valid(self):
473
"""Has the timeout not yet passed for this client?"""
474
if not getattr(self, u"enabled", False):
476
now = datetime.datetime.utcnow()
477
if self.last_checked_ok is None:
478
return now < (self.created + self.timeout)
480
return now < (self.last_checked_ok + self.timeout)
483
504
def dbus_service_property(dbus_interface, signature=u"v",
484
505
access=u"readwrite", byte_arrays=False):
492
513
dbus.service.method, except there is only "signature", since the
493
514
type from Get() and the type sent to Set() is the same.
516
# Encoding deeply encoded byte arrays is not supported yet by the
517
# "Set" method, so we fail early here:
518
if byte_arrays and signature != u"ay":
519
raise ValueError(u"Byte arrays not supported for non-'ay'"
520
u" signature %r" % signature)
495
521
def decorator(func):
496
522
func._dbus_is_property = True
497
523
func._dbus_interface = dbus_interface
620
650
"""Standard D-Bus method, overloaded to insert property tags.
622
652
xmlstring = dbus.service.Object.Introspect(self, object_path,
624
document = xml.dom.minidom.parseString(xmlstring)
626
def make_tag(document, name, prop):
627
e = document.createElement(u"property")
628
e.setAttribute(u"name", name)
629
e.setAttribute(u"type", prop._dbus_signature)
630
e.setAttribute(u"access", prop._dbus_access)
632
for if_tag in document.getElementsByTagName(u"interface"):
633
for tag in (make_tag(document, name, prop)
635
in self._get_all_dbus_properties()
636
if prop._dbus_interface
637
== if_tag.getAttribute(u"name")):
638
if_tag.appendChild(tag)
639
xmlstring = document.toxml(u"utf-8")
655
document = xml.dom.minidom.parseString(xmlstring)
656
def make_tag(document, name, prop):
657
e = document.createElement(u"property")
658
e.setAttribute(u"name", name)
659
e.setAttribute(u"type", prop._dbus_signature)
660
e.setAttribute(u"access", prop._dbus_access)
662
for if_tag in document.getElementsByTagName(u"interface"):
663
for tag in (make_tag(document, name, prop)
665
in self._get_all_dbus_properties()
666
if prop._dbus_interface
667
== if_tag.getAttribute(u"name")):
668
if_tag.appendChild(tag)
669
# Add the names to the return values for the
670
# "org.freedesktop.DBus.Properties" methods
671
if (if_tag.getAttribute(u"name")
672
== u"org.freedesktop.DBus.Properties"):
673
for cn in if_tag.getElementsByTagName(u"method"):
674
if cn.getAttribute(u"name") == u"Get":
675
for arg in cn.getElementsByTagName(u"arg"):
676
if (arg.getAttribute(u"direction")
678
arg.setAttribute(u"name", u"value")
679
elif cn.getAttribute(u"name") == u"GetAll":
680
for arg in cn.getElementsByTagName(u"arg"):
681
if (arg.getAttribute(u"direction")
683
arg.setAttribute(u"name", u"props")
684
xmlstring = document.toxml(u"utf-8")
686
except (AttributeError, xml.dom.DOMException,
687
xml.parsers.expat.ExpatError), error:
688
logger.error(u"Failed to override Introspection method",
680
729
variant_level=1))
683
def disable(self, signal = True):
732
def disable(self, quiet = False):
684
733
oldstate = getattr(self, u"enabled", False)
685
r = Client.disable(self)
686
if signal and oldstate != self.enabled:
734
r = Client.disable(self, quiet=quiet)
735
if not quiet and oldstate != self.enabled:
687
736
# Emit D-Bus signal
688
737
self.PropertyChanged(dbus.String(u"enabled"),
689
738
dbus.Boolean(False, variant_level=1))
754
803
self.PropertyChanged(dbus.String(u"checker_running"),
755
804
dbus.Boolean(False, variant_level=1))
758
## D-Bus methods & signals
807
def _reset_approved(self):
808
self._approved = None
811
def approve(self, value=True):
812
self._approved = value
813
gobject.timeout_add(self._timedelta_to_milliseconds(self.approved_duration, self._reset_approved))
815
def approved_pending(self):
816
return self.approvals_pending > 0
819
## D-Bus methods, signals & properties
759
820
_interface = u"se.bsnet.fukt.Mandos.Client"
762
@dbus.service.method(_interface)
764
return self.checked_ok()
766
824
# CheckerCompleted - signal
767
825
@dbus.service.signal(_interface, signature=u"nxs")
784
# ReceivedSecret - signal
785
843
@dbus.service.signal(_interface)
786
def ReceivedSecret(self):
790
848
# Rejected - signal
791
@dbus.service.signal(_interface)
849
@dbus.service.signal(_interface, signature=u"s")
850
def Rejected(self, reason):
854
# NeedApproval - signal
855
@dbus.service.signal(_interface, signature=u"db")
856
def NeedApproval(self, timeout, default):
863
@dbus.service.method(_interface, in_signature=u"b")
864
def Approve(self, value):
868
@dbus.service.method(_interface)
870
return self.checked_ok()
796
872
# Enable - method
797
873
@dbus.service.method(_interface)
816
892
def StopChecker(self):
817
893
self.stop_checker()
897
# approved_pending - property
898
@dbus_service_property(_interface, signature=u"b", access=u"read")
899
def approved_pending_dbus_property(self):
900
return dbus.Boolean(self.approved_pending())
902
# approved_by_default - property
903
@dbus_service_property(_interface, signature=u"b",
905
def approved_by_default_dbus_property(self):
906
return dbus.Boolean(self.approved_by_default)
908
# approved_delay - property
909
@dbus_service_property(_interface, signature=u"t",
911
def approved_delay_dbus_property(self):
912
return dbus.UInt64(self.approved_delay_milliseconds())
914
# approved_duration - property
915
@dbus_service_property(_interface, signature=u"t",
917
def approved_duration_dbus_property(self):
918
return dbus.UInt64(self._timedelta_to_milliseconds(
919
self.approved_duration))
819
921
# name - property
820
922
@dbus_service_property(_interface, signature=u"s", access=u"read")
821
923
def name_dbus_property(self):
1060
class ProxyClient(object):
1061
def __init__(self, child_pipe, fpr, address):
1062
self._pipe = child_pipe
1063
self._pipe.send(('init', fpr, address))
1064
if not self._pipe.recv():
1067
def __getattribute__(self, name):
1068
if(name == '_pipe'):
1069
return super(ProxyClient, self).__getattribute__(name)
1070
self._pipe.send(('getattr', name))
1071
data = self._pipe.recv()
1072
if data[0] == 'data':
1074
if data[0] == 'function':
1075
def func(*args, **kwargs):
1076
self._pipe.send(('funcall', name, args, kwargs))
1077
return self._pipe.recv()[1]
1080
def __setattr__(self, name, value):
1081
if(name == '_pipe'):
1082
return super(ProxyClient, self).__setattr__(name, value)
1083
self._pipe.send(('setattr', name, value))
958
1086
class ClientHandler(socketserver.BaseRequestHandler, object):
959
1087
"""A class to handle client connections.
962
1090
Note: This will run in its own forked process."""
964
1092
def handle(self):
965
logger.info(u"TCP connection from: %s",
966
unicode(self.client_address))
967
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
968
# Open IPC pipe to parent process
969
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1093
with contextlib.closing(self.server.child_pipe) as child_pipe:
1094
logger.info(u"TCP connection from: %s",
1095
unicode(self.client_address))
1096
logger.debug(u"Pipe FD: %d",
1097
self.server.child_pipe.fileno())
970
1099
session = (gnutls.connection
971
1100
.ClientSession(self.request,
972
1101
gnutls.connection
973
1102
.X509Credentials()))
975
line = self.request.makefile().readline()
976
logger.debug(u"Protocol version: %r", line)
978
if int(line.strip().split()[0]) > 1:
980
except (ValueError, IndexError, RuntimeError), error:
981
logger.error(u"Unknown protocol version: %s", error)
984
1104
# Note: gnutls.connection.X509Credentials is really a
985
1105
# generic GnuTLS certificate credentials object so long as
986
1106
# no X.509 keys are added to it. Therefore, we can use it
987
1107
# here despite using OpenPGP certificates.
989
1109
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
990
1110
# u"+AES-256-CBC", u"+SHA1",
991
1111
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
997
1117
(gnutls.library.functions
998
1118
.gnutls_priority_set_direct(session._c_object,
999
1119
priority, None))
1121
# Start communication using the Mandos protocol
1122
# Get protocol number
1123
line = self.request.makefile().readline()
1124
logger.debug(u"Protocol version: %r", line)
1126
if int(line.strip().split()[0]) > 1:
1128
except (ValueError, IndexError, RuntimeError), error:
1129
logger.error(u"Unknown protocol version: %s", error)
1132
# Start GnuTLS connection
1002
1134
session.handshake()
1003
1135
except gnutls.errors.GNUTLSError, error:
1006
1138
# established. Just abandon the request.
1008
1140
logger.debug(u"Handshake succeeded")
1142
approval_required = False
1010
fpr = self.fingerprint(self.peer_certificate(session))
1011
except (TypeError, gnutls.errors.GNUTLSError), error:
1012
logger.warning(u"Bad certificate: %s", error)
1015
logger.debug(u"Fingerprint: %s", fpr)
1145
fpr = self.fingerprint(self.peer_certificate
1147
except (TypeError, gnutls.errors.GNUTLSError), error:
1148
logger.warning(u"Bad certificate: %s", error)
1150
logger.debug(u"Fingerprint: %s", fpr)
1153
client = ProxyClient(child_pipe, fpr,
1154
self.client_address)
1158
if client.approved_delay:
1159
delay = client.approved_delay
1160
client.approvals_pending += 1
1161
approval_required = True
1164
if not client.enabled:
1165
logger.warning(u"Client %s is disabled",
1167
if self.server.use_dbus:
1169
client.Rejected("Disabled")
1172
if client._approved or not client.approved_delay:
1173
#We are approved or approval is disabled
1175
elif client._approved is None:
1176
logger.info(u"Client %s need approval",
1178
if self.server.use_dbus:
1180
client.NeedApproval(
1181
client.approved_delay_milliseconds(),
1182
client.approved_by_default)
1184
logger.warning(u"Client %s was not approved",
1186
if self.server.use_dbus:
1188
client.Rejected("Disapproved")
1191
#wait until timeout or approved
1192
#x = float(client._timedelta_to_milliseconds(delay))
1193
time = datetime.datetime.now()
1194
client.changedstate.acquire()
1195
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1196
client.changedstate.release()
1197
time2 = datetime.datetime.now()
1198
if (time2 - time) >= delay:
1199
if not client.approved_by_default:
1200
logger.warning("Client %s timed out while"
1201
" waiting for approval",
1203
if self.server.use_dbus:
1205
client.Rejected("Time out")
1210
delay -= time2 - time
1213
while sent_size < len(client.secret):
1214
# XXX handle session exception
1215
sent = session.send(client.secret[sent_size:])
1216
logger.debug(u"Sent: %d, remaining: %d",
1217
sent, len(client.secret)
1218
- (sent_size + sent))
1221
logger.info(u"Sending secret to %s", client.name)
1222
# bump the timeout as if seen
1224
if self.server.use_dbus:
1017
for c in self.server.clients:
1018
if c.fingerprint == fpr:
1022
ipc.write(u"NOTFOUND %s %s\n"
1023
% (fpr, unicode(self.client_address)))
1026
# Have to check if client.still_valid(), since it is
1027
# possible that the client timed out while establishing
1028
# the GnuTLS session.
1029
if not client.still_valid():
1030
ipc.write(u"INVALID %s\n" % client.name)
1033
ipc.write(u"SENDING %s\n" % client.name)
1035
while sent_size < len(client.secret):
1036
sent = session.send(client.secret[sent_size:])
1037
logger.debug(u"Sent: %d, remaining: %d",
1038
sent, len(client.secret)
1039
- (sent_size + sent))
1229
if approval_required:
1230
client.approvals_pending -= 1
1044
1234
def peer_certificate(session):
1107
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1108
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1297
class MultiprocessingMixIn(object):
1298
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1299
def sub_process_main(self, request, address):
1301
self.finish_request(request, address)
1303
self.handle_error(request, address)
1304
self.close_request(request)
1306
def process_request(self, request, address):
1307
"""Start a new process to process the request."""
1308
multiprocessing.Process(target = self.sub_process_main,
1309
args = (request, address)).start()
1311
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1312
""" adds a pipe to the MixIn """
1109
1313
def process_request(self, request, client_address):
1110
1314
"""Overrides and wraps the original process_request().
1112
1316
This function creates a new pipe in self.pipe
1114
self.pipe = os.pipe()
1115
super(ForkingMixInWithPipe,
1318
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1320
super(MultiprocessingMixInWithPipe,
1116
1321
self).process_request(request, client_address)
1117
os.close(self.pipe[1]) # close write end
1118
self.add_pipe(self.pipe[0])
1119
def add_pipe(self, pipe):
1322
self.child_pipe.close()
1323
self.add_pipe(parent_pipe)
1325
def add_pipe(self, parent_pipe):
1120
1326
"""Dummy function; override as necessary"""
1124
class IPv6_TCPServer(ForkingMixInWithPipe,
1329
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1125
1330
socketserver.TCPServer, object):
1126
1331
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1212
1417
return socketserver.TCPServer.server_activate(self)
1213
1418
def enable(self):
1214
1419
self.enabled = True
1215
def add_pipe(self, pipe):
1420
def add_pipe(self, parent_pipe):
1216
1421
# Call "handle_ipc" for both data and EOF events
1217
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1219
def handle_ipc(self, source, condition, file_objects={}):
1422
gobject.io_add_watch(parent_pipe.fileno(),
1423
gobject.IO_IN | gobject.IO_HUP,
1424
functools.partial(self.handle_ipc,
1425
parent_pipe = parent_pipe))
1427
def handle_ipc(self, source, condition, parent_pipe=None,
1428
client_object=None):
1220
1429
condition_names = {
1221
1430
gobject.IO_IN: u"IN", # There is data to read.
1222
1431
gobject.IO_OUT: u"OUT", # Data can be written (without
1233
1442
if cond & condition)
1234
1443
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1235
1444
conditions_string)
1237
# Turn the pipe file descriptor into a Python file object
1238
if source not in file_objects:
1239
file_objects[source] = os.fdopen(source, u"r", 1)
1241
# Read a line from the file object
1242
cmdline = file_objects[source].readline()
1243
if not cmdline: # Empty line means end of file
1244
# close the IPC pipe
1245
file_objects[source].close()
1246
del file_objects[source]
1248
# Stop calling this function
1251
logger.debug(u"IPC command: %r", cmdline)
1253
# Parse and act on command
1254
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1256
if cmd == u"NOTFOUND":
1257
logger.warning(u"Client not found for fingerprint: %s",
1261
mandos_dbus_service.ClientNotFound(args)
1262
elif cmd == u"INVALID":
1263
for client in self.clients:
1264
if client.name == args:
1265
logger.warning(u"Client %s is invalid", args)
1271
logger.error(u"Unknown client %s is invalid", args)
1272
elif cmd == u"SENDING":
1273
for client in self.clients:
1274
if client.name == args:
1275
logger.info(u"Sending secret to %s", client.name)
1279
client.ReceivedSecret()
1282
logger.error(u"Sending secret to unknown client %s",
1285
logger.error(u"Unknown IPC command: %r", cmdline)
1287
# Keep calling this function
1446
# error or the other end of multiprocessing.Pipe has closed
1447
if condition & gobject.IO_HUP or condition & gobject.IO_ERR:
1450
# Read a request from the child
1451
request = parent_pipe.recv()
1452
logger.debug(u"IPC request: %s", repr(request))
1453
command = request[0]
1455
if command == 'init':
1457
address = request[2]
1459
for c in self.clients:
1460
if c.fingerprint == fpr:
1464
logger.warning(u"Client not found for fingerprint: %s, ad"
1465
u"dress: %s", fpr, address)
1468
mandos_dbus_service.ClientNotFound(fpr, address)
1469
parent_pipe.send(False)
1472
gobject.io_add_watch(parent_pipe.fileno(),
1473
gobject.IO_IN | gobject.IO_HUP,
1474
functools.partial(self.handle_ipc,
1475
parent_pipe = parent_pipe,
1476
client_object = client))
1477
parent_pipe.send(True)
1478
# remove the old hook in favor of the new above hook on same fileno
1480
if command == 'funcall':
1481
funcname = request[1]
1485
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1487
if command == 'getattr':
1488
attrname = request[1]
1489
if callable(client_object.__getattribute__(attrname)):
1490
parent_pipe.send(('function',))
1492
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1494
if command == 'setattr':
1495
attrname = request[1]
1497
setattr(client_object, attrname, value)
1367
1578
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1368
1579
if not stat.S_ISCHR(os.fstat(null).st_mode):
1369
1580
raise OSError(errno.ENODEV,
1370
u"/dev/null not a character device")
1581
u"%s not a character device"
1371
1583
os.dup2(null, sys.stdin.fileno())
1372
1584
os.dup2(null, sys.stdout.fileno())
1373
1585
os.dup2(null, sys.stderr.fileno())
1540
1754
bus = dbus.SystemBus()
1541
1755
# End of Avahi example code
1543
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1758
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1759
bus, do_not_queue=True)
1760
except dbus.exceptions.NameExistsException, e:
1761
logger.error(unicode(e) + u", disabling D-Bus")
1763
server_settings[u"use_dbus"] = False
1764
tcp_server.use_dbus = False
1544
1765
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1545
1766
service = AvahiService(name = server_settings[u"servicename"],
1546
1767
servicetype = u"_mandos._tcp",
1548
1769
if server_settings["interface"]:
1549
1770
service.interface = (if_nametoindex
1550
1771
(str(server_settings[u"interface"])))
1773
global multiprocessing_manager
1774
multiprocessing_manager = multiprocessing.Manager()
1552
1776
client_class = Client
1554
1778
client_class = functools.partial(ClientDBus, bus = bus)
1779
def client_config_items(config, section):
1780
special_settings = {
1781
"approved_by_default":
1782
lambda: config.getboolean(section,
1783
"approved_by_default"),
1785
for name, value in config.items(section):
1787
yield (name, special_settings[name]())
1555
1791
tcp_server.clients.update(set(
1556
1792
client_class(name = section,
1557
config= dict(client_config.items(section)))
1793
config= dict(client_config_items(
1794
client_config, section)))
1558
1795
for section in client_config.sections()))
1559
1796
if not tcp_server.clients:
1560
1797
logger.warning(u"No clients defined")
1607
1833
dbus.service.Object.__init__(self, bus, u"/")
1608
1834
_interface = u"se.bsnet.fukt.Mandos"
1610
@dbus.service.signal(_interface, signature=u"oa{sv}")
1611
def ClientAdded(self, objpath, properties):
1836
@dbus.service.signal(_interface, signature=u"o")
1837
def ClientAdded(self, objpath):
1615
@dbus.service.signal(_interface, signature=u"s")
1616
def ClientNotFound(self, fingerprint):
1841
@dbus.service.signal(_interface, signature=u"ss")
1842
def ClientNotFound(self, fingerprint, address):
1645
1871
tcp_server.clients.remove(c)
1646
1872
c.remove_from_connection()
1647
1873
# Don't signal anything except ClientRemoved
1648
c.disable(signal=False)
1874
c.disable(quiet=True)
1649
1875
# Emit D-Bus signal
1650
1876
self.ClientRemoved(object_path, c.name)
1878
raise KeyError(object_path)
1656
1882
mandos_dbus_service = MandosDBusService()
1885
"Cleanup function; run on exit"
1888
while tcp_server.clients:
1889
client = tcp_server.clients.pop()
1891
client.remove_from_connection()
1892
client.disable_hook = None
1893
# Don't signal anything except ClientRemoved
1894
client.disable(quiet=True)
1897
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1900
atexit.register(cleanup)
1658
1902
for client in tcp_server.clients:
1660
1904
# Emit D-Bus signal
1661
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1905
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1663
1906
client.enable()
1665
1908
tcp_server.enable()