/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

* 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
86
84
 
87
85
 
88
86
version = "1.4.1"
89
 
stored_state_path = "/var/lib/mandos/clients.pickle"
90
87
 
91
 
logger = logging.getLogger()
 
88
#logger = logging.getLogger('mandos')
 
89
logger = logging.Logger('mandos')
92
90
syslogger = (logging.handlers.SysLogHandler
93
91
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
94
92
              address = str("/dev/log")))
95
 
 
96
 
try:
97
 
    if_nametoindex = (ctypes.cdll.LoadLibrary
98
 
                      (ctypes.util.find_library("c"))
99
 
                      .if_nametoindex)
100
 
except (OSError, AttributeError):
101
 
    def if_nametoindex(interface):
102
 
        "Get an interface index the hard way, i.e. using fcntl()"
103
 
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
104
 
        with contextlib.closing(socket.socket()) as s:
105
 
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
106
 
                                struct.pack(str("16s16x"),
107
 
                                            interface))
108
 
        interface_index = struct.unpack(str("I"),
109
 
                                        ifreq[16:20])[0]
110
 
        return interface_index
111
 
 
112
 
 
113
 
def initlogger(level=logging.WARNING):
114
 
    """init logger and add loglevel"""
115
 
    
116
 
    syslogger.setFormatter(logging.Formatter
117
 
                           ('Mandos [%(process)d]: %(levelname)s:'
118
 
                            ' %(message)s'))
119
 
    logger.addHandler(syslogger)
120
 
    
121
 
    console = logging.StreamHandler()
122
 
    console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
123
 
                                           ' [%(process)d]:'
124
 
                                           ' %(levelname)s:'
125
 
                                           ' %(message)s'))
126
 
    logger.addHandler(console)
127
 
    logger.setLevel(level)
128
 
 
 
93
syslogger.setFormatter(logging.Formatter
 
94
                       ('Mandos [%(process)d]: %(levelname)s:'
 
95
                        ' %(message)s'))
 
96
logger.addHandler(syslogger)
 
97
 
 
98
console = logging.StreamHandler()
 
99
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
 
100
                                       ' %(levelname)s:'
 
101
                                       ' %(message)s'))
 
102
logger.addHandler(console)
129
103
 
130
104
class AvahiError(Exception):
131
105
    def __init__(self, value, *args, **kwargs):
190
164
                            .GetAlternativeServiceName(self.name))
191
165
        logger.info("Changing Zeroconf service name to %r ...",
192
166
                    self.name)
 
167
        syslogger.setFormatter(logging.Formatter
 
168
                               ('Mandos (%s) [%%(process)d]:'
 
169
                                ' %%(levelname)s: %%(message)s'
 
170
                                % self.name))
193
171
        self.remove()
194
172
        try:
195
173
            self.add()
215
193
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
216
194
        self.entry_group_state_changed_match = (
217
195
            self.group.connect_to_signal(
218
 
                'StateChanged', self.entry_group_state_changed))
 
196
                'StateChanged', self .entry_group_state_changed))
219
197
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
220
198
                     self.name, self.type)
221
199
        self.group.AddService(
247
225
            try:
248
226
                self.group.Free()
249
227
            except (dbus.exceptions.UnknownMethodException,
250
 
                    dbus.exceptions.DBusException):
 
228
                    dbus.exceptions.DBusException) as e:
251
229
                pass
252
230
            self.group = None
253
231
        self.remove()
287
265
                                 self.server_state_changed)
288
266
        self.server_state_changed(self.server.GetState())
289
267
 
290
 
class AvahiServiceToSyslog(AvahiService):
291
 
    def rename(self):
292
 
        """Add the new name to the syslog messages"""
293
 
        ret = AvahiService.rename(self)
294
 
        syslogger.setFormatter(logging.Formatter
295
 
                               ('Mandos (%s) [%%(process)d]:'
296
 
                                ' %%(levelname)s: %%(message)s'
297
 
                                % self.name))
298
 
        return ret
299
268
 
300
269
def _timedelta_to_milliseconds(td):
301
270
    "Convert a datetime.timedelta() to milliseconds"
320
289
                     instance %(name)s can be used in the command.
321
290
    checker_initiator_tag: a gobject event source tag, or None
322
291
    created:    datetime.datetime(); (UTC) object creation
323
 
    client_structure: Object describing what attributes a client has
324
 
                      and is used for storing the client at exit
325
292
    current_checker_command: string; current running checker_command
 
293
    disable_hook:  If set, called by disable() as disable_hook(self)
326
294
    disable_initiator_tag: a gobject event source tag, or None
327
295
    enabled:    bool()
328
296
    fingerprint: string (40 or 32 hexadecimal digits); used to
331
299
    interval:   datetime.timedelta(); How often to start a new checker
332
300
    last_approval_request: datetime.datetime(); (UTC) or None
333
301
    last_checked_ok: datetime.datetime(); (UTC) or None
334
 
    last_checker_status: integer between 0 and 255 reflecting exit status
335
 
                         of last checker. -1 reflect crashed checker,
336
 
                         or None.
337
302
    last_enabled: datetime.datetime(); (UTC)
338
303
    name:       string; from the config file, used in log messages and
339
304
                        D-Bus identifiers
366
331
    def approval_delay_milliseconds(self):
367
332
        return _timedelta_to_milliseconds(self.approval_delay)
368
333
    
369
 
    def __init__(self, name = None, config=None):
 
334
    def __init__(self, name = None, disable_hook=None, config=None):
370
335
        """Note: the 'checker' key in 'config' sets the
371
336
        'checker_command' attribute and *not* the 'checker'
372
337
        attribute."""
392
357
                            % self.name)
393
358
        self.host = config.get("host", "")
394
359
        self.created = datetime.datetime.utcnow()
395
 
        self.enabled = True
 
360
        self.enabled = False
396
361
        self.last_approval_request = None
397
 
        self.last_enabled = datetime.datetime.utcnow()
 
362
        self.last_enabled = None
398
363
        self.last_checked_ok = None
399
 
        self.last_checker_status = None
400
364
        self.timeout = string_to_delta(config["timeout"])
401
365
        self.extended_timeout = string_to_delta(config
402
366
                                                ["extended_timeout"])
403
367
        self.interval = string_to_delta(config["interval"])
 
368
        self.disable_hook = disable_hook
404
369
        self.checker = None
405
370
        self.checker_initiator_tag = None
406
371
        self.disable_initiator_tag = None
407
 
        self.expires = datetime.datetime.utcnow() + self.timeout
 
372
        self.expires = None
408
373
        self.checker_callback_tag = None
409
374
        self.checker_command = config["checker"]
410
375
        self.current_checker_command = None
 
376
        self.last_connect = None
411
377
        self._approved = None
412
378
        self.approved_by_default = config.get("approved_by_default",
413
379
                                              True)
419
385
        self.changedstate = (multiprocessing_manager
420
386
                             .Condition(multiprocessing_manager
421
387
                                        .Lock()))
422
 
        self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
423
 
        self.client_structure.append("client_structure")
424
 
 
425
 
 
426
 
        for name, t in inspect.getmembers(type(self),
427
 
                                          lambda obj: isinstance(obj, property)):
428
 
            if not name.startswith("_"):
429
 
                self.client_structure.append(name)
430
388
    
431
 
    # Send notice to process children that client state has changed
432
389
    def send_changedstate(self):
433
 
        with self.changedstate:
434
 
            self.changedstate.notify_all()
 
390
        self.changedstate.acquire()
 
391
        self.changedstate.notify_all()
 
392
        self.changedstate.release()
435
393
    
436
394
    def enable(self):
437
395
        """Start this client's checker and timeout hooks"""
439
397
            # Already enabled
440
398
            return
441
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
442
406
        self.expires = datetime.datetime.utcnow() + self.timeout
 
407
        self.disable_initiator_tag = (gobject.timeout_add
 
408
                                   (self.timeout_milliseconds(),
 
409
                                    self.disable))
443
410
        self.enabled = True
444
411
        self.last_enabled = datetime.datetime.utcnow()
445
 
        self.init_checker()
 
412
        # Also start a new checker *right now*.
 
413
        self.start_checker()
446
414
    
447
415
    def disable(self, quiet=True):
448
416
        """Disable this client."""
460
428
            gobject.source_remove(self.checker_initiator_tag)
461
429
            self.checker_initiator_tag = None
462
430
        self.stop_checker()
 
431
        if self.disable_hook:
 
432
            self.disable_hook(self)
463
433
        self.enabled = False
464
434
        # Do not run this again if called by a gobject.timeout_add
465
435
        return False
466
436
    
467
437
    def __del__(self):
 
438
        self.disable_hook = None
468
439
        self.disable()
469
 
 
470
 
    def init_checker(self):
471
 
        # Schedule a new checker to be started an 'interval' from now,
472
 
        # and every interval from then on.
473
 
        self.checker_initiator_tag = (gobject.timeout_add
474
 
                                      (self.interval_milliseconds(),
475
 
                                       self.start_checker))
476
 
        # Schedule a disable() when 'timeout' has passed
477
 
        self.disable_initiator_tag = (gobject.timeout_add
478
 
                                   (self.timeout_milliseconds(),
479
 
                                    self.disable))
480
 
        # Also start a new checker *right now*.
481
 
        self.start_checker()
482
 
 
483
 
        
 
440
    
484
441
    def checker_callback(self, pid, condition, command):
485
442
        """The checker has completed, so take appropriate actions."""
486
443
        self.checker_callback_tag = None
487
444
        self.checker = None
488
445
        if os.WIFEXITED(condition):
489
 
            self.last_checker_status =  os.WEXITSTATUS(condition)
490
 
            if self.last_checker_status == 0:
 
446
            exitstatus = os.WEXITSTATUS(condition)
 
447
            if exitstatus == 0:
491
448
                logger.info("Checker for %(name)s succeeded",
492
449
                            vars(self))
493
450
                self.checked_ok()
495
452
                logger.info("Checker for %(name)s failed",
496
453
                            vars(self))
497
454
        else:
498
 
            self.last_checker_status = -1
499
455
            logger.warning("Checker for %(name)s crashed?",
500
456
                           vars(self))
501
457
    
612
568
                raise
613
569
        self.checker = None
614
570
 
615
 
    # Encrypts a client secret and stores it in a varible encrypted_secret
616
 
    def encrypt_secret(self, key):
617
 
        # Encryption-key need to be of a specific size, so we hash inputed key
618
 
        hasheng = hashlib.sha256()
619
 
        hasheng.update(key)
620
 
        encryptionkey = hasheng.digest()
621
 
 
622
 
        # Create validation hash so we know at decryption if it was sucessful
623
 
        hasheng = hashlib.sha256()
624
 
        hasheng.update(self.secret)
625
 
        validationhash = hasheng.digest()
626
 
 
627
 
        # Encrypt secret
628
 
        iv = os.urandom(Crypto.Cipher.AES.block_size)
629
 
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
630
 
                                        Crypto.Cipher.AES.MODE_CFB, iv)
631
 
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
632
 
        self.encrypted_secret = (ciphertext, iv)
633
 
 
634
 
    # Decrypt a encrypted client secret
635
 
    def decrypt_secret(self, key):
636
 
        # Decryption-key need to be of a specific size, so we hash inputed key
637
 
        hasheng = hashlib.sha256()
638
 
        hasheng.update(key)
639
 
        encryptionkey = hasheng.digest()
640
 
 
641
 
        # Decrypt encrypted secret
642
 
        ciphertext, iv = self.encrypted_secret
643
 
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
644
 
                                        Crypto.Cipher.AES.MODE_CFB, iv)
645
 
        plain = ciphereng.decrypt(ciphertext)
646
 
 
647
 
        # Validate decrypted secret to know if it was succesful
648
 
        hasheng = hashlib.sha256()
649
 
        validationhash = plain[:hasheng.digest_size]
650
 
        secret = plain[hasheng.digest_size:]
651
 
        hasheng.update(secret)
652
 
 
653
 
        # if validation fails, we use key as new secret. Otherwhise, we use
654
 
        # the decrypted secret
655
 
        if hasheng.digest() == validationhash:
656
 
            self.secret = secret
657
 
        else:
658
 
            self.secret = key
659
 
        del self.encrypted_secret
660
 
 
661
571
 
662
572
def dbus_service_property(dbus_interface, signature="v",
663
573
                          access="readwrite", byte_arrays=False):
781
691
        
782
692
        Note: Will not include properties with access="write".
783
693
        """
784
 
        properties = {}
 
694
        all = {}
785
695
        for name, prop in self._get_all_dbus_properties():
786
696
            if (interface_name
787
697
                and interface_name != prop._dbus_interface):
792
702
                continue
793
703
            value = prop()
794
704
            if not hasattr(value, "variant_level"):
795
 
                properties[name] = value
 
705
                all[name] = value
796
706
                continue
797
 
            properties[name] = type(value)(value, variant_level=
798
 
                                           value.variant_level+1)
799
 
        return dbus.Dictionary(properties, signature="sv")
 
707
            all[name] = type(value)(value, variant_level=
 
708
                                    value.variant_level+1)
 
709
        return dbus.Dictionary(all, signature="sv")
800
710
    
801
711
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
802
712
                         out_signature="s",
964
874
    # dbus.service.Object doesn't use super(), so we can't either.
965
875
    
966
876
    def __init__(self, bus = None, *args, **kwargs):
 
877
        self._approvals_pending = 0
967
878
        self.bus = bus
968
879
        Client.__init__(self, *args, **kwargs)
969
 
 
970
 
        self._approvals_pending = 0
971
880
        # Only now, when this client is initialized, can it show up on
972
881
        # the D-Bus
973
882
        client_object_name = unicode(self.name).translate(
1143
1052
        "D-Bus signal"
1144
1053
        return self.need_approval()
1145
1054
    
1146
 
    # NeRwequest - signal
1147
 
    @dbus.service.signal(_interface, signature="s")
1148
 
    def NewRequest(self, ip):
1149
 
        """D-Bus signal
1150
 
        Is sent after a client request a password.
1151
 
        """
1152
 
        pass
1153
 
 
1154
1055
    ## Methods
1155
1056
    
1156
1057
    # Approve - method
1454
1355
                    logger.warning("Bad certificate: %s", error)
1455
1356
                    return
1456
1357
                logger.debug("Fingerprint: %s", fpr)
1457
 
                if self.server.use_dbus:
1458
 
                    # Emit D-Bus signal
1459
 
                    client.NewRequest(str(self.client_address))
1460
1358
                
1461
1359
                try:
1462
1360
                    client = ProxyClient(child_pipe, fpr,
1726
1624
        self.enabled = False
1727
1625
        self.clients = clients
1728
1626
        if self.clients is None:
1729
 
            self.clients = {}
 
1627
            self.clients = set()
1730
1628
        self.use_dbus = use_dbus
1731
1629
        self.gnutls_priority = gnutls_priority
1732
1630
        IPv6_TCPServer.__init__(self, server_address,
1779
1677
            fpr = request[1]
1780
1678
            address = request[2]
1781
1679
            
1782
 
            for c in self.clients.itervalues():
 
1680
            for c in self.clients:
1783
1681
                if c.fingerprint == fpr:
1784
1682
                    client = c
1785
1683
                    break
1869
1767
    return timevalue
1870
1768
 
1871
1769
 
 
1770
def if_nametoindex(interface):
 
1771
    """Call the C function if_nametoindex(), or equivalent
 
1772
    
 
1773
    Note: This function cannot accept a unicode string."""
 
1774
    global if_nametoindex
 
1775
    try:
 
1776
        if_nametoindex = (ctypes.cdll.LoadLibrary
 
1777
                          (ctypes.util.find_library("c"))
 
1778
                          .if_nametoindex)
 
1779
    except (OSError, AttributeError):
 
1780
        logger.warning("Doing if_nametoindex the hard way")
 
1781
        def if_nametoindex(interface):
 
1782
            "Get an interface index the hard way, i.e. using fcntl()"
 
1783
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
 
1784
            with contextlib.closing(socket.socket()) as s:
 
1785
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
1786
                                    struct.pack(str("16s16x"),
 
1787
                                                interface))
 
1788
            interface_index = struct.unpack(str("I"),
 
1789
                                            ifreq[16:20])[0]
 
1790
            return interface_index
 
1791
    return if_nametoindex(interface)
 
1792
 
 
1793
 
1872
1794
def daemon(nochdir = False, noclose = False):
1873
1795
    """See daemon(3).  Standard BSD Unix function.
1874
1796
    
1929
1851
                        " system bus interface")
1930
1852
    parser.add_argument("--no-ipv6", action="store_false",
1931
1853
                        dest="use_ipv6", help="Do not use IPv6")
1932
 
    parser.add_argument("--no-restore", action="store_false",
1933
 
                        dest="restore", help="Do not restore stored state",
1934
 
                        default=True)
1935
 
 
1936
1854
    options = parser.parse_args()
1937
1855
    
1938
1856
    if options.check:
1973
1891
    # options, if set.
1974
1892
    for option in ("interface", "address", "port", "debug",
1975
1893
                   "priority", "servicename", "configdir",
1976
 
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
 
1894
                   "use_dbus", "use_ipv6", "debuglevel"):
1977
1895
        value = getattr(options, option)
1978
1896
        if value is not None:
1979
1897
            server_settings[option] = value
1992
1910
    use_dbus = server_settings["use_dbus"]
1993
1911
    use_ipv6 = server_settings["use_ipv6"]
1994
1912
    
1995
 
    if debug:
1996
 
        initlogger(logging.DEBUG)
1997
 
    else:
1998
 
        if not debuglevel:
1999
 
            initlogger()
2000
 
        else:
2001
 
            level = getattr(logging, debuglevel.upper())
2002
 
            initlogger(level)    
2003
 
    
2004
1913
    if server_settings["servicename"] != "Mandos":
2005
1914
        syslogger.setFormatter(logging.Formatter
2006
1915
                               ('Mandos (%s) [%%(process)d]:'
2060
1969
        if error[0] != errno.EPERM:
2061
1970
            raise error
2062
1971
    
 
1972
    if not debug and not debuglevel:
 
1973
        syslogger.setLevel(logging.WARNING)
 
1974
        console.setLevel(logging.WARNING)
 
1975
    if debuglevel:
 
1976
        level = getattr(logging, debuglevel.upper())
 
1977
        syslogger.setLevel(level)
 
1978
        console.setLevel(level)
 
1979
    
2063
1980
    if debug:
2064
1981
        # Enable all possible GnuTLS debugging
2065
1982
        
2107
2024
            server_settings["use_dbus"] = False
2108
2025
            tcp_server.use_dbus = False
2109
2026
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2110
 
    service = AvahiServiceToSyslog(name =
2111
 
                                   server_settings["servicename"],
2112
 
                                   servicetype = "_mandos._tcp",
2113
 
                                   protocol = protocol, bus = bus)
 
2027
    service = AvahiService(name = server_settings["servicename"],
 
2028
                           servicetype = "_mandos._tcp",
 
2029
                           protocol = protocol, bus = bus)
2114
2030
    if server_settings["interface"]:
2115
2031
        service.interface = (if_nametoindex
2116
2032
                             (str(server_settings["interface"])))
2122
2038
    if use_dbus:
2123
2039
        client_class = functools.partial(ClientDBusTransitional,
2124
2040
                                         bus = bus)
2125
 
    
2126
 
    special_settings = {
2127
 
        # Some settings need to be accessd by special methods;
2128
 
        # booleans need .getboolean(), etc.  Here is a list of them:
2129
 
        "approved_by_default":
2130
 
            lambda section:
2131
 
            client_config.getboolean(section, "approved_by_default"),
2132
 
        }
2133
 
    # Construct a new dict of client settings of this form:
2134
 
    # { client_name: {setting_name: value, ...}, ...}
2135
 
    # with exceptions for any special settings as defined above
2136
 
    client_settings = dict((clientname,
2137
 
                           dict((setting,
2138
 
                                 (value if setting not in special_settings
2139
 
                                  else special_settings[setting](clientname)))
2140
 
                                for setting, value in client_config.items(clientname)))
2141
 
                          for clientname in client_config.sections())
2142
 
    
2143
 
    old_client_settings = {}
2144
 
    clients_data = []
2145
 
 
2146
 
    # Get client data and settings from last running state. 
2147
 
    if server_settings["restore"]:
2148
 
        try:
2149
 
            with open(stored_state_path, "rb") as stored_state:
2150
 
                clients_data, old_client_settings = pickle.load(stored_state)
2151
 
            os.remove(stored_state_path)
2152
 
        except IOError as e:
2153
 
            logger.warning("Could not load persistant state: {0}".format(e))
2154
 
            if e.errno != errno.ENOENT:
2155
 
                raise
2156
 
 
2157
 
    for client in clients_data:
2158
 
        client_name = client["name"]
2159
 
        
2160
 
        # Decide which value to use after restoring saved state.
2161
 
        # We have three different values: Old config file,
2162
 
        # new config file, and saved state.
2163
 
        # New config value takes precedence if it differs from old
2164
 
        # config value, otherwise use saved state.
2165
 
        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):
2166
2048
            try:
2167
 
                # For each value in new config, check if it differs
2168
 
                # from the old config value (Except for the "secret"
2169
 
                # attribute)
2170
 
                if name != "secret" and value != old_client_settings[client_name][name]:
2171
 
                    setattr(client, name, value)
 
2049
                yield (name, special_settings[name]())
2172
2050
            except KeyError:
2173
 
                pass
2174
 
 
2175
 
        # Clients who has passed its expire date, can still be enabled if its
2176
 
        # last checker was sucessful. Clients who checkers failed before we
2177
 
        # stored it state is asumed to had failed checker during downtime.
2178
 
        if client["enabled"] and client["last_checked_ok"]:
2179
 
            if ((datetime.datetime.utcnow() - client["last_checked_ok"])
2180
 
                > client["interval"]):
2181
 
                if client["last_checker_status"] != 0:
2182
 
                    client["enabled"] = False
2183
 
                else:
2184
 
                    client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2185
 
 
2186
 
        client["changedstate"] = (multiprocessing_manager
2187
 
                                  .Condition(multiprocessing_manager
2188
 
                                             .Lock()))
2189
 
        if use_dbus:
2190
 
            new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
2191
 
            tcp_server.clients[client_name] = new_client
2192
 
            new_client.bus = bus
2193
 
            for name, value in client.iteritems():
2194
 
                setattr(new_client, name, value)
2195
 
            client_object_name = unicode(client_name).translate(
2196
 
                {ord("."): ord("_"),
2197
 
                 ord("-"): ord("_")})
2198
 
            new_client.dbus_object_path = (dbus.ObjectPath
2199
 
                                     ("/clients/" + client_object_name))
2200
 
            DBusObjectWithProperties.__init__(new_client,
2201
 
                                              new_client.bus,
2202
 
                                              new_client.dbus_object_path)
2203
 
        else:
2204
 
            tcp_server.clients[client_name] = Client.__new__(Client)
2205
 
            for name, value in client.iteritems():
2206
 
                setattr(tcp_server.clients[client_name], name, value)
2207
 
                
2208
 
        tcp_server.clients[client_name].decrypt_secret(
2209
 
            client_settings[client_name]["secret"])            
2210
 
        
2211
 
    # Create/remove clients based on new changes made to config
2212
 
    for clientname in set(old_client_settings) - set(client_settings):
2213
 
        del tcp_server.clients[clientname]
2214
 
    for clientname in set(client_settings) - set(old_client_settings):
2215
 
        tcp_server.clients[clientname] = (client_class(name = clientname,
2216
 
                                                       config =
2217
 
                                                       client_settings
2218
 
                                                       [clientname]))
 
2051
                yield (name, value)
2219
2052
    
2220
 
 
 
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()))
2221
2058
    if not tcp_server.clients:
2222
2059
        logger.warning("No clients defined")
2223
2060
        
2266
2103
            def GetAllClients(self):
2267
2104
                "D-Bus method"
2268
2105
                return dbus.Array(c.dbus_object_path
2269
 
                                  for c in
2270
 
                                  tcp_server.clients.itervalues())
 
2106
                                  for c in tcp_server.clients)
2271
2107
            
2272
2108
            @dbus.service.method(_interface,
2273
2109
                                 out_signature="a{oa{sv}}")
2275
2111
                "D-Bus method"
2276
2112
                return dbus.Dictionary(
2277
2113
                    ((c.dbus_object_path, c.GetAll(""))
2278
 
                     for c in tcp_server.clients.itervalues()),
 
2114
                     for c in tcp_server.clients),
2279
2115
                    signature="oa{sv}")
2280
2116
            
2281
2117
            @dbus.service.method(_interface, in_signature="o")
2282
2118
            def RemoveClient(self, object_path):
2283
2119
                "D-Bus method"
2284
 
                for c in tcp_server.clients.itervalues():
 
2120
                for c in tcp_server.clients:
2285
2121
                    if c.dbus_object_path == object_path:
2286
 
                        del tcp_server.clients[c.name]
 
2122
                        tcp_server.clients.remove(c)
2287
2123
                        c.remove_from_connection()
2288
2124
                        # Don't signal anything except ClientRemoved
2289
2125
                        c.disable(quiet=True)
2303
2139
        service.cleanup()
2304
2140
        
2305
2141
        multiprocessing.active_children()
2306
 
        if not (tcp_server.clients or client_settings):
2307
 
            return
2308
 
 
2309
 
        # Store client before exiting. Secrets are encrypted with key based
2310
 
        # on what config file has. If config file is removed/edited, old
2311
 
        # secret will thus be unrecovable.
2312
 
        clients = []
2313
 
        for client in tcp_server.clients.itervalues():
2314
 
            client.encrypt_secret(client_settings[client.name]["secret"])
2315
 
 
2316
 
            client_dict = {}
2317
 
 
2318
 
            # A list of attributes that will not be stored when shuting down.
2319
 
            exclude = set(("bus", "changedstate", "secret"))            
2320
 
            for name, typ in inspect.getmembers(dbus.service.Object):
2321
 
                exclude.add(name)
2322
 
                
2323
 
            client_dict["encrypted_secret"] = client.encrypted_secret
2324
 
            for attr in client.client_structure:
2325
 
                if attr not in exclude:
2326
 
                    client_dict[attr] = getattr(client, attr)
2327
 
 
2328
 
            clients.append(client_dict) 
2329
 
            del client_settings[client.name]["secret"]
2330
 
            
2331
 
        try:
2332
 
            with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
2333
 
                pickle.dump((clients, client_settings), stored_state)
2334
 
        except IOError as e:
2335
 
            logger.warning("Could not save persistant state: {0}".format(e))
2336
 
            if e.errno != errno.ENOENT:
2337
 
                raise
2338
 
 
2339
 
        # Delete all clients, and settings from config
2340
2142
        while tcp_server.clients:
2341
 
            name, client = tcp_server.clients.popitem()
 
2143
            client = tcp_server.clients.pop()
2342
2144
            if use_dbus:
2343
2145
                client.remove_from_connection()
 
2146
            client.disable_hook = None
2344
2147
            # Don't signal anything except ClientRemoved
2345
2148
            client.disable(quiet=True)
2346
2149
            if use_dbus:
2348
2151
                mandos_dbus_service.ClientRemoved(client
2349
2152
                                                  .dbus_object_path,
2350
2153
                                                  client.name)
2351
 
        client_settings.clear()
2352
2154
    
2353
2155
    atexit.register(cleanup)
2354
2156
    
2355
 
    for client in tcp_server.clients.itervalues():
 
2157
    for client in tcp_server.clients:
2356
2158
        if use_dbus:
2357
2159
            # Emit D-Bus signal
2358
2160
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2359
 
        # Need to initiate checking of clients
2360
 
        if client.enabled:
2361
 
            client.init_checker()
2362
 
 
 
2161
        client.enable()
2363
2162
    
2364
2163
    tcp_server.enable()
2365
2164
    tcp_server.server_activate()