/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

  • Committer: Björn Påhlsson
  • Date: 2011-10-02 19:18:24 UTC
  • mto: (237.7.53 trunk)
  • mto: This revision was merged to the branch mainline in revision 286.
  • Revision ID: belorn@fukt.bsnet.se-20111002191824-eweh4pvneeg3qzia
transitional stuff actually working
documented change to D-Bus API

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
# along with this program.  If not, see
29
29
# <http://www.gnu.org/licenses/>.
30
30
31
 
# Contact the authors at <mandos@recompile.se>.
 
31
# Contact the authors at <mandos@fukt.bsnet.se>.
32
32
33
33
 
34
34
from __future__ import (division, absolute_import, print_function,
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
85
83
        SO_BINDTODEVICE = None
86
84
 
87
85
 
88
 
version = "1.4.1"
89
 
 
90
 
logger = logging.getLogger()
91
 
stored_state_path = "/var/lib/mandos/clients.pickle"
92
 
 
 
86
version = "1.3.1"
 
87
 
 
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
165
160
                            " after %i retries, exiting.",
166
161
                            self.rename_count)
167
162
            raise AvahiServiceError("Too many renames")
168
 
        self.name = unicode(self.server
169
 
                            .GetAlternativeServiceName(self.name))
 
163
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
170
164
        logger.info("Changing Zeroconf service name to %r ...",
171
165
                    self.name)
 
166
        syslogger.setFormatter(logging.Formatter
 
167
                               ('Mandos (%s) [%%(process)d]:'
 
168
                                ' %%(levelname)s: %%(message)s'
 
169
                                % self.name))
172
170
        self.remove()
173
171
        try:
174
172
            self.add()
194
192
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
195
193
        self.entry_group_state_changed_match = (
196
194
            self.group.connect_to_signal(
197
 
                'StateChanged', self.entry_group_state_changed))
 
195
                'StateChanged', self .entry_group_state_changed))
198
196
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
199
197
                     self.name, self.type)
200
198
        self.group.AddService(
266
264
                                 self.server_state_changed)
267
265
        self.server_state_changed(self.server.GetState())
268
266
 
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
267
 
279
268
def _timedelta_to_milliseconds(td):
280
269
    "Convert a datetime.timedelta() to milliseconds"
299
288
                     instance %(name)s can be used in the command.
300
289
    checker_initiator_tag: a gobject event source tag, or None
301
290
    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
291
    current_checker_command: string; current running checker_command
 
292
    disable_hook:  If set, called by disable() as disable_hook(self)
305
293
    disable_initiator_tag: a gobject event source tag, or None
306
294
    enabled:    bool()
307
295
    fingerprint: string (40 or 32 hexadecimal digits); used to
310
298
    interval:   datetime.timedelta(); How often to start a new checker
311
299
    last_approval_request: datetime.datetime(); (UTC) or None
312
300
    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
301
    last_enabled: datetime.datetime(); (UTC)
317
302
    name:       string; from the config file, used in log messages and
318
303
                        D-Bus identifiers
336
321
    
337
322
    def extended_timeout_milliseconds(self):
338
323
        "Return the 'extended_timeout' attribute in milliseconds"
339
 
        return _timedelta_to_milliseconds(self.extended_timeout)
 
324
        return _timedelta_to_milliseconds(self.extended_timeout)    
340
325
    
341
326
    def interval_milliseconds(self):
342
327
        "Return the 'interval' attribute in milliseconds"
345
330
    def approval_delay_milliseconds(self):
346
331
        return _timedelta_to_milliseconds(self.approval_delay)
347
332
    
348
 
    def __init__(self, name = None, config=None):
 
333
    def __init__(self, name = None, disable_hook=None, config=None):
349
334
        """Note: the 'checker' key in 'config' sets the
350
335
        'checker_command' attribute and *not* the 'checker'
351
336
        attribute."""
371
356
                            % self.name)
372
357
        self.host = config.get("host", "")
373
358
        self.created = datetime.datetime.utcnow()
374
 
        self.enabled = True
 
359
        self.enabled = False
375
360
        self.last_approval_request = None
376
 
        self.last_enabled = datetime.datetime.utcnow()
 
361
        self.last_enabled = None
377
362
        self.last_checked_ok = None
378
 
        self.last_checker_status = None
379
363
        self.timeout = string_to_delta(config["timeout"])
380
 
        self.extended_timeout = string_to_delta(config
381
 
                                                ["extended_timeout"])
 
364
        self.extended_timeout = string_to_delta(config["extended_timeout"])
382
365
        self.interval = string_to_delta(config["interval"])
 
366
        self.disable_hook = disable_hook
383
367
        self.checker = None
384
368
        self.checker_initiator_tag = None
385
369
        self.disable_initiator_tag = None
386
 
        self.expires = datetime.datetime.utcnow() + self.timeout
 
370
        self.expires = None
387
371
        self.checker_callback_tag = None
388
372
        self.checker_command = config["checker"]
389
373
        self.current_checker_command = None
 
374
        self.last_connect = None
390
375
        self._approved = None
391
376
        self.approved_by_default = config.get("approved_by_default",
392
377
                                              True)
395
380
            config["approval_delay"])
396
381
        self.approval_duration = string_to_delta(
397
382
            config["approval_duration"])
398
 
        self.changedstate = (multiprocessing_manager
399
 
                             .Condition(multiprocessing_manager
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)
 
383
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
409
384
    
410
 
    # Send notice to process children that client state has changed
411
385
    def send_changedstate(self):
412
 
        with self.changedstate:
413
 
            self.changedstate.notify_all()
414
 
    
 
386
        self.changedstate.acquire()
 
387
        self.changedstate.notify_all()
 
388
        self.changedstate.release()
 
389
        
415
390
    def enable(self):
416
391
        """Start this client's checker and timeout hooks"""
417
392
        if getattr(self, "enabled", False):
418
393
            # Already enabled
419
394
            return
420
395
        self.send_changedstate()
 
396
        # Schedule a new checker to be started an 'interval' from now,
 
397
        # and every interval from then on.
 
398
        self.checker_initiator_tag = (gobject.timeout_add
 
399
                                      (self.interval_milliseconds(),
 
400
                                       self.start_checker))
 
401
        # Schedule a disable() when 'timeout' has passed
421
402
        self.expires = datetime.datetime.utcnow() + self.timeout
 
403
        self.disable_initiator_tag = (gobject.timeout_add
 
404
                                   (self.timeout_milliseconds(),
 
405
                                    self.disable))
422
406
        self.enabled = True
423
407
        self.last_enabled = datetime.datetime.utcnow()
424
 
        self.init_checker()
 
408
        # Also start a new checker *right now*.
 
409
        self.start_checker()
425
410
    
426
411
    def disable(self, quiet=True):
427
412
        """Disable this client."""
439
424
            gobject.source_remove(self.checker_initiator_tag)
440
425
            self.checker_initiator_tag = None
441
426
        self.stop_checker()
 
427
        if self.disable_hook:
 
428
            self.disable_hook(self)
442
429
        self.enabled = False
443
430
        # Do not run this again if called by a gobject.timeout_add
444
431
        return False
445
432
    
446
433
    def __del__(self):
 
434
        self.disable_hook = None
447
435
        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
 
        
 
436
    
463
437
    def checker_callback(self, pid, condition, command):
464
438
        """The checker has completed, so take appropriate actions."""
465
439
        self.checker_callback_tag = None
466
440
        self.checker = None
467
441
        if os.WIFEXITED(condition):
468
 
            self.last_checker_status =  os.WEXITSTATUS(condition)
469
 
            if self.last_checker_status == 0:
 
442
            exitstatus = os.WEXITSTATUS(condition)
 
443
            if exitstatus == 0:
470
444
                logger.info("Checker for %(name)s succeeded",
471
445
                            vars(self))
472
446
                self.checked_ok()
474
448
                logger.info("Checker for %(name)s failed",
475
449
                            vars(self))
476
450
        else:
477
 
            self.last_checker_status = -1
478
451
            logger.warning("Checker for %(name)s crashed?",
479
452
                           vars(self))
480
453
    
487
460
        if timeout is None:
488
461
            timeout = self.timeout
489
462
        self.last_checked_ok = datetime.datetime.utcnow()
490
 
        if self.disable_initiator_tag is not None:
491
 
            gobject.source_remove(self.disable_initiator_tag)
492
 
        if getattr(self, "enabled", False):
493
 
            self.disable_initiator_tag = (gobject.timeout_add
494
 
                                          (_timedelta_to_milliseconds
495
 
                                           (timeout), self.disable))
496
 
            self.expires = datetime.datetime.utcnow() + timeout
 
463
        gobject.source_remove(self.disable_initiator_tag)
 
464
        self.expires = datetime.datetime.utcnow() + timeout
 
465
        self.disable_initiator_tag = (gobject.timeout_add
 
466
                                      (_timedelta_to_milliseconds(timeout),
 
467
                                       self.disable))
497
468
    
498
469
    def need_approval(self):
499
470
        self.last_approval_request = datetime.datetime.utcnow()
591
562
                raise
592
563
        self.checker = None
593
564
 
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
565
 
641
566
def dbus_service_property(dbus_interface, signature="v",
642
567
                          access="readwrite", byte_arrays=False):
704
629
        """
705
630
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
706
631
                for cls in self.__class__.__mro__
707
 
                for name, prop in
708
 
                inspect.getmembers(cls, self._is_dbus_property))
 
632
                for name, prop in inspect.getmembers(cls, self._is_dbus_property))
709
633
    
710
634
    def _get_dbus_property(self, interface_name, property_name):
711
635
        """Returns a bound method if one exists which is a D-Bus
712
636
        property with the specified name and interface.
713
637
        """
714
638
        for cls in  self.__class__.__mro__:
715
 
            for name, value in (inspect.getmembers
716
 
                                (cls, self._is_dbus_property)):
717
 
                if (value._dbus_name == property_name
718
 
                    and value._dbus_interface == interface_name):
 
639
            for name, value in inspect.getmembers(cls, self._is_dbus_property):
 
640
                if value._dbus_name == property_name and value._dbus_interface == interface_name:
719
641
                    return value.__get__(self)
720
642
        
721
643
        # No such property
722
644
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
723
645
                                   + interface_name + "."
724
646
                                   + property_name)
 
647
 
725
648
    
726
649
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
727
650
                         out_signature="v")
832
755
    return dbus.String(dt.isoformat(),
833
756
                       variant_level=variant_level)
834
757
 
835
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
836
 
                                  .__metaclass__):
837
 
    """Applied to an empty subclass of a D-Bus object, this metaclass
838
 
    will add additional D-Bus attributes matching a certain pattern.
839
 
    """
 
758
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
840
759
    def __new__(mcs, name, bases, attr):
841
 
        # Go through all the base classes which could have D-Bus
842
 
        # methods, signals, or properties in them
843
 
        for base in (b for b in bases
844
 
                     if issubclass(b, dbus.service.Object)):
845
 
            # Go though all attributes of the base class
846
 
            for attrname, attribute in inspect.getmembers(base):
847
 
                # Ignore non-D-Bus attributes, and D-Bus attributes
848
 
                # with the wrong interface name
849
 
                if (not hasattr(attribute, "_dbus_interface")
850
 
                    or not attribute._dbus_interface
851
 
                    .startswith("se.recompile.Mandos")):
852
 
                    continue
853
 
                # Create an alternate D-Bus interface name based on
854
 
                # the current name
855
 
                alt_interface = (attribute._dbus_interface
856
 
                                 .replace("se.recompile.Mandos",
857
 
                                          "se.bsnet.fukt.Mandos"))
858
 
                # Is this a D-Bus signal?
859
 
                if getattr(attribute, "_dbus_is_signal", False):
860
 
                    # Extract the original non-method function by
861
 
                    # black magic
862
 
                    nonmethod_func = (dict(
863
 
                            zip(attribute.func_code.co_freevars,
864
 
                                attribute.__closure__))["func"]
865
 
                                      .cell_contents)
866
 
                    # Create a new, but exactly alike, function
867
 
                    # object, and decorate it to be a new D-Bus signal
868
 
                    # with the alternate D-Bus interface name
869
 
                    new_function = (dbus.service.signal
870
 
                                    (alt_interface,
871
 
                                     attribute._dbus_signature)
872
 
                                    (types.FunctionType(
873
 
                                nonmethod_func.func_code,
874
 
                                nonmethod_func.func_globals,
875
 
                                nonmethod_func.func_name,
876
 
                                nonmethod_func.func_defaults,
877
 
                                nonmethod_func.func_closure)))
878
 
                    # Define a creator of a function to call both the
879
 
                    # old and new functions, so both the old and new
880
 
                    # signals gets sent when the function is called
881
 
                    def fixscope(func1, func2):
882
 
                        """This function is a scope container to pass
883
 
                        func1 and func2 to the "call_both" function
884
 
                        outside of its arguments"""
885
 
                        def call_both(*args, **kwargs):
886
 
                            """This function will emit two D-Bus
887
 
                            signals by calling func1 and func2"""
888
 
                            func1(*args, **kwargs)
889
 
                            func2(*args, **kwargs)
890
 
                        return call_both
891
 
                    # Create the "call_both" function and add it to
892
 
                    # the class
893
 
                    attr[attrname] = fixscope(attribute,
894
 
                                              new_function)
895
 
                # Is this a D-Bus method?
896
 
                elif getattr(attribute, "_dbus_is_method", False):
897
 
                    # Create a new, but exactly alike, function
898
 
                    # object.  Decorate it to be a new D-Bus method
899
 
                    # with the alternate D-Bus interface name.  Add it
900
 
                    # to the class.
901
 
                    attr[attrname] = (dbus.service.method
902
 
                                      (alt_interface,
903
 
                                       attribute._dbus_in_signature,
904
 
                                       attribute._dbus_out_signature)
905
 
                                      (types.FunctionType
906
 
                                       (attribute.func_code,
907
 
                                        attribute.func_globals,
908
 
                                        attribute.func_name,
909
 
                                        attribute.func_defaults,
910
 
                                        attribute.func_closure)))
911
 
                # Is this a D-Bus property?
912
 
                elif getattr(attribute, "_dbus_is_property", False):
913
 
                    # Create a new, but exactly alike, function
914
 
                    # object, and decorate it to be a new D-Bus
915
 
                    # property with the alternate D-Bus interface
916
 
                    # name.  Add it to the class.
917
 
                    attr[attrname] = (dbus_service_property
918
 
                                      (alt_interface,
919
 
                                       attribute._dbus_signature,
920
 
                                       attribute._dbus_access,
921
 
                                       attribute
922
 
                                       ._dbus_get_args_options
923
 
                                       ["byte_arrays"])
924
 
                                      (types.FunctionType
925
 
                                       (attribute.func_code,
926
 
                                        attribute.func_globals,
927
 
                                        attribute.func_name,
928
 
                                        attribute.func_defaults,
929
 
                                        attribute.func_closure)))
 
760
        for attrname, old_dbusobj in inspect.getmembers(bases[0]):
 
761
            new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
 
762
            if (getattr(old_dbusobj, "_dbus_is_signal", False)
 
763
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
 
764
                unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
 
765
                                    old_dbusobj.__closure__))["func"].cell_contents
 
766
                newfunc = types.FunctionType(unwrappedfunc.func_code,
 
767
                                             unwrappedfunc.func_globals,
 
768
                                             unwrappedfunc.func_name,
 
769
                                             unwrappedfunc.func_defaults,
 
770
                                             unwrappedfunc.func_closure)
 
771
                new_dbusfunc = dbus.service.signal(
 
772
                    new_interface, old_dbusobj._dbus_signature)(newfunc)            
 
773
                attr["_transitional_" + attrname] = new_dbusfunc
 
774
 
 
775
                def fixscope(func1, func2):
 
776
                    def newcall(*args, **kwargs):
 
777
                        func1(*args, **kwargs)
 
778
                        func2(*args, **kwargs)
 
779
                    return newcall
 
780
 
 
781
                attr[attrname] = fixscope(old_dbusobj, new_dbusfunc)
 
782
            
 
783
            elif (getattr(old_dbusobj, "_dbus_is_method", False)
 
784
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
 
785
                new_dbusfunc = (dbus.service.method
 
786
                                (new_interface,
 
787
                                 old_dbusobj._dbus_in_signature,
 
788
                                 old_dbusobj._dbus_out_signature)
 
789
                                (types.FunctionType
 
790
                                 (old_dbusobj.func_code,
 
791
                                  old_dbusobj.func_globals,
 
792
                                  old_dbusobj.func_name,
 
793
                                  old_dbusobj.func_defaults,
 
794
                                  old_dbusobj.func_closure)))
 
795
 
 
796
                attr[attrname] = new_dbusfunc
 
797
            elif (getattr(old_dbusobj, "_dbus_is_property", False)
 
798
                  and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
 
799
                new_dbusfunc = (dbus_service_property
 
800
                                (new_interface,
 
801
                                 old_dbusobj._dbus_signature,
 
802
                                 old_dbusobj._dbus_access,
 
803
                                 old_dbusobj._dbus_get_args_options["byte_arrays"])
 
804
                                (types.FunctionType
 
805
                                 (old_dbusobj.func_code,
 
806
                                  old_dbusobj.func_globals,
 
807
                                  old_dbusobj.func_name,
 
808
                                  old_dbusobj.func_defaults,
 
809
                                  old_dbusobj.func_closure)))
 
810
 
 
811
                attr[attrname] = new_dbusfunc
930
812
        return type.__new__(mcs, name, bases, attr)
931
813
 
932
814
class ClientDBus(Client, DBusObjectWithProperties):
943
825
    # dbus.service.Object doesn't use super(), so we can't either.
944
826
    
945
827
    def __init__(self, bus = None, *args, **kwargs):
 
828
        self._approvals_pending = 0
946
829
        self.bus = bus
947
830
        Client.__init__(self, *args, **kwargs)
948
 
 
949
 
        self._approvals_pending = 0
950
831
        # Only now, when this client is initialized, can it show up on
951
832
        # the D-Bus
952
833
        client_object_name = unicode(self.name).translate(
960
841
    def notifychangeproperty(transform_func,
961
842
                             dbus_name, type_func=lambda x: x,
962
843
                             variant_level=1):
963
 
        """ Modify a variable so that it's a property which announces
964
 
        its changes to DBus.
965
 
 
966
 
        transform_fun: Function that takes a value and a variant_level
967
 
                       and transforms it to a D-Bus type.
968
 
        dbus_name: D-Bus name of the variable
 
844
        """ Modify a variable so that its a property that announce its
 
845
        changes to DBus.
 
846
        transform_fun: Function that takes a value and transform it to
 
847
                       DBus type.
 
848
        dbus_name: DBus name of the variable
969
849
        type_func: Function that transform the value before sending it
970
 
                   to the D-Bus.  Default: no transform
971
 
        variant_level: D-Bus variant level.  Default: 1
 
850
                   to DBus
 
851
        variant_level: DBus variant level. default: 1
972
852
        """
973
 
        attrname = "_{0}".format(dbus_name)
 
853
        real_value = [None,]
974
854
        def setter(self, value):
 
855
            old_value = real_value[0]
 
856
            real_value[0] = value
975
857
            if hasattr(self, "dbus_object_path"):
976
 
                if (not hasattr(self, attrname) or
977
 
                    type_func(getattr(self, attrname, None))
978
 
                    != type_func(value)):
979
 
                    dbus_value = transform_func(type_func(value),
980
 
                                                variant_level
981
 
                                                =variant_level)
 
858
                if type_func(old_value) != type_func(real_value[0]):
 
859
                    dbus_value = transform_func(type_func(real_value[0]),
 
860
                                                variant_level)
982
861
                    self.PropertyChanged(dbus.String(dbus_name),
983
862
                                         dbus_value)
984
 
            setattr(self, attrname, value)
985
863
        
986
 
        return property(lambda self: getattr(self, attrname), setter)
 
864
        return property(lambda self: real_value[0], setter)
987
865
    
988
866
    
989
867
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
994
872
    last_enabled = notifychangeproperty(datetime_to_dbus,
995
873
                                        "LastEnabled")
996
874
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
997
 
                                   type_func = lambda checker:
998
 
                                       checker is not None)
 
875
                                   type_func = lambda checker: checker is not None)
999
876
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1000
877
                                           "LastCheckedOK")
1001
 
    last_approval_request = notifychangeproperty(
1002
 
        datetime_to_dbus, "LastApprovalRequest")
 
878
    last_approval_request = notifychangeproperty(datetime_to_dbus,
 
879
                                                 "LastApprovalRequest")
1003
880
    approved_by_default = notifychangeproperty(dbus.Boolean,
1004
881
                                               "ApprovedByDefault")
1005
 
    approval_delay = notifychangeproperty(dbus.UInt16,
1006
 
                                          "ApprovalDelay",
1007
 
                                          type_func =
1008
 
                                          _timedelta_to_milliseconds)
1009
 
    approval_duration = notifychangeproperty(
1010
 
        dbus.UInt16, "ApprovalDuration",
1011
 
        type_func = _timedelta_to_milliseconds)
 
882
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
 
883
                                          type_func = _timedelta_to_milliseconds)
 
884
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
 
885
                                             type_func = _timedelta_to_milliseconds)
1012
886
    host = notifychangeproperty(dbus.String, "Host")
1013
887
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
1014
 
                                   type_func =
1015
 
                                   _timedelta_to_milliseconds)
1016
 
    extended_timeout = notifychangeproperty(
1017
 
        dbus.UInt16, "ExtendedTimeout",
1018
 
        type_func = _timedelta_to_milliseconds)
1019
 
    interval = notifychangeproperty(dbus.UInt16,
1020
 
                                    "Interval",
1021
 
                                    type_func =
1022
 
                                    _timedelta_to_milliseconds)
 
888
                                   type_func = _timedelta_to_milliseconds)
 
889
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
 
890
                                            type_func = _timedelta_to_milliseconds)
 
891
    interval = notifychangeproperty(dbus.UInt16, "Interval",
 
892
                                    type_func = _timedelta_to_milliseconds)
1023
893
    checker_command = notifychangeproperty(dbus.String, "Checker")
1024
894
    
1025
895
    del notifychangeproperty
1079
949
    
1080
950
    
1081
951
    ## D-Bus methods, signals & properties
1082
 
    _interface = "se.recompile.Mandos.Client"
1083
 
    
 
952
    _interface = "se.bsnet.fukt.Mandos.Client"
 
953
 
1084
954
    ## Signals
1085
955
    
1086
956
    # CheckerCompleted - signal
1260
1130
        gobject.source_remove(self.disable_initiator_tag)
1261
1131
        self.disable_initiator_tag = None
1262
1132
        self.expires = None
1263
 
        time_to_die = _timedelta_to_milliseconds((self
1264
 
                                                  .last_checked_ok
1265
 
                                                  + self.timeout)
1266
 
                                                 - datetime.datetime
1267
 
                                                 .utcnow())
 
1133
        time_to_die = (self.
 
1134
                       _timedelta_to_milliseconds((self
 
1135
                                                   .last_checked_ok
 
1136
                                                   + self.timeout)
 
1137
                                                  - datetime.datetime
 
1138
                                                  .utcnow()))
1268
1139
        if time_to_die <= 0:
1269
1140
            # The timeout has passed
1270
1141
            self.disable()
1271
1142
        else:
1272
1143
            self.expires = (datetime.datetime.utcnow()
1273
 
                            + datetime.timedelta(milliseconds =
1274
 
                                                 time_to_die))
 
1144
                            + datetime.timedelta(milliseconds = time_to_die))
1275
1145
            self.disable_initiator_tag = (gobject.timeout_add
1276
1146
                                          (time_to_die, self.disable))
1277
1147
    
1357
1227
        self._pipe.send(('setattr', name, value))
1358
1228
 
1359
1229
class ClientDBusTransitional(ClientDBus):
1360
 
    __metaclass__ = AlternateDBusNamesMetaclass
 
1230
    __metaclass__ = transitional_dbus_metaclass
1361
1231
 
1362
1232
class ClientHandler(socketserver.BaseRequestHandler, object):
1363
1233
    """A class to handle client connections.
1443
1313
                                       client.name)
1444
1314
                        if self.server.use_dbus:
1445
1315
                            # Emit D-Bus signal
1446
 
                            client.Rejected("Disabled")
 
1316
                            client.Rejected("Disabled")                    
1447
1317
                        return
1448
1318
                    
1449
1319
                    if client._approved or not client.approval_delay:
1466
1336
                        return
1467
1337
                    
1468
1338
                    #wait until timeout or approved
 
1339
                    #x = float(client._timedelta_to_milliseconds(delay))
1469
1340
                    time = datetime.datetime.now()
1470
1341
                    client.changedstate.acquire()
1471
 
                    (client.changedstate.wait
1472
 
                     (float(client._timedelta_to_milliseconds(delay)
1473
 
                            / 1000)))
 
1342
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1474
1343
                    client.changedstate.release()
1475
1344
                    time2 = datetime.datetime.now()
1476
1345
                    if (time2 - time) >= delay:
1500
1369
                    sent_size += sent
1501
1370
                
1502
1371
                logger.info("Sending secret to %s", client.name)
1503
 
                # bump the timeout using extended_timeout
 
1372
                # bump the timeout as if seen
1504
1373
                client.checked_ok(client.extended_timeout)
1505
1374
                if self.server.use_dbus:
1506
1375
                    # Emit D-Bus signal
1586
1455
        except:
1587
1456
            self.handle_error(request, address)
1588
1457
        self.close_request(request)
1589
 
    
 
1458
            
1590
1459
    def process_request(self, request, address):
1591
1460
        """Start a new process to process the request."""
1592
 
        proc = multiprocessing.Process(target = self.sub_process_main,
1593
 
                                       args = (request,
1594
 
                                               address))
1595
 
        proc.start()
1596
 
        return proc
 
1461
        multiprocessing.Process(target = self.sub_process_main,
 
1462
                                args = (request, address)).start()
1597
1463
 
1598
1464
 
1599
1465
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1605
1471
        """
1606
1472
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1607
1473
        
1608
 
        proc = MultiprocessingMixIn.process_request(self, request,
1609
 
                                                    client_address)
 
1474
        super(MultiprocessingMixInWithPipe,
 
1475
              self).process_request(request, client_address)
1610
1476
        self.child_pipe.close()
1611
 
        self.add_pipe(parent_pipe, proc)
 
1477
        self.add_pipe(parent_pipe)
1612
1478
    
1613
 
    def add_pipe(self, parent_pipe, proc):
 
1479
    def add_pipe(self, parent_pipe):
1614
1480
        """Dummy function; override as necessary"""
1615
1481
        raise NotImplementedError
1616
1482
 
1694
1560
        self.enabled = False
1695
1561
        self.clients = clients
1696
1562
        if self.clients is None:
1697
 
            self.clients = {}
 
1563
            self.clients = set()
1698
1564
        self.use_dbus = use_dbus
1699
1565
        self.gnutls_priority = gnutls_priority
1700
1566
        IPv6_TCPServer.__init__(self, server_address,
1704
1570
    def server_activate(self):
1705
1571
        if self.enabled:
1706
1572
            return socketserver.TCPServer.server_activate(self)
1707
 
    
1708
1573
    def enable(self):
1709
1574
        self.enabled = True
1710
 
    
1711
 
    def add_pipe(self, parent_pipe, proc):
 
1575
    def add_pipe(self, parent_pipe):
1712
1576
        # Call "handle_ipc" for both data and EOF events
1713
1577
        gobject.io_add_watch(parent_pipe.fileno(),
1714
1578
                             gobject.IO_IN | gobject.IO_HUP,
1715
1579
                             functools.partial(self.handle_ipc,
1716
 
                                               parent_pipe =
1717
 
                                               parent_pipe,
1718
 
                                               proc = proc))
1719
 
    
 
1580
                                               parent_pipe = parent_pipe))
 
1581
        
1720
1582
    def handle_ipc(self, source, condition, parent_pipe=None,
1721
 
                   proc = None, client_object=None):
 
1583
                   client_object=None):
1722
1584
        condition_names = {
1723
1585
            gobject.IO_IN: "IN",   # There is data to read.
1724
1586
            gobject.IO_OUT: "OUT", # Data can be written (without
1733
1595
                                       for cond, name in
1734
1596
                                       condition_names.iteritems()
1735
1597
                                       if cond & condition)
1736
 
        # error, or the other end of multiprocessing.Pipe has closed
 
1598
        # error or the other end of multiprocessing.Pipe has closed
1737
1599
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1738
 
            # Wait for other process to exit
1739
 
            proc.join()
1740
1600
            return False
1741
1601
        
1742
1602
        # Read a request from the child
1747
1607
            fpr = request[1]
1748
1608
            address = request[2]
1749
1609
            
1750
 
            for c in self.clients.itervalues():
 
1610
            for c in self.clients:
1751
1611
                if c.fingerprint == fpr:
1752
1612
                    client = c
1753
1613
                    break
1756
1616
                            "dress: %s", fpr, address)
1757
1617
                if self.use_dbus:
1758
1618
                    # Emit D-Bus signal
1759
 
                    mandos_dbus_service.ClientNotFound(fpr,
1760
 
                                                       address[0])
 
1619
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
1761
1620
                parent_pipe.send(False)
1762
1621
                return False
1763
1622
            
1764
1623
            gobject.io_add_watch(parent_pipe.fileno(),
1765
1624
                                 gobject.IO_IN | gobject.IO_HUP,
1766
1625
                                 functools.partial(self.handle_ipc,
1767
 
                                                   parent_pipe =
1768
 
                                                   parent_pipe,
1769
 
                                                   proc = proc,
1770
 
                                                   client_object =
1771
 
                                                   client))
 
1626
                                                   parent_pipe = parent_pipe,
 
1627
                                                   client_object = client))
1772
1628
            parent_pipe.send(True)
1773
 
            # remove the old hook in favor of the new above hook on
1774
 
            # same fileno
 
1629
            # remove the old hook in favor of the new above hook on same fileno
1775
1630
            return False
1776
1631
        if command == 'funcall':
1777
1632
            funcname = request[1]
1778
1633
            args = request[2]
1779
1634
            kwargs = request[3]
1780
1635
            
1781
 
            parent_pipe.send(('data', getattr(client_object,
1782
 
                                              funcname)(*args,
1783
 
                                                         **kwargs)))
 
1636
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1784
1637
        
1785
1638
        if command == 'getattr':
1786
1639
            attrname = request[1]
1787
1640
            if callable(client_object.__getattribute__(attrname)):
1788
1641
                parent_pipe.send(('function',))
1789
1642
            else:
1790
 
                parent_pipe.send(('data', client_object
1791
 
                                  .__getattribute__(attrname)))
 
1643
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1792
1644
        
1793
1645
        if command == 'setattr':
1794
1646
            attrname = request[1]
1921
1773
                        " system bus interface")
1922
1774
    parser.add_argument("--no-ipv6", action="store_false",
1923
1775
                        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
 
 
1928
1776
    options = parser.parse_args()
1929
1777
    
1930
1778
    if options.check:
1965
1813
    # options, if set.
1966
1814
    for option in ("interface", "address", "port", "debug",
1967
1815
                   "priority", "servicename", "configdir",
1968
 
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
 
1816
                   "use_dbus", "use_ipv6", "debuglevel"):
1969
1817
        value = getattr(options, option)
1970
1818
        if value is not None:
1971
1819
            server_settings[option] = value
2044
1892
            raise error
2045
1893
    
2046
1894
    if not debug and not debuglevel:
2047
 
        logger.setLevel(logging.WARNING)
 
1895
        syslogger.setLevel(logging.WARNING)
 
1896
        console.setLevel(logging.WARNING)
2048
1897
    if debuglevel:
2049
1898
        level = getattr(logging, debuglevel.upper())
2050
 
        logger.setLevel(level)
 
1899
        syslogger.setLevel(level)
 
1900
        console.setLevel(level)
2051
1901
    
2052
1902
    if debug:
2053
 
        logger.setLevel(logging.DEBUG)
2054
1903
        # Enable all possible GnuTLS debugging
2055
1904
        
2056
1905
        # "Use a log level over 10 to enable all debugging options."
2086
1935
    # End of Avahi example code
2087
1936
    if use_dbus:
2088
1937
        try:
2089
 
            bus_name = dbus.service.BusName("se.recompile.Mandos",
2090
 
                                            bus, do_not_queue=True)
2091
 
            old_bus_name = (dbus.service.BusName
2092
 
                            ("se.bsnet.fukt.Mandos", bus,
2093
 
                             do_not_queue=True))
 
1938
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
 
1939
                                            bus, do_not_queue=True)
 
1940
            bus_name2 = dbus.service.BusName("se.recompile.Mandos",
 
1941
                                            bus, do_not_queue=True)
2094
1942
        except dbus.exceptions.NameExistsException as e:
2095
1943
            logger.error(unicode(e) + ", disabling D-Bus")
2096
1944
            use_dbus = False
2097
1945
            server_settings["use_dbus"] = False
2098
1946
            tcp_server.use_dbus = False
2099
1947
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2100
 
    service = AvahiServiceToSyslog(name =
2101
 
                                   server_settings["servicename"],
2102
 
                                   servicetype = "_mandos._tcp",
2103
 
                                   protocol = protocol, bus = bus)
 
1948
    service = AvahiService(name = server_settings["servicename"],
 
1949
                           servicetype = "_mandos._tcp",
 
1950
                           protocol = protocol, bus = bus)
2104
1951
    if server_settings["interface"]:
2105
1952
        service.interface = (if_nametoindex
2106
1953
                             (str(server_settings["interface"])))
2110
1957
    
2111
1958
    client_class = Client
2112
1959
    if use_dbus:
2113
 
        client_class = functools.partial(ClientDBusTransitional,
2114
 
                                         bus = bus)
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():
 
1960
        client_class = functools.partial(ClientDBusTransitional, bus = bus)        
 
1961
    def client_config_items(config, section):
 
1962
        special_settings = {
 
1963
            "approved_by_default":
 
1964
                lambda: config.getboolean(section,
 
1965
                                          "approved_by_default"),
 
1966
            }
 
1967
        for name, value in config.items(section):
2156
1968
            try:
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)
 
1969
                yield (name, special_settings[name]())
2162
1970
            except KeyError:
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]))
 
1971
                yield (name, value)
2209
1972
    
2210
 
 
 
1973
    tcp_server.clients.update(set(
 
1974
            client_class(name = section,
 
1975
                         config= dict(client_config_items(
 
1976
                        client_config, section)))
 
1977
            for section in client_config.sections()))
2211
1978
    if not tcp_server.clients:
2212
1979
        logger.warning("No clients defined")
2213
1980
        
2235
2002
            """A D-Bus proxy object"""
2236
2003
            def __init__(self):
2237
2004
                dbus.service.Object.__init__(self, bus, "/")
2238
 
            _interface = "se.recompile.Mandos"
 
2005
            _interface = "se.bsnet.fukt.Mandos"
2239
2006
            
2240
2007
            @dbus.service.signal(_interface, signature="o")
2241
2008
            def ClientAdded(self, objpath):
2256
2023
            def GetAllClients(self):
2257
2024
                "D-Bus method"
2258
2025
                return dbus.Array(c.dbus_object_path
2259
 
                                  for c in
2260
 
                                  tcp_server.clients.itervalues())
 
2026
                                  for c in tcp_server.clients)
2261
2027
            
2262
2028
            @dbus.service.method(_interface,
2263
2029
                                 out_signature="a{oa{sv}}")
2265
2031
                "D-Bus method"
2266
2032
                return dbus.Dictionary(
2267
2033
                    ((c.dbus_object_path, c.GetAll(""))
2268
 
                     for c in tcp_server.clients.itervalues()),
 
2034
                     for c in tcp_server.clients),
2269
2035
                    signature="oa{sv}")
2270
2036
            
2271
2037
            @dbus.service.method(_interface, in_signature="o")
2272
2038
            def RemoveClient(self, object_path):
2273
2039
                "D-Bus method"
2274
 
                for c in tcp_server.clients.itervalues():
 
2040
                for c in tcp_server.clients:
2275
2041
                    if c.dbus_object_path == object_path:
2276
 
                        del tcp_server.clients[c.name]
 
2042
                        tcp_server.clients.remove(c)
2277
2043
                        c.remove_from_connection()
2278
2044
                        # Don't signal anything except ClientRemoved
2279
2045
                        c.disable(quiet=True)
2285
2051
            del _interface
2286
2052
        
2287
2053
        class MandosDBusServiceTransitional(MandosDBusService):
2288
 
            __metaclass__ = AlternateDBusNamesMetaclass
 
2054
            __metaclass__ = transitional_dbus_metaclass
2289
2055
        mandos_dbus_service = MandosDBusServiceTransitional()
2290
2056
    
2291
2057
    def cleanup():
2292
2058
        "Cleanup function; run on exit"
2293
2059
        service.cleanup()
2294
2060
        
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
2330
2061
        while tcp_server.clients:
2331
 
            name, client = tcp_server.clients.popitem()
 
2062
            client = tcp_server.clients.pop()
2332
2063
            if use_dbus:
2333
2064
                client.remove_from_connection()
 
2065
            client.disable_hook = None
2334
2066
            # Don't signal anything except ClientRemoved
2335
2067
            client.disable(quiet=True)
2336
2068
            if use_dbus:
2337
2069
                # Emit D-Bus signal
2338
 
                mandos_dbus_service.ClientRemoved(client
2339
 
                                                  .dbus_object_path,
 
2070
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2340
2071
                                                  client.name)
2341
 
        client_settings.clear()
2342
2072
    
2343
2073
    atexit.register(cleanup)
2344
2074
    
2345
 
    for client in tcp_server.clients.itervalues():
 
2075
    for client in tcp_server.clients:
2346
2076
        if use_dbus:
2347
2077
            # Emit D-Bus signal
2348
2078
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2349
 
        # Need to initiate checking of clients
2350
 
        if client.enabled:
2351
 
            client.init_checker()
2352
 
 
 
2079
        client.enable()
2353
2080
    
2354
2081
    tcp_server.enable()
2355
2082
    tcp_server.server_activate()