/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

* initramfs-tools-hook: Set DEVICE for network hooks.

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
67
66
 
68
67
import dbus
69
68
import dbus.service
74
73
import ctypes.util
75
74
import xml.dom.minidom
76
75
import inspect
77
 
import Crypto.Cipher.AES
78
76
 
79
77
try:
80
78
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
87
85
 
88
86
version = "1.4.1"
89
87
 
90
 
logger = logging.getLogger()
91
 
stored_state_path = "/var/lib/mandos/clients.pickle"
92
 
 
 
88
#logger = logging.getLogger('mandos')
 
89
logger = logging.Logger('mandos')
93
90
syslogger = (logging.handlers.SysLogHandler
94
91
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
95
92
              address = str("/dev/log")))
99
96
logger.addHandler(syslogger)
100
97
 
101
98
console = logging.StreamHandler()
102
 
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
103
 
                                       ' [%(process)d]:'
 
99
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
104
100
                                       ' %(levelname)s:'
105
101
                                       ' %(message)s'))
106
102
logger.addHandler(console)
107
103
 
108
 
 
109
104
class AvahiError(Exception):
110
105
    def __init__(self, value, *args, **kwargs):
111
106
        self.value = value
169
164
                            .GetAlternativeServiceName(self.name))
170
165
        logger.info("Changing Zeroconf service name to %r ...",
171
166
                    self.name)
 
167
        syslogger.setFormatter(logging.Formatter
 
168
                               ('Mandos (%s) [%%(process)d]:'
 
169
                                ' %%(levelname)s: %%(message)s'
 
170
                                % self.name))
172
171
        self.remove()
173
172
        try:
174
173
            self.add()
194
193
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
195
194
        self.entry_group_state_changed_match = (
196
195
            self.group.connect_to_signal(
197
 
                'StateChanged', self.entry_group_state_changed))
 
196
                'StateChanged', self .entry_group_state_changed))
198
197
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
199
198
                     self.name, self.type)
200
199
        self.group.AddService(
266
265
                                 self.server_state_changed)
267
266
        self.server_state_changed(self.server.GetState())
268
267
 
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
278
268
 
279
269
def _timedelta_to_milliseconds(td):
280
270
    "Convert a datetime.timedelta() to milliseconds"
299
289
                     instance %(name)s can be used in the command.
300
290
    checker_initiator_tag: a gobject event source tag, or None
301
291
    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
304
292
    current_checker_command: string; current running checker_command
 
293
    disable_hook:  If set, called by disable() as disable_hook(self)
305
294
    disable_initiator_tag: a gobject event source tag, or None
306
295
    enabled:    bool()
307
296
    fingerprint: string (40 or 32 hexadecimal digits); used to
310
299
    interval:   datetime.timedelta(); How often to start a new checker
311
300
    last_approval_request: datetime.datetime(); (UTC) or None
312
301
    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.
316
302
    last_enabled: datetime.datetime(); (UTC)
317
303
    name:       string; from the config file, used in log messages and
318
304
                        D-Bus identifiers
345
331
    def approval_delay_milliseconds(self):
346
332
        return _timedelta_to_milliseconds(self.approval_delay)
347
333
    
348
 
    def __init__(self, name = None, config=None):
 
334
    def __init__(self, name = None, disable_hook=None, config=None):
349
335
        """Note: the 'checker' key in 'config' sets the
350
336
        'checker_command' attribute and *not* the 'checker'
351
337
        attribute."""
371
357
                            % self.name)
372
358
        self.host = config.get("host", "")
373
359
        self.created = datetime.datetime.utcnow()
374
 
        self.enabled = True
 
360
        self.enabled = False
375
361
        self.last_approval_request = None
376
 
        self.last_enabled = datetime.datetime.utcnow()
 
362
        self.last_enabled = None
377
363
        self.last_checked_ok = None
378
 
        self.last_checker_status = None
379
364
        self.timeout = string_to_delta(config["timeout"])
380
365
        self.extended_timeout = string_to_delta(config
381
366
                                                ["extended_timeout"])
382
367
        self.interval = string_to_delta(config["interval"])
 
368
        self.disable_hook = disable_hook
383
369
        self.checker = None
384
370
        self.checker_initiator_tag = None
385
371
        self.disable_initiator_tag = None
386
 
        self.expires = datetime.datetime.utcnow() + self.timeout
 
372
        self.expires = None
387
373
        self.checker_callback_tag = None
388
374
        self.checker_command = config["checker"]
389
375
        self.current_checker_command = None
 
376
        self.last_connect = None
390
377
        self._approved = None
391
378
        self.approved_by_default = config.get("approved_by_default",
392
379
                                              True)
398
385
        self.changedstate = (multiprocessing_manager
399
386
                             .Condition(multiprocessing_manager
400
387
                                        .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)
409
388
    
410
 
    # Send notice to process children that client state has changed
411
389
    def send_changedstate(self):
412
 
        with self.changedstate:
413
 
            self.changedstate.notify_all()
 
390
        self.changedstate.acquire()
 
391
        self.changedstate.notify_all()
 
392
        self.changedstate.release()
414
393
    
415
394
    def enable(self):
416
395
        """Start this client's checker and timeout hooks"""
418
397
            # Already enabled
419
398
            return
420
399
        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
421
406
        self.expires = datetime.datetime.utcnow() + self.timeout
 
407
        self.disable_initiator_tag = (gobject.timeout_add
 
408
                                   (self.timeout_milliseconds(),
 
409
                                    self.disable))
422
410
        self.enabled = True
423
411
        self.last_enabled = datetime.datetime.utcnow()
424
 
        self.init_checker()
 
412
        # Also start a new checker *right now*.
 
413
        self.start_checker()
425
414
    
426
415
    def disable(self, quiet=True):
427
416
        """Disable this client."""
439
428
            gobject.source_remove(self.checker_initiator_tag)
440
429
            self.checker_initiator_tag = None
441
430
        self.stop_checker()
 
431
        if self.disable_hook:
 
432
            self.disable_hook(self)
442
433
        self.enabled = False
443
434
        # Do not run this again if called by a gobject.timeout_add
444
435
        return False
445
436
    
446
437
    def __del__(self):
 
438
        self.disable_hook = None
447
439
        self.disable()
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
 
        
 
440
    
463
441
    def checker_callback(self, pid, condition, command):
464
442
        """The checker has completed, so take appropriate actions."""
465
443
        self.checker_callback_tag = None
466
444
        self.checker = None
467
445
        if os.WIFEXITED(condition):
468
 
            self.last_checker_status =  os.WEXITSTATUS(condition)
469
 
            if self.last_checker_status == 0:
 
446
            exitstatus = os.WEXITSTATUS(condition)
 
447
            if exitstatus == 0:
470
448
                logger.info("Checker for %(name)s succeeded",
471
449
                            vars(self))
472
450
                self.checked_ok()
474
452
                logger.info("Checker for %(name)s failed",
475
453
                            vars(self))
476
454
        else:
477
 
            self.last_checker_status = -1
478
455
            logger.warning("Checker for %(name)s crashed?",
479
456
                           vars(self))
480
457
    
591
568
                raise
592
569
        self.checker = None
593
570
 
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
 
 
640
571
 
641
572
def dbus_service_property(dbus_interface, signature="v",
642
573
                          access="readwrite", byte_arrays=False):
943
874
    # dbus.service.Object doesn't use super(), so we can't either.
944
875
    
945
876
    def __init__(self, bus = None, *args, **kwargs):
 
877
        self._approvals_pending = 0
946
878
        self.bus = bus
947
879
        Client.__init__(self, *args, **kwargs)
948
 
 
949
 
        self._approvals_pending = 0
950
880
        # Only now, when this client is initialized, can it show up on
951
881
        # the D-Bus
952
882
        client_object_name = unicode(self.name).translate(
1122
1052
        "D-Bus signal"
1123
1053
        return self.need_approval()
1124
1054
    
1125
 
    # NeRwequest - signal
1126
 
    @dbus.service.signal(_interface, signature="s")
1127
 
    def NewRequest(self, ip):
1128
 
        """D-Bus signal
1129
 
        Is sent after a client request a password.
1130
 
        """
1131
 
        pass
1132
 
 
1133
1055
    ## Methods
1134
1056
    
1135
1057
    # Approve - method
1433
1355
                    logger.warning("Bad certificate: %s", error)
1434
1356
                    return
1435
1357
                logger.debug("Fingerprint: %s", fpr)
1436
 
                if self.server.use_dbus:
1437
 
                    # Emit D-Bus signal
1438
 
                    client.NewRequest(str(self.client_address))
1439
1358
                
1440
1359
                try:
1441
1360
                    client = ProxyClient(child_pipe, fpr,
1705
1624
        self.enabled = False
1706
1625
        self.clients = clients
1707
1626
        if self.clients is None:
1708
 
            self.clients = {}
 
1627
            self.clients = set()
1709
1628
        self.use_dbus = use_dbus
1710
1629
        self.gnutls_priority = gnutls_priority
1711
1630
        IPv6_TCPServer.__init__(self, server_address,
1758
1677
            fpr = request[1]
1759
1678
            address = request[2]
1760
1679
            
1761
 
            for c in self.clients.itervalues():
 
1680
            for c in self.clients:
1762
1681
                if c.fingerprint == fpr:
1763
1682
                    client = c
1764
1683
                    break
1932
1851
                        " system bus interface")
1933
1852
    parser.add_argument("--no-ipv6", action="store_false",
1934
1853
                        dest="use_ipv6", help="Do not use IPv6")
1935
 
    parser.add_argument("--no-restore", action="store_false",
1936
 
                        dest="restore", help="Do not restore stored state",
1937
 
                        default=True)
1938
 
 
1939
1854
    options = parser.parse_args()
1940
1855
    
1941
1856
    if options.check:
1976
1891
    # options, if set.
1977
1892
    for option in ("interface", "address", "port", "debug",
1978
1893
                   "priority", "servicename", "configdir",
1979
 
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
 
1894
                   "use_dbus", "use_ipv6", "debuglevel"):
1980
1895
        value = getattr(options, option)
1981
1896
        if value is not None:
1982
1897
            server_settings[option] = value
2055
1970
            raise error
2056
1971
    
2057
1972
    if not debug and not debuglevel:
2058
 
        logger.setLevel(logging.WARNING)
 
1973
        syslogger.setLevel(logging.WARNING)
 
1974
        console.setLevel(logging.WARNING)
2059
1975
    if debuglevel:
2060
1976
        level = getattr(logging, debuglevel.upper())
2061
 
        logger.setLevel(level)
 
1977
        syslogger.setLevel(level)
 
1978
        console.setLevel(level)
2062
1979
    
2063
1980
    if debug:
2064
 
        logger.setLevel(logging.DEBUG)
2065
1981
        # Enable all possible GnuTLS debugging
2066
1982
        
2067
1983
        # "Use a log level over 10 to enable all debugging options."
2108
2024
            server_settings["use_dbus"] = False
2109
2025
            tcp_server.use_dbus = False
2110
2026
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2111
 
    service = AvahiServiceToSyslog(name =
2112
 
                                   server_settings["servicename"],
2113
 
                                   servicetype = "_mandos._tcp",
2114
 
                                   protocol = protocol, bus = bus)
 
2027
    service = AvahiService(name = server_settings["servicename"],
 
2028
                           servicetype = "_mandos._tcp",
 
2029
                           protocol = protocol, bus = bus)
2115
2030
    if server_settings["interface"]:
2116
2031
        service.interface = (if_nametoindex
2117
2032
                             (str(server_settings["interface"])))
2123
2038
    if use_dbus:
2124
2039
        client_class = functools.partial(ClientDBusTransitional,
2125
2040
                                         bus = bus)
2126
 
    
2127
 
    special_settings = {
2128
 
        # Some settings need to be accessd by special methods;
2129
 
        # booleans need .getboolean(), etc.  Here is a list of them:
2130
 
        "approved_by_default":
2131
 
            lambda section:
2132
 
            client_config.getboolean(section, "approved_by_default"),
2133
 
        }
2134
 
    # Construct a new dict of client settings of this form:
2135
 
    # { client_name: {setting_name: value, ...}, ...}
2136
 
    # with exceptions for any special settings as defined above
2137
 
    client_settings = dict((clientname,
2138
 
                           dict((setting,
2139
 
                                 (value if setting not in special_settings
2140
 
                                  else special_settings[setting](clientname)))
2141
 
                                for setting, value in client_config.items(clientname)))
2142
 
                          for clientname in client_config.sections())
2143
 
    
2144
 
    old_client_settings = {}
2145
 
    clients_data = []
2146
 
 
2147
 
    # Get client data and settings from last running state. 
2148
 
    if server_settings["restore"]:
2149
 
        try:
2150
 
            with open(stored_state_path, "rb") as stored_state:
2151
 
                clients_data, old_client_settings = pickle.load(stored_state)
2152
 
            os.remove(stored_state_path)
2153
 
        except IOError as e:
2154
 
            logger.warning("Could not load persistant state: {0}".format(e))
2155
 
            if e.errno != errno.ENOENT:
2156
 
                raise
2157
 
 
2158
 
    for client in clients_data:
2159
 
        client_name = client["name"]
2160
 
        
2161
 
        # Decide which value to use after restoring saved state.
2162
 
        # We have three different values: Old config file,
2163
 
        # new config file, and saved state.
2164
 
        # New config value takes precedence if it differs from old
2165
 
        # config value, otherwise use saved state.
2166
 
        for name, value in client_settings[client_name].items():
 
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):
2167
2048
            try:
2168
 
                # For each value in new config, check if it differs
2169
 
                # from the old config value (Except for the "secret"
2170
 
                # attribute)
2171
 
                if name != "secret" and value != old_client_settings[client_name][name]:
2172
 
                    setattr(client, name, value)
 
2049
                yield (name, special_settings[name]())
2173
2050
            except KeyError:
2174
 
                pass
2175
 
 
2176
 
        # Clients who has passed its expire date, can still be enabled if its
2177
 
        # last checker was sucessful. Clients who checkers failed before we
2178
 
        # stored it state is asumed to had failed checker during downtime.
2179
 
        if client["enabled"] and client["last_checked_ok"]:
2180
 
            if ((datetime.datetime.utcnow() - client["last_checked_ok"])
2181
 
                > client["interval"]):
2182
 
                if client["last_checker_status"] != 0:
2183
 
                    client["enabled"] = False
2184
 
                else:
2185
 
                    client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2186
 
 
2187
 
        client["changedstate"] = (multiprocessing_manager
2188
 
                                  .Condition(multiprocessing_manager
2189
 
                                             .Lock()))
2190
 
        if use_dbus:
2191
 
            new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
2192
 
            tcp_server.clients[client_name] = new_client
2193
 
            new_client.bus = bus
2194
 
            for name, value in client.iteritems():
2195
 
                setattr(new_client, name, value)
2196
 
            client_object_name = unicode(client_name).translate(
2197
 
                {ord("."): ord("_"),
2198
 
                 ord("-"): ord("_")})
2199
 
            new_client.dbus_object_path = (dbus.ObjectPath
2200
 
                                     ("/clients/" + client_object_name))
2201
 
            DBusObjectWithProperties.__init__(new_client,
2202
 
                                              new_client.bus,
2203
 
                                              new_client.dbus_object_path)
2204
 
        else:
2205
 
            tcp_server.clients[client_name] = Client.__new__(Client)
2206
 
            for name, value in client.iteritems():
2207
 
                setattr(tcp_server.clients[client_name], name, value)
2208
 
                
2209
 
        tcp_server.clients[client_name].decrypt_secret(
2210
 
            client_settings[client_name]["secret"])            
2211
 
        
2212
 
    # Create/remove clients based on new changes made to config
2213
 
    for clientname in set(old_client_settings) - set(client_settings):
2214
 
        del tcp_server.clients[clientname]
2215
 
    for clientname in set(client_settings) - set(old_client_settings):
2216
 
        tcp_server.clients[clientname] = (client_class(name = clientname,
2217
 
                                                       config =
2218
 
                                                       client_settings
2219
 
                                                       [clientname]))
 
2051
                yield (name, value)
2220
2052
    
2221
 
 
 
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()))
2222
2058
    if not tcp_server.clients:
2223
2059
        logger.warning("No clients defined")
2224
2060
        
2267
2103
            def GetAllClients(self):
2268
2104
                "D-Bus method"
2269
2105
                return dbus.Array(c.dbus_object_path
2270
 
                                  for c in
2271
 
                                  tcp_server.clients.itervalues())
 
2106
                                  for c in tcp_server.clients)
2272
2107
            
2273
2108
            @dbus.service.method(_interface,
2274
2109
                                 out_signature="a{oa{sv}}")
2276
2111
                "D-Bus method"
2277
2112
                return dbus.Dictionary(
2278
2113
                    ((c.dbus_object_path, c.GetAll(""))
2279
 
                     for c in tcp_server.clients.itervalues()),
 
2114
                     for c in tcp_server.clients),
2280
2115
                    signature="oa{sv}")
2281
2116
            
2282
2117
            @dbus.service.method(_interface, in_signature="o")
2283
2118
            def RemoveClient(self, object_path):
2284
2119
                "D-Bus method"
2285
 
                for c in tcp_server.clients.itervalues():
 
2120
                for c in tcp_server.clients:
2286
2121
                    if c.dbus_object_path == object_path:
2287
 
                        del tcp_server.clients[c.name]
 
2122
                        tcp_server.clients.remove(c)
2288
2123
                        c.remove_from_connection()
2289
2124
                        # Don't signal anything except ClientRemoved
2290
2125
                        c.disable(quiet=True)
2304
2139
        service.cleanup()
2305
2140
        
2306
2141
        multiprocessing.active_children()
2307
 
        if not (tcp_server.clients or client_settings):
2308
 
            return
2309
 
 
2310
 
        # Store client before exiting. Secrets are encrypted with key based
2311
 
        # on what config file has. If config file is removed/edited, old
2312
 
        # secret will thus be unrecovable.
2313
 
        clients = []
2314
 
        for client in tcp_server.clients.itervalues():
2315
 
            client.encrypt_secret(client_settings[client.name]["secret"])
2316
 
 
2317
 
            client_dict = {}
2318
 
 
2319
 
            # A list of attributes that will not be stored when shuting down.
2320
 
            exclude = set(("bus", "changedstate", "secret"))            
2321
 
            for name, typ in inspect.getmembers(dbus.service.Object):
2322
 
                exclude.add(name)
2323
 
                
2324
 
            client_dict["encrypted_secret"] = client.encrypted_secret
2325
 
            for attr in client.client_structure:
2326
 
                if attr not in exclude:
2327
 
                    client_dict[attr] = getattr(client, attr)
2328
 
 
2329
 
            clients.append(client_dict) 
2330
 
            del client_settings[client.name]["secret"]
2331
 
            
2332
 
        try:
2333
 
            with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
2334
 
                pickle.dump((clients, client_settings), stored_state)
2335
 
        except IOError as e:
2336
 
            logger.warning("Could not save persistant state: {0}".format(e))
2337
 
            if e.errno != errno.ENOENT:
2338
 
                raise
2339
 
 
2340
 
        # Delete all clients, and settings from config
2341
2142
        while tcp_server.clients:
2342
 
            name, client = tcp_server.clients.popitem()
 
2143
            client = tcp_server.clients.pop()
2343
2144
            if use_dbus:
2344
2145
                client.remove_from_connection()
 
2146
            client.disable_hook = None
2345
2147
            # Don't signal anything except ClientRemoved
2346
2148
            client.disable(quiet=True)
2347
2149
            if use_dbus:
2349
2151
                mandos_dbus_service.ClientRemoved(client
2350
2152
                                                  .dbus_object_path,
2351
2153
                                                  client.name)
2352
 
        client_settings.clear()
2353
2154
    
2354
2155
    atexit.register(cleanup)
2355
2156
    
2356
 
    for client in tcp_server.clients.itervalues():
 
2157
    for client in tcp_server.clients:
2357
2158
        if use_dbus:
2358
2159
            # Emit D-Bus signal
2359
2160
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2360
 
        # Need to initiate checking of clients
2361
 
        if client.enabled:
2362
 
            client.init_checker()
2363
 
 
 
2161
        client.enable()
2364
2162
    
2365
2163
    tcp_server.enable()
2366
2164
    tcp_server.server_activate()