/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos

merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
63
63
import cPickle as pickle
64
64
import multiprocessing
65
65
import types
 
66
import hashlib
66
67
 
67
68
import dbus
68
69
import dbus.service
73
74
import ctypes.util
74
75
import xml.dom.minidom
75
76
import inspect
 
77
import Crypto.Cipher.AES
76
78
 
77
79
try:
78
80
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
83
85
        SO_BINDTODEVICE = None
84
86
 
85
87
 
86
 
version = "1.4.0"
87
 
 
88
 
#logger = logging.getLogger('mandos')
89
 
logger = logging.Logger('mandos')
 
88
version = "1.4.1"
 
89
 
 
90
logger = logging.getLogger()
 
91
stored_state_path = "/var/lib/mandos/clients.pickle"
 
92
 
90
93
syslogger = (logging.handlers.SysLogHandler
91
94
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
92
95
              address = str("/dev/log")))
96
99
logger.addHandler(syslogger)
97
100
 
98
101
console = logging.StreamHandler()
99
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
 
102
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
103
                                       ' [%(process)d]:'
100
104
                                       ' %(levelname)s:'
101
105
                                       ' %(message)s'))
102
106
logger.addHandler(console)
103
107
 
 
108
 
104
109
class AvahiError(Exception):
105
110
    def __init__(self, value, *args, **kwargs):
106
111
        self.value = value
164
169
                            .GetAlternativeServiceName(self.name))
165
170
        logger.info("Changing Zeroconf service name to %r ...",
166
171
                    self.name)
167
 
        syslogger.setFormatter(logging.Formatter
168
 
                               ('Mandos (%s) [%%(process)d]:'
169
 
                                ' %%(levelname)s: %%(message)s'
170
 
                                % self.name))
171
172
        self.remove()
172
173
        try:
173
174
            self.add()
193
194
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
194
195
        self.entry_group_state_changed_match = (
195
196
            self.group.connect_to_signal(
196
 
                'StateChanged', self .entry_group_state_changed))
 
197
                'StateChanged', self.entry_group_state_changed))
197
198
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
198
199
                     self.name, self.type)
199
200
        self.group.AddService(
265
266
                                 self.server_state_changed)
266
267
        self.server_state_changed(self.server.GetState())
267
268
 
 
269
class AvahiServiceToSyslog(AvahiService):
 
270
    def rename(self):
 
271
        """Add the new name to the syslog messages"""
 
272
        ret = AvahiService.rename(self)
 
273
        syslogger.setFormatter(logging.Formatter
 
274
                               ('Mandos (%s) [%%(process)d]:'
 
275
                                ' %%(levelname)s: %%(message)s'
 
276
                                % self.name))
 
277
        return ret
268
278
 
269
279
def _timedelta_to_milliseconds(td):
270
280
    "Convert a datetime.timedelta() to milliseconds"
289
299
                     instance %(name)s can be used in the command.
290
300
    checker_initiator_tag: a gobject event source tag, or None
291
301
    created:    datetime.datetime(); (UTC) object creation
 
302
    client_structure: Object describing what attributes a client has
 
303
                      and is used for storing the client at exit
292
304
    current_checker_command: string; current running checker_command
293
 
    disable_hook:  If set, called by disable() as disable_hook(self)
294
305
    disable_initiator_tag: a gobject event source tag, or None
295
306
    enabled:    bool()
296
307
    fingerprint: string (40 or 32 hexadecimal digits); used to
299
310
    interval:   datetime.timedelta(); How often to start a new checker
300
311
    last_approval_request: datetime.datetime(); (UTC) or None
301
312
    last_checked_ok: datetime.datetime(); (UTC) or None
 
313
    last_checker_status: integer between 0 and 255 reflecting exit status
 
314
                         of last checker. -1 reflect crashed checker,
 
315
                         or None.
302
316
    last_enabled: datetime.datetime(); (UTC)
303
317
    name:       string; from the config file, used in log messages and
304
318
                        D-Bus identifiers
331
345
    def approval_delay_milliseconds(self):
332
346
        return _timedelta_to_milliseconds(self.approval_delay)
333
347
    
334
 
    def __init__(self, name = None, disable_hook=None, config=None):
 
348
    def __init__(self, name = None, config=None):
335
349
        """Note: the 'checker' key in 'config' sets the
336
350
        'checker_command' attribute and *not* the 'checker'
337
351
        attribute."""
357
371
                            % self.name)
358
372
        self.host = config.get("host", "")
359
373
        self.created = datetime.datetime.utcnow()
360
 
        self.enabled = False
 
374
        self.enabled = True
361
375
        self.last_approval_request = None
362
 
        self.last_enabled = None
 
376
        self.last_enabled = datetime.datetime.utcnow()
363
377
        self.last_checked_ok = None
 
378
        self.last_checker_status = None
364
379
        self.timeout = string_to_delta(config["timeout"])
365
380
        self.extended_timeout = string_to_delta(config
366
381
                                                ["extended_timeout"])
367
382
        self.interval = string_to_delta(config["interval"])
368
 
        self.disable_hook = disable_hook
369
383
        self.checker = None
370
384
        self.checker_initiator_tag = None
371
385
        self.disable_initiator_tag = None
372
 
        self.expires = None
 
386
        self.expires = datetime.datetime.utcnow() + self.timeout
373
387
        self.checker_callback_tag = None
374
388
        self.checker_command = config["checker"]
375
389
        self.current_checker_command = None
376
 
        self.last_connect = None
377
390
        self._approved = None
378
391
        self.approved_by_default = config.get("approved_by_default",
379
392
                                              True)
385
398
        self.changedstate = (multiprocessing_manager
386
399
                             .Condition(multiprocessing_manager
387
400
                                        .Lock()))
 
401
        self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
 
402
        self.client_structure.append("client_structure")
 
403
 
 
404
 
 
405
        for name, t in inspect.getmembers(type(self),
 
406
                                          lambda obj: isinstance(obj, property)):
 
407
            if not name.startswith("_"):
 
408
                self.client_structure.append(name)
388
409
    
 
410
    # Send notice to process children that client state has changed
389
411
    def send_changedstate(self):
390
 
        self.changedstate.acquire()
391
 
        self.changedstate.notify_all()
392
 
        self.changedstate.release()
 
412
        with self.changedstate:
 
413
            self.changedstate.notify_all()
393
414
    
394
415
    def enable(self):
395
416
        """Start this client's checker and timeout hooks"""
397
418
            # Already enabled
398
419
            return
399
420
        self.send_changedstate()
400
 
        # Schedule a new checker to be started an 'interval' from now,
401
 
        # and every interval from then on.
402
 
        self.checker_initiator_tag = (gobject.timeout_add
403
 
                                      (self.interval_milliseconds(),
404
 
                                       self.start_checker))
405
 
        # Schedule a disable() when 'timeout' has passed
406
421
        self.expires = datetime.datetime.utcnow() + self.timeout
407
 
        self.disable_initiator_tag = (gobject.timeout_add
408
 
                                   (self.timeout_milliseconds(),
409
 
                                    self.disable))
410
422
        self.enabled = True
411
423
        self.last_enabled = datetime.datetime.utcnow()
412
 
        # Also start a new checker *right now*.
413
 
        self.start_checker()
 
424
        self.init_checker()
414
425
    
415
426
    def disable(self, quiet=True):
416
427
        """Disable this client."""
428
439
            gobject.source_remove(self.checker_initiator_tag)
429
440
            self.checker_initiator_tag = None
430
441
        self.stop_checker()
431
 
        if self.disable_hook:
432
 
            self.disable_hook(self)
433
442
        self.enabled = False
434
443
        # Do not run this again if called by a gobject.timeout_add
435
444
        return False
436
445
    
437
446
    def __del__(self):
438
 
        self.disable_hook = None
439
447
        self.disable()
440
 
    
 
448
 
 
449
    def init_checker(self):
 
450
        # Schedule a new checker to be started an 'interval' from now,
 
451
        # and every interval from then on.
 
452
        self.checker_initiator_tag = (gobject.timeout_add
 
453
                                      (self.interval_milliseconds(),
 
454
                                       self.start_checker))
 
455
        # Schedule a disable() when 'timeout' has passed
 
456
        self.disable_initiator_tag = (gobject.timeout_add
 
457
                                   (self.timeout_milliseconds(),
 
458
                                    self.disable))
 
459
        # Also start a new checker *right now*.
 
460
        self.start_checker()
 
461
 
 
462
        
441
463
    def checker_callback(self, pid, condition, command):
442
464
        """The checker has completed, so take appropriate actions."""
443
465
        self.checker_callback_tag = None
444
466
        self.checker = None
445
467
        if os.WIFEXITED(condition):
446
 
            exitstatus = os.WEXITSTATUS(condition)
447
 
            if exitstatus == 0:
 
468
            self.last_checker_status =  os.WEXITSTATUS(condition)
 
469
            if self.last_checker_status == 0:
448
470
                logger.info("Checker for %(name)s succeeded",
449
471
                            vars(self))
450
472
                self.checked_ok()
452
474
                logger.info("Checker for %(name)s failed",
453
475
                            vars(self))
454
476
        else:
 
477
            self.last_checker_status = -1
455
478
            logger.warning("Checker for %(name)s crashed?",
456
479
                           vars(self))
457
480
    
568
591
                raise
569
592
        self.checker = None
570
593
 
 
594
    # Encrypts a client secret and stores it in a varible encrypted_secret
 
595
    def encrypt_secret(self, key):
 
596
        # Encryption-key need to be of a specific size, so we hash inputed key
 
597
        hasheng = hashlib.sha256()
 
598
        hasheng.update(key)
 
599
        encryptionkey = hasheng.digest()
 
600
 
 
601
        # Create validation hash so we know at decryption if it was sucessful
 
602
        hasheng = hashlib.sha256()
 
603
        hasheng.update(self.secret)
 
604
        validationhash = hasheng.digest()
 
605
 
 
606
        # Encrypt secret
 
607
        iv = os.urandom(Crypto.Cipher.AES.block_size)
 
608
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
609
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
610
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
 
611
        self.encrypted_secret = (ciphertext, iv)
 
612
 
 
613
    # Decrypt a encrypted client secret
 
614
    def decrypt_secret(self, key):
 
615
        # Decryption-key need to be of a specific size, so we hash inputed key
 
616
        hasheng = hashlib.sha256()
 
617
        hasheng.update(key)
 
618
        encryptionkey = hasheng.digest()
 
619
 
 
620
        # Decrypt encrypted secret
 
621
        ciphertext, iv = self.encrypted_secret
 
622
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
623
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
624
        plain = ciphereng.decrypt(ciphertext)
 
625
 
 
626
        # Validate decrypted secret to know if it was succesful
 
627
        hasheng = hashlib.sha256()
 
628
        validationhash = plain[:hasheng.digest_size]
 
629
        secret = plain[hasheng.digest_size:]
 
630
        hasheng.update(secret)
 
631
 
 
632
        # if validation fails, we use key as new secret. Otherwhise, we use
 
633
        # the decrypted secret
 
634
        if hasheng.digest() == validationhash:
 
635
            self.secret = secret
 
636
        else:
 
637
            self.secret = key
 
638
        del self.encrypted_secret
 
639
 
571
640
 
572
641
def dbus_service_property(dbus_interface, signature="v",
573
642
                          access="readwrite", byte_arrays=False):
874
943
    # dbus.service.Object doesn't use super(), so we can't either.
875
944
    
876
945
    def __init__(self, bus = None, *args, **kwargs):
 
946
        self.bus = bus
 
947
        Client.__init__(self, *args, **kwargs)
 
948
 
877
949
        self._approvals_pending = 0
878
 
        self.bus = bus
879
 
        Client.__init__(self, *args, **kwargs)
880
950
        # Only now, when this client is initialized, can it show up on
881
951
        # the D-Bus
882
952
        client_object_name = unicode(self.name).translate(
1624
1694
        self.enabled = False
1625
1695
        self.clients = clients
1626
1696
        if self.clients is None:
1627
 
            self.clients = set()
 
1697
            self.clients = {}
1628
1698
        self.use_dbus = use_dbus
1629
1699
        self.gnutls_priority = gnutls_priority
1630
1700
        IPv6_TCPServer.__init__(self, server_address,
1677
1747
            fpr = request[1]
1678
1748
            address = request[2]
1679
1749
            
1680
 
            for c in self.clients:
 
1750
            for c in self.clients.itervalues():
1681
1751
                if c.fingerprint == fpr:
1682
1752
                    client = c
1683
1753
                    break
1851
1921
                        " system bus interface")
1852
1922
    parser.add_argument("--no-ipv6", action="store_false",
1853
1923
                        dest="use_ipv6", help="Do not use IPv6")
 
1924
    parser.add_argument("--no-restore", action="store_false",
 
1925
                        dest="restore", help="Do not restore stored state",
 
1926
                        default=True)
 
1927
 
1854
1928
    options = parser.parse_args()
1855
1929
    
1856
1930
    if options.check:
1891
1965
    # options, if set.
1892
1966
    for option in ("interface", "address", "port", "debug",
1893
1967
                   "priority", "servicename", "configdir",
1894
 
                   "use_dbus", "use_ipv6", "debuglevel"):
 
1968
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
1895
1969
        value = getattr(options, option)
1896
1970
        if value is not None:
1897
1971
            server_settings[option] = value
1970
2044
            raise error
1971
2045
    
1972
2046
    if not debug and not debuglevel:
1973
 
        syslogger.setLevel(logging.WARNING)
1974
 
        console.setLevel(logging.WARNING)
 
2047
        logger.setLevel(logging.WARNING)
1975
2048
    if debuglevel:
1976
2049
        level = getattr(logging, debuglevel.upper())
1977
 
        syslogger.setLevel(level)
1978
 
        console.setLevel(level)
 
2050
        logger.setLevel(level)
1979
2051
    
1980
2052
    if debug:
 
2053
        logger.setLevel(logging.DEBUG)
1981
2054
        # Enable all possible GnuTLS debugging
1982
2055
        
1983
2056
        # "Use a log level over 10 to enable all debugging options."
2024
2097
            server_settings["use_dbus"] = False
2025
2098
            tcp_server.use_dbus = False
2026
2099
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2027
 
    service = AvahiService(name = server_settings["servicename"],
2028
 
                           servicetype = "_mandos._tcp",
2029
 
                           protocol = protocol, bus = bus)
 
2100
    service = AvahiServiceToSyslog(name =
 
2101
                                   server_settings["servicename"],
 
2102
                                   servicetype = "_mandos._tcp",
 
2103
                                   protocol = protocol, bus = bus)
2030
2104
    if server_settings["interface"]:
2031
2105
        service.interface = (if_nametoindex
2032
2106
                             (str(server_settings["interface"])))
2038
2112
    if use_dbus:
2039
2113
        client_class = functools.partial(ClientDBusTransitional,
2040
2114
                                         bus = bus)
2041
 
    def client_config_items(config, section):
2042
 
        special_settings = {
2043
 
            "approved_by_default":
2044
 
                lambda: config.getboolean(section,
2045
 
                                          "approved_by_default"),
2046
 
            }
2047
 
        for name, value in config.items(section):
 
2115
    
 
2116
    special_settings = {
 
2117
        # Some settings need to be accessd by special methods;
 
2118
        # booleans need .getboolean(), etc.  Here is a list of them:
 
2119
        "approved_by_default":
 
2120
            lambda section:
 
2121
            client_config.getboolean(section, "approved_by_default"),
 
2122
        }
 
2123
    # Construct a new dict of client settings of this form:
 
2124
    # { client_name: {setting_name: value, ...}, ...}
 
2125
    # with exceptions for any special settings as defined above
 
2126
    client_settings = dict((clientname,
 
2127
                           dict((setting,
 
2128
                                 (value if setting not in special_settings
 
2129
                                  else special_settings[setting](clientname)))
 
2130
                                for setting, value in client_config.items(clientname)))
 
2131
                          for clientname in client_config.sections())
 
2132
    
 
2133
    old_client_settings = {}
 
2134
    clients_data = []
 
2135
 
 
2136
    # Get client data and settings from last running state. 
 
2137
    if server_settings["restore"]:
 
2138
        try:
 
2139
            with open(stored_state_path, "rb") as stored_state:
 
2140
                clients_data, old_client_settings = pickle.load(stored_state)
 
2141
            os.remove(stored_state_path)
 
2142
        except IOError as e:
 
2143
            logger.warning("Could not load persistant state: {0}".format(e))
 
2144
            if e.errno != errno.ENOENT:
 
2145
                raise
 
2146
 
 
2147
    for client in clients_data:
 
2148
        client_name = client["name"]
 
2149
        
 
2150
        # Decide which value to use after restoring saved state.
 
2151
        # We have three different values: Old config file,
 
2152
        # new config file, and saved state.
 
2153
        # New config value takes precedence if it differs from old
 
2154
        # config value, otherwise use saved state.
 
2155
        for name, value in client_settings[client_name].items():
2048
2156
            try:
2049
 
                yield (name, special_settings[name]())
 
2157
                # For each value in new config, check if it differs
 
2158
                # from the old config value (Except for the "secret"
 
2159
                # attribute)
 
2160
                if name != "secret" and value != old_client_settings[client_name][name]:
 
2161
                    setattr(client, name, value)
2050
2162
            except KeyError:
2051
 
                yield (name, value)
 
2163
                pass
 
2164
 
 
2165
        # Clients who has passed its expire date, can still be enabled if its
 
2166
        # last checker was sucessful. Clients who checkers failed before we
 
2167
        # stored it state is asumed to had failed checker during downtime.
 
2168
        if client["enabled"] and client["last_checked_ok"]:
 
2169
            if ((datetime.datetime.utcnow() - client["last_checked_ok"])
 
2170
                > client["interval"]):
 
2171
                if client["last_checker_status"] != 0:
 
2172
                    client["enabled"] = False
 
2173
                else:
 
2174
                    client["expires"] = datetime.datetime.utcnow() + client["timeout"]
 
2175
 
 
2176
        client["changedstate"] = (multiprocessing_manager
 
2177
                                  .Condition(multiprocessing_manager
 
2178
                                             .Lock()))
 
2179
        if use_dbus:
 
2180
            new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
 
2181
            tcp_server.clients[client_name] = new_client
 
2182
            new_client.bus = bus
 
2183
            for name, value in client.iteritems():
 
2184
                setattr(new_client, name, value)
 
2185
            client_object_name = unicode(client_name).translate(
 
2186
                {ord("."): ord("_"),
 
2187
                 ord("-"): ord("_")})
 
2188
            new_client.dbus_object_path = (dbus.ObjectPath
 
2189
                                     ("/clients/" + client_object_name))
 
2190
            DBusObjectWithProperties.__init__(new_client,
 
2191
                                              new_client.bus,
 
2192
                                              new_client.dbus_object_path)
 
2193
        else:
 
2194
            tcp_server.clients[client_name] = Client.__new__(Client)
 
2195
            for name, value in client.iteritems():
 
2196
                setattr(tcp_server.clients[client_name], name, value)
 
2197
                
 
2198
        tcp_server.clients[client_name].decrypt_secret(
 
2199
            client_settings[client_name]["secret"])            
 
2200
        
 
2201
    # Create/remove clients based on new changes made to config
 
2202
    for clientname in set(old_client_settings) - set(client_settings):
 
2203
        del tcp_server.clients[clientname]
 
2204
    for clientname in set(client_settings) - set(old_client_settings):
 
2205
        tcp_server.clients[clientname] = (client_class(name = clientname,
 
2206
                                                       config =
 
2207
                                                       client_settings
 
2208
                                                       [clientname]))
2052
2209
    
2053
 
    tcp_server.clients.update(set(
2054
 
            client_class(name = section,
2055
 
                         config= dict(client_config_items(
2056
 
                        client_config, section)))
2057
 
            for section in client_config.sections()))
 
2210
 
2058
2211
    if not tcp_server.clients:
2059
2212
        logger.warning("No clients defined")
2060
2213
        
2103
2256
            def GetAllClients(self):
2104
2257
                "D-Bus method"
2105
2258
                return dbus.Array(c.dbus_object_path
2106
 
                                  for c in tcp_server.clients)
 
2259
                                  for c in
 
2260
                                  tcp_server.clients.itervalues())
2107
2261
            
2108
2262
            @dbus.service.method(_interface,
2109
2263
                                 out_signature="a{oa{sv}}")
2111
2265
                "D-Bus method"
2112
2266
                return dbus.Dictionary(
2113
2267
                    ((c.dbus_object_path, c.GetAll(""))
2114
 
                     for c in tcp_server.clients),
 
2268
                     for c in tcp_server.clients.itervalues()),
2115
2269
                    signature="oa{sv}")
2116
2270
            
2117
2271
            @dbus.service.method(_interface, in_signature="o")
2118
2272
            def RemoveClient(self, object_path):
2119
2273
                "D-Bus method"
2120
 
                for c in tcp_server.clients:
 
2274
                for c in tcp_server.clients.itervalues():
2121
2275
                    if c.dbus_object_path == object_path:
2122
 
                        tcp_server.clients.remove(c)
 
2276
                        del tcp_server.clients[c.name]
2123
2277
                        c.remove_from_connection()
2124
2278
                        # Don't signal anything except ClientRemoved
2125
2279
                        c.disable(quiet=True)
2139
2293
        service.cleanup()
2140
2294
        
2141
2295
        multiprocessing.active_children()
 
2296
        if not (tcp_server.clients or client_settings):
 
2297
            return
 
2298
 
 
2299
        # Store client before exiting. Secrets are encrypted with key based
 
2300
        # on what config file has. If config file is removed/edited, old
 
2301
        # secret will thus be unrecovable.
 
2302
        clients = []
 
2303
        for client in tcp_server.clients.itervalues():
 
2304
            client.encrypt_secret(client_settings[client.name]["secret"])
 
2305
 
 
2306
            client_dict = {}
 
2307
 
 
2308
            # A list of attributes that will not be stored when shuting down.
 
2309
            exclude = set(("bus", "changedstate", "secret"))            
 
2310
            for name, typ in inspect.getmembers(dbus.service.Object):
 
2311
                exclude.add(name)
 
2312
                
 
2313
            client_dict["encrypted_secret"] = client.encrypted_secret
 
2314
            for attr in client.client_structure:
 
2315
                if attr not in exclude:
 
2316
                    client_dict[attr] = getattr(client, attr)
 
2317
 
 
2318
            clients.append(client_dict) 
 
2319
            del client_settings[client.name]["secret"]
 
2320
            
 
2321
        try:
 
2322
            with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
 
2323
                pickle.dump((clients, client_settings), stored_state)
 
2324
        except IOError as e:
 
2325
            logger.warning("Could not save persistant state: {0}".format(e))
 
2326
            if e.errno != errno.ENOENT:
 
2327
                raise
 
2328
 
 
2329
        # Delete all clients, and settings from config
2142
2330
        while tcp_server.clients:
2143
 
            client = tcp_server.clients.pop()
 
2331
            name, client = tcp_server.clients.popitem()
2144
2332
            if use_dbus:
2145
2333
                client.remove_from_connection()
2146
 
            client.disable_hook = None
2147
2334
            # Don't signal anything except ClientRemoved
2148
2335
            client.disable(quiet=True)
2149
2336
            if use_dbus:
2151
2338
                mandos_dbus_service.ClientRemoved(client
2152
2339
                                                  .dbus_object_path,
2153
2340
                                                  client.name)
 
2341
        client_settings.clear()
2154
2342
    
2155
2343
    atexit.register(cleanup)
2156
2344
    
2157
 
    for client in tcp_server.clients:
 
2345
    for client in tcp_server.clients.itervalues():
2158
2346
        if use_dbus:
2159
2347
            # Emit D-Bus signal
2160
2348
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2161
 
        client.enable()
 
2349
        # Need to initiate checking of clients
 
2350
        if client.enabled:
 
2351
            client.init_checker()
 
2352
 
2162
2353
    
2163
2354
    tcp_server.enable()
2164
2355
    tcp_server.server_activate()