/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Björn Påhlsson
  • Date: 2011-12-20 20:13:36 UTC
  • mfrom: (505.3.26 client-network-hooks)
  • mto: This revision was merged to the branch mainline in revision 525.
  • Revision ID: belorn@recompile.se-20111220201336-zs7sj3inc9wtioau
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
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
314
 
                         status of last checker. -1 reflect crashed
315
 
                         checker, 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
402
 
                                 in self.__dict__.iterkeys()
403
 
                                 if not attr.startswith("_")]
404
 
        self.client_structure.append("client_structure")
405
 
 
406
 
 
407
 
        for name, t in inspect.getmembers(type(self),
408
 
                                          lambda obj:
409
 
                                              isinstance(obj,
410
 
                                                         property)):
411
 
            if not name.startswith("_"):
412
 
                self.client_structure.append(name)
413
388
    
414
 
    # Send notice to process children that client state has changed
415
389
    def send_changedstate(self):
416
 
        with self.changedstate:
417
 
            self.changedstate.notify_all()
 
390
        self.changedstate.acquire()
 
391
        self.changedstate.notify_all()
 
392
        self.changedstate.release()
418
393
    
419
394
    def enable(self):
420
395
        """Start this client's checker and timeout hooks"""
422
397
            # Already enabled
423
398
            return
424
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
425
406
        self.expires = datetime.datetime.utcnow() + self.timeout
 
407
        self.disable_initiator_tag = (gobject.timeout_add
 
408
                                   (self.timeout_milliseconds(),
 
409
                                    self.disable))
426
410
        self.enabled = True
427
411
        self.last_enabled = datetime.datetime.utcnow()
428
 
        self.init_checker()
 
412
        # Also start a new checker *right now*.
 
413
        self.start_checker()
429
414
    
430
415
    def disable(self, quiet=True):
431
416
        """Disable this client."""
443
428
            gobject.source_remove(self.checker_initiator_tag)
444
429
            self.checker_initiator_tag = None
445
430
        self.stop_checker()
 
431
        if self.disable_hook:
 
432
            self.disable_hook(self)
446
433
        self.enabled = False
447
434
        # Do not run this again if called by a gobject.timeout_add
448
435
        return False
449
436
    
450
437
    def __del__(self):
 
438
        self.disable_hook = None
451
439
        self.disable()
452
 
 
453
 
    def init_checker(self):
454
 
        # Schedule a new checker to be started an 'interval' from now,
455
 
        # and every interval from then on.
456
 
        self.checker_initiator_tag = (gobject.timeout_add
457
 
                                      (self.interval_milliseconds(),
458
 
                                       self.start_checker))
459
 
        # Schedule a disable() when 'timeout' has passed
460
 
        self.disable_initiator_tag = (gobject.timeout_add
461
 
                                   (self.timeout_milliseconds(),
462
 
                                    self.disable))
463
 
        # Also start a new checker *right now*.
464
 
        self.start_checker()
465
 
 
466
 
        
 
440
    
467
441
    def checker_callback(self, pid, condition, command):
468
442
        """The checker has completed, so take appropriate actions."""
469
443
        self.checker_callback_tag = None
470
444
        self.checker = None
471
445
        if os.WIFEXITED(condition):
472
 
            self.last_checker_status =  os.WEXITSTATUS(condition)
473
 
            if self.last_checker_status == 0:
 
446
            exitstatus = os.WEXITSTATUS(condition)
 
447
            if exitstatus == 0:
474
448
                logger.info("Checker for %(name)s succeeded",
475
449
                            vars(self))
476
450
                self.checked_ok()
478
452
                logger.info("Checker for %(name)s failed",
479
453
                            vars(self))
480
454
        else:
481
 
            self.last_checker_status = -1
482
455
            logger.warning("Checker for %(name)s crashed?",
483
456
                           vars(self))
484
457
    
595
568
                raise
596
569
        self.checker = None
597
570
 
598
 
    # Encrypts a client secret and stores it in a varible
599
 
    # encrypted_secret
600
 
    def encrypt_secret(self, key):
601
 
        # Encryption-key need to be of a specific size, so we hash
602
 
        # supplied key
603
 
        hasheng = hashlib.sha256()
604
 
        hasheng.update(key)
605
 
        encryptionkey = hasheng.digest()
606
 
 
607
 
        # Create validation hash so we know at decryption if it was
608
 
        # sucessful
609
 
        hasheng = hashlib.sha256()
610
 
        hasheng.update(self.secret)
611
 
        validationhash = hasheng.digest()
612
 
 
613
 
        # Encrypt secret
614
 
        iv = os.urandom(Crypto.Cipher.AES.block_size)
615
 
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
616
 
                                        Crypto.Cipher.AES.MODE_CFB, iv)
617
 
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
618
 
        self.encrypted_secret = (ciphertext, iv)
619
 
 
620
 
    # Decrypt a encrypted client secret
621
 
    def decrypt_secret(self, key):
622
 
        # Decryption-key need to be of a specific size, so we hash
623
 
        # supplied key
624
 
        hasheng = hashlib.sha256()
625
 
        hasheng.update(key)
626
 
        encryptionkey = hasheng.digest()
627
 
 
628
 
        # Decrypt encrypted secret
629
 
        ciphertext, iv = self.encrypted_secret
630
 
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
631
 
                                        Crypto.Cipher.AES.MODE_CFB, iv)
632
 
        plain = ciphereng.decrypt(ciphertext)
633
 
 
634
 
        # Validate decrypted secret to know if it was succesful
635
 
        hasheng = hashlib.sha256()
636
 
        validationhash = plain[:hasheng.digest_size]
637
 
        secret = plain[hasheng.digest_size:]
638
 
        hasheng.update(secret)
639
 
 
640
 
        # If validation fails, we use key as new secret. Otherwise, we
641
 
        # use the decrypted secret
642
 
        if hasheng.digest() == validationhash:
643
 
            self.secret = secret
644
 
        else:
645
 
            self.secret = key
646
 
        del self.encrypted_secret
647
 
 
648
571
 
649
572
def dbus_service_property(dbus_interface, signature="v",
650
573
                          access="readwrite", byte_arrays=False):
951
874
    # dbus.service.Object doesn't use super(), so we can't either.
952
875
    
953
876
    def __init__(self, bus = None, *args, **kwargs):
954
 
        self.bus = bus
955
877
        self._approvals_pending = 0
 
878
        self.bus = bus
956
879
        Client.__init__(self, *args, **kwargs)
957
 
        self.add_to_dbus()
958
 
    
959
 
    def add_to_dbus(self):
960
880
        # Only now, when this client is initialized, can it show up on
961
881
        # the D-Bus
962
882
        client_object_name = unicode(self.name).translate(
1012
932
        datetime_to_dbus, "LastApprovalRequest")
1013
933
    approved_by_default = notifychangeproperty(dbus.Boolean,
1014
934
                                               "ApprovedByDefault")
1015
 
    approval_delay = notifychangeproperty(dbus.UInt64,
 
935
    approval_delay = notifychangeproperty(dbus.UInt16,
1016
936
                                          "ApprovalDelay",
1017
937
                                          type_func =
1018
938
                                          _timedelta_to_milliseconds)
1019
939
    approval_duration = notifychangeproperty(
1020
 
        dbus.UInt64, "ApprovalDuration",
 
940
        dbus.UInt16, "ApprovalDuration",
1021
941
        type_func = _timedelta_to_milliseconds)
1022
942
    host = notifychangeproperty(dbus.String, "Host")
1023
 
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
 
943
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
1024
944
                                   type_func =
1025
945
                                   _timedelta_to_milliseconds)
1026
946
    extended_timeout = notifychangeproperty(
1027
 
        dbus.UInt64, "ExtendedTimeout",
 
947
        dbus.UInt16, "ExtendedTimeout",
1028
948
        type_func = _timedelta_to_milliseconds)
1029
 
    interval = notifychangeproperty(dbus.UInt64,
 
949
    interval = notifychangeproperty(dbus.UInt16,
1030
950
                                    "Interval",
1031
951
                                    type_func =
1032
952
                                    _timedelta_to_milliseconds)
1704
1624
        self.enabled = False
1705
1625
        self.clients = clients
1706
1626
        if self.clients is None:
1707
 
            self.clients = {}
 
1627
            self.clients = set()
1708
1628
        self.use_dbus = use_dbus
1709
1629
        self.gnutls_priority = gnutls_priority
1710
1630
        IPv6_TCPServer.__init__(self, server_address,
1757
1677
            fpr = request[1]
1758
1678
            address = request[2]
1759
1679
            
1760
 
            for c in self.clients.itervalues():
 
1680
            for c in self.clients:
1761
1681
                if c.fingerprint == fpr:
1762
1682
                    client = c
1763
1683
                    break
1931
1851
                        " system bus interface")
1932
1852
    parser.add_argument("--no-ipv6", action="store_false",
1933
1853
                        dest="use_ipv6", help="Do not use IPv6")
1934
 
    parser.add_argument("--no-restore", action="store_false",
1935
 
                        dest="restore",
1936
 
                        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
2140
 
                                  setting not in special_settings
2141
 
                                  else special_settings[setting]
2142
 
                                  (clientname)))
2143
 
                                for setting, value
2144
 
                                in client_config.items(clientname)))
2145
 
                          for clientname in client_config.sections())
2146
 
    
2147
 
    old_client_settings = {}
2148
 
    clients_data = []
2149
 
 
2150
 
    # Get client data and settings from last running state. 
2151
 
    if server_settings["restore"]:
2152
 
        try:
2153
 
            with open(stored_state_path, "rb") as stored_state:
2154
 
                clients_data, old_client_settings = (
2155
 
                    pickle.load(stored_state))
2156
 
            os.remove(stored_state_path)
2157
 
        except IOError as e:
2158
 
            logger.warning("Could not load persistant state: {0}"
2159
 
                           .format(e))
2160
 
            if e.errno != errno.ENOENT:
2161
 
                raise
2162
 
 
2163
 
    for client in clients_data:
2164
 
        client_name = client["name"]
2165
 
        
2166
 
        # Decide which value to use after restoring saved state.
2167
 
        # We have three different values: Old config file,
2168
 
        # new config file, and saved state.
2169
 
        # New config value takes precedence if it differs from old
2170
 
        # config value, otherwise use saved state.
2171
 
        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):
2172
2048
            try:
2173
 
                # For each value in new config, check if it differs
2174
 
                # from the old config value (Except for the "secret"
2175
 
                # attribute)
2176
 
                if (name != "secret" and
2177
 
                    value != old_client_settings[client_name][name]):
2178
 
                    setattr(client, name, value)
 
2049
                yield (name, special_settings[name]())
2179
2050
            except KeyError:
2180
 
                pass
2181
 
 
2182
 
        # Clients who has passed its expire date, can still be enabled
2183
 
        # if its last checker was sucessful. Clients who checkers
2184
 
        # failed before we stored it state is asumed to had failed
2185
 
        # checker during downtime.
2186
 
        if client["enabled"] and client["last_checked_ok"]:
2187
 
            if ((datetime.datetime.utcnow()
2188
 
                 - client["last_checked_ok"]) > client["interval"]):
2189
 
                if client["last_checker_status"] != 0:
2190
 
                    client["enabled"] = False
2191
 
                else:
2192
 
                    client["expires"] = (datetime.datetime.utcnow()
2193
 
                                         + client["timeout"])
2194
 
 
2195
 
        client["changedstate"] = (multiprocessing_manager
2196
 
                                  .Condition(multiprocessing_manager
2197
 
                                             .Lock()))
2198
 
        if use_dbus:
2199
 
            new_client = ClientDBusTransitional.__new__(
2200
 
                ClientDBusTransitional)
2201
 
            tcp_server.clients[client_name] = new_client
2202
 
            new_client.bus = bus
2203
 
            for name, value in client.iteritems():
2204
 
                setattr(new_client, name, value)
2205
 
            new_client._approvals_pending = 0
2206
 
            new_client.add_to_dbus()
2207
 
        else:
2208
 
            tcp_server.clients[client_name] = Client.__new__(Client)
2209
 
            for name, value in client.iteritems():
2210
 
                setattr(tcp_server.clients[client_name], name, value)
2211
 
                
2212
 
        tcp_server.clients[client_name].decrypt_secret(
2213
 
            client_settings[client_name]["secret"])            
2214
 
        
2215
 
    # Create/remove clients based on new changes made to config
2216
 
    for clientname in set(old_client_settings) - set(client_settings):
2217
 
        del tcp_server.clients[clientname]
2218
 
    for clientname in set(client_settings) - set(old_client_settings):
2219
 
        tcp_server.clients[clientname] = client_class(name
2220
 
                                                      = clientname,
2221
 
                                                      config =
2222
 
                                                      client_settings
2223
 
                                                      [clientname])
 
2051
                yield (name, value)
2224
2052
    
 
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()))
2225
2058
    if not tcp_server.clients:
2226
2059
        logger.warning("No clients defined")
2227
2060
        
2270
2103
            def GetAllClients(self):
2271
2104
                "D-Bus method"
2272
2105
                return dbus.Array(c.dbus_object_path
2273
 
                                  for c in
2274
 
                                  tcp_server.clients.itervalues())
 
2106
                                  for c in tcp_server.clients)
2275
2107
            
2276
2108
            @dbus.service.method(_interface,
2277
2109
                                 out_signature="a{oa{sv}}")
2279
2111
                "D-Bus method"
2280
2112
                return dbus.Dictionary(
2281
2113
                    ((c.dbus_object_path, c.GetAll(""))
2282
 
                     for c in tcp_server.clients.itervalues()),
 
2114
                     for c in tcp_server.clients),
2283
2115
                    signature="oa{sv}")
2284
2116
            
2285
2117
            @dbus.service.method(_interface, in_signature="o")
2286
2118
            def RemoveClient(self, object_path):
2287
2119
                "D-Bus method"
2288
 
                for c in tcp_server.clients.itervalues():
 
2120
                for c in tcp_server.clients:
2289
2121
                    if c.dbus_object_path == object_path:
2290
 
                        del tcp_server.clients[c.name]
 
2122
                        tcp_server.clients.remove(c)
2291
2123
                        c.remove_from_connection()
2292
2124
                        # Don't signal anything except ClientRemoved
2293
2125
                        c.disable(quiet=True)
2307
2139
        service.cleanup()
2308
2140
        
2309
2141
        multiprocessing.active_children()
2310
 
        if not (tcp_server.clients or client_settings):
2311
 
            return
2312
 
 
2313
 
        # Store client before exiting. Secrets are encrypted with key
2314
 
        # based on what config file has. If config file is
2315
 
        # removed/edited, old secret will thus be unrecovable.
2316
 
        clients = []
2317
 
        for client in tcp_server.clients.itervalues():
2318
 
            client.encrypt_secret(
2319
 
                client_settings[client.name]["secret"])
2320
 
 
2321
 
            client_dict = {}
2322
 
 
2323
 
            # A list of attributes that will not be stored when
2324
 
            # shutting down.
2325
 
            exclude = set(("bus", "changedstate", "secret"))
2326
 
            for name, typ in inspect.getmembers(dbus.service.Object):
2327
 
                exclude.add(name)
2328
 
                
2329
 
            client_dict["encrypted_secret"] = client.encrypted_secret
2330
 
            for attr in client.client_structure:
2331
 
                if attr not in exclude:
2332
 
                    client_dict[attr] = getattr(client, attr)
2333
 
 
2334
 
            clients.append(client_dict) 
2335
 
            del client_settings[client.name]["secret"]
2336
 
            
2337
 
        try:
2338
 
            with os.fdopen(os.open(stored_state_path,
2339
 
                                   os.O_CREAT|os.O_WRONLY|os.O_TRUNC,
2340
 
                                   stat.S_IRUSR | stat.S_IWUSR),
2341
 
                           "wb") as stored_state:
2342
 
                pickle.dump((clients, client_settings), stored_state)
2343
 
        except IOError as e:
2344
 
            logger.warning("Could not save persistant state: {0}"
2345
 
                           .format(e))
2346
 
            if e.errno != errno.ENOENT:
2347
 
                raise
2348
 
 
2349
 
        # Delete all clients, and settings from config
2350
2142
        while tcp_server.clients:
2351
 
            name, client = tcp_server.clients.popitem()
 
2143
            client = tcp_server.clients.pop()
2352
2144
            if use_dbus:
2353
2145
                client.remove_from_connection()
 
2146
            client.disable_hook = None
2354
2147
            # Don't signal anything except ClientRemoved
2355
2148
            client.disable(quiet=True)
2356
2149
            if use_dbus:
2358
2151
                mandos_dbus_service.ClientRemoved(client
2359
2152
                                                  .dbus_object_path,
2360
2153
                                                  client.name)
2361
 
        client_settings.clear()
2362
2154
    
2363
2155
    atexit.register(cleanup)
2364
2156
    
2365
 
    for client in tcp_server.clients.itervalues():
 
2157
    for client in tcp_server.clients:
2366
2158
        if use_dbus:
2367
2159
            # Emit D-Bus signal
2368
2160
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2369
 
        # Need to initiate checking of clients
2370
 
        if client.enabled:
2371
 
            client.init_checker()
2372
 
 
 
2161
        client.enable()
2373
2162
    
2374
2163
    tcp_server.enable()
2375
2164
    tcp_server.server_activate()