/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: teddy at bsnet
  • Date: 2011-11-12 18:15:29 UTC
  • mfrom: (521.1.1 doc-filename-class)
  • Revision ID: teddy@fukt.bsnet.se-20111112181529-v4emlw0v5fugv2tk
Merge doc markup fixes.

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@fukt.bsnet.se>.
 
31
# Contact the authors at <mandos@recompile.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
66
67
 
67
68
import dbus
68
69
import dbus.service
73
74
import ctypes.util
74
75
import xml.dom.minidom
75
76
import inspect
 
77
import Crypto.Cipher.AES
76
78
 
77
79
try:
78
80
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
83
85
        SO_BINDTODEVICE = None
84
86
 
85
87
 
86
 
version = "1.3.1"
87
 
 
88
 
#logger = logging.getLogger('mandos')
89
 
logger = logging.Logger('mandos')
 
88
version = "1.4.1"
 
89
 
 
90
logger = logging.getLogger()
 
91
stored_state_path = "/var/lib/mandos/clients.pickle"
 
92
 
90
93
syslogger = (logging.handlers.SysLogHandler
91
94
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
92
95
              address = str("/dev/log")))
96
99
logger.addHandler(syslogger)
97
100
 
98
101
console = logging.StreamHandler()
99
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
 
102
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
103
                                       ' [%(process)d]:'
100
104
                                       ' %(levelname)s:'
101
105
                                       ' %(message)s'))
102
106
logger.addHandler(console)
103
107
 
 
108
 
104
109
class AvahiError(Exception):
105
110
    def __init__(self, value, *args, **kwargs):
106
111
        self.value = value
160
165
                            " after %i retries, exiting.",
161
166
                            self.rename_count)
162
167
            raise AvahiServiceError("Too many renames")
163
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
168
        self.name = unicode(self.server
 
169
                            .GetAlternativeServiceName(self.name))
164
170
        logger.info("Changing Zeroconf service name to %r ...",
165
171
                    self.name)
166
 
        syslogger.setFormatter(logging.Formatter
167
 
                               ('Mandos (%s) [%%(process)d]:'
168
 
                                ' %%(levelname)s: %%(message)s'
169
 
                                % self.name))
170
172
        self.remove()
171
173
        try:
172
174
            self.add()
192
194
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
193
195
        self.entry_group_state_changed_match = (
194
196
            self.group.connect_to_signal(
195
 
                'StateChanged', self .entry_group_state_changed))
 
197
                'StateChanged', self.entry_group_state_changed))
196
198
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
197
199
                     self.name, self.type)
198
200
        self.group.AddService(
264
266
                                 self.server_state_changed)
265
267
        self.server_state_changed(self.server.GetState())
266
268
 
 
269
class AvahiServiceToSyslog(AvahiService):
 
270
    def rename(self):
 
271
        """Add the new name to the syslog messages"""
 
272
        ret = AvahiService.rename(self)
 
273
        syslogger.setFormatter(logging.Formatter
 
274
                               ('Mandos (%s) [%%(process)d]:'
 
275
                                ' %%(levelname)s: %%(message)s'
 
276
                                % self.name))
 
277
        return ret
267
278
 
268
279
def _timedelta_to_milliseconds(td):
269
280
    "Convert a datetime.timedelta() to milliseconds"
288
299
                     instance %(name)s can be used in the command.
289
300
    checker_initiator_tag: a gobject event source tag, or None
290
301
    created:    datetime.datetime(); (UTC) object creation
 
302
    client_structure: Object describing what attributes a client has
 
303
                      and is used for storing the client at exit
291
304
    current_checker_command: string; current running checker_command
292
 
    disable_hook:  If set, called by disable() as disable_hook(self)
293
305
    disable_initiator_tag: a gobject event source tag, or None
294
306
    enabled:    bool()
295
307
    fingerprint: string (40 or 32 hexadecimal digits); used to
298
310
    interval:   datetime.timedelta(); How often to start a new checker
299
311
    last_approval_request: datetime.datetime(); (UTC) or None
300
312
    last_checked_ok: datetime.datetime(); (UTC) or None
 
313
    last_checker_status: integer between 0 and 255 reflecting exit
 
314
                         status of last checker. -1 reflect crashed
 
315
                         checker, or None.
301
316
    last_enabled: datetime.datetime(); (UTC)
302
317
    name:       string; from the config file, used in log messages and
303
318
                        D-Bus identifiers
321
336
    
322
337
    def extended_timeout_milliseconds(self):
323
338
        "Return the 'extended_timeout' attribute in milliseconds"
324
 
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
339
        return _timedelta_to_milliseconds(self.extended_timeout)
325
340
    
326
341
    def interval_milliseconds(self):
327
342
        "Return the 'interval' attribute in milliseconds"
330
345
    def approval_delay_milliseconds(self):
331
346
        return _timedelta_to_milliseconds(self.approval_delay)
332
347
    
333
 
    def __init__(self, name = None, disable_hook=None, config=None):
 
348
    def __init__(self, name = None, config=None):
334
349
        """Note: the 'checker' key in 'config' sets the
335
350
        'checker_command' attribute and *not* the 'checker'
336
351
        attribute."""
356
371
                            % self.name)
357
372
        self.host = config.get("host", "")
358
373
        self.created = datetime.datetime.utcnow()
359
 
        self.enabled = False
 
374
        self.enabled = True
360
375
        self.last_approval_request = None
361
 
        self.last_enabled = None
 
376
        self.last_enabled = datetime.datetime.utcnow()
362
377
        self.last_checked_ok = None
 
378
        self.last_checker_status = None
363
379
        self.timeout = string_to_delta(config["timeout"])
364
 
        self.extended_timeout = string_to_delta(config["extended_timeout"])
 
380
        self.extended_timeout = string_to_delta(config
 
381
                                                ["extended_timeout"])
365
382
        self.interval = string_to_delta(config["interval"])
366
 
        self.disable_hook = disable_hook
367
383
        self.checker = None
368
384
        self.checker_initiator_tag = None
369
385
        self.disable_initiator_tag = None
370
 
        self.expires = None
 
386
        self.expires = datetime.datetime.utcnow() + self.timeout
371
387
        self.checker_callback_tag = None
372
388
        self.checker_command = config["checker"]
373
389
        self.current_checker_command = None
374
 
        self.last_connect = None
375
390
        self._approved = None
376
391
        self.approved_by_default = config.get("approved_by_default",
377
392
                                              True)
380
395
            config["approval_delay"])
381
396
        self.approval_duration = string_to_delta(
382
397
            config["approval_duration"])
383
 
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
 
398
        self.changedstate = (multiprocessing_manager
 
399
                             .Condition(multiprocessing_manager
 
400
                                        .Lock()))
 
401
        self.client_structure = [attr for attr
 
402
                                 in self.__dict__.iterkeys()
 
403
                                 if not attr.startswith("_")]
 
404
        self.client_structure.append("client_structure")
 
405
 
 
406
 
 
407
        for name, t in inspect.getmembers(type(self),
 
408
                                          lambda obj:
 
409
                                              isinstance(obj,
 
410
                                                         property)):
 
411
            if not name.startswith("_"):
 
412
                self.client_structure.append(name)
384
413
    
 
414
    # Send notice to process children that client state has changed
385
415
    def send_changedstate(self):
386
 
        self.changedstate.acquire()
387
 
        self.changedstate.notify_all()
388
 
        self.changedstate.release()
389
 
        
 
416
        with self.changedstate:
 
417
            self.changedstate.notify_all()
 
418
    
390
419
    def enable(self):
391
420
        """Start this client's checker and timeout hooks"""
392
421
        if getattr(self, "enabled", False):
393
422
            # Already enabled
394
423
            return
395
424
        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
402
425
        self.expires = datetime.datetime.utcnow() + self.timeout
403
 
        self.disable_initiator_tag = (gobject.timeout_add
404
 
                                   (self.timeout_milliseconds(),
405
 
                                    self.disable))
406
426
        self.enabled = True
407
427
        self.last_enabled = datetime.datetime.utcnow()
408
 
        # Also start a new checker *right now*.
409
 
        self.start_checker()
 
428
        self.init_checker()
410
429
    
411
430
    def disable(self, quiet=True):
412
431
        """Disable this client."""
424
443
            gobject.source_remove(self.checker_initiator_tag)
425
444
            self.checker_initiator_tag = None
426
445
        self.stop_checker()
427
 
        if self.disable_hook:
428
 
            self.disable_hook(self)
429
446
        self.enabled = False
430
447
        # Do not run this again if called by a gobject.timeout_add
431
448
        return False
432
449
    
433
450
    def __del__(self):
434
 
        self.disable_hook = None
435
451
        self.disable()
436
 
    
 
452
 
 
453
    def init_checker(self):
 
454
        # Schedule a new checker to be started an 'interval' from now,
 
455
        # and every interval from then on.
 
456
        self.checker_initiator_tag = (gobject.timeout_add
 
457
                                      (self.interval_milliseconds(),
 
458
                                       self.start_checker))
 
459
        # Schedule a disable() when 'timeout' has passed
 
460
        self.disable_initiator_tag = (gobject.timeout_add
 
461
                                   (self.timeout_milliseconds(),
 
462
                                    self.disable))
 
463
        # Also start a new checker *right now*.
 
464
        self.start_checker()
 
465
 
 
466
        
437
467
    def checker_callback(self, pid, condition, command):
438
468
        """The checker has completed, so take appropriate actions."""
439
469
        self.checker_callback_tag = None
440
470
        self.checker = None
441
471
        if os.WIFEXITED(condition):
442
 
            exitstatus = os.WEXITSTATUS(condition)
443
 
            if exitstatus == 0:
 
472
            self.last_checker_status =  os.WEXITSTATUS(condition)
 
473
            if self.last_checker_status == 0:
444
474
                logger.info("Checker for %(name)s succeeded",
445
475
                            vars(self))
446
476
                self.checked_ok()
448
478
                logger.info("Checker for %(name)s failed",
449
479
                            vars(self))
450
480
        else:
 
481
            self.last_checker_status = -1
451
482
            logger.warning("Checker for %(name)s crashed?",
452
483
                           vars(self))
453
484
    
460
491
        if timeout is None:
461
492
            timeout = self.timeout
462
493
        self.last_checked_ok = datetime.datetime.utcnow()
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))
 
494
        if self.disable_initiator_tag is not None:
 
495
            gobject.source_remove(self.disable_initiator_tag)
 
496
        if getattr(self, "enabled", False):
 
497
            self.disable_initiator_tag = (gobject.timeout_add
 
498
                                          (_timedelta_to_milliseconds
 
499
                                           (timeout), self.disable))
 
500
            self.expires = datetime.datetime.utcnow() + timeout
468
501
    
469
502
    def need_approval(self):
470
503
        self.last_approval_request = datetime.datetime.utcnow()
562
595
                raise
563
596
        self.checker = None
564
597
 
 
598
    # Encrypts a client secret and stores it in a varible
 
599
    # encrypted_secret
 
600
    def encrypt_secret(self, key):
 
601
        # Encryption-key need to be of a specific size, so we hash
 
602
        # supplied key
 
603
        hasheng = hashlib.sha256()
 
604
        hasheng.update(key)
 
605
        encryptionkey = hasheng.digest()
 
606
 
 
607
        # Create validation hash so we know at decryption if it was
 
608
        # sucessful
 
609
        hasheng = hashlib.sha256()
 
610
        hasheng.update(self.secret)
 
611
        validationhash = hasheng.digest()
 
612
 
 
613
        # Encrypt secret
 
614
        iv = os.urandom(Crypto.Cipher.AES.block_size)
 
615
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
616
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
617
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
 
618
        self.encrypted_secret = (ciphertext, iv)
 
619
 
 
620
    # Decrypt a encrypted client secret
 
621
    def decrypt_secret(self, key):
 
622
        # Decryption-key need to be of a specific size, so we hash
 
623
        # supplied key
 
624
        hasheng = hashlib.sha256()
 
625
        hasheng.update(key)
 
626
        encryptionkey = hasheng.digest()
 
627
 
 
628
        # Decrypt encrypted secret
 
629
        ciphertext, iv = self.encrypted_secret
 
630
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
631
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
632
        plain = ciphereng.decrypt(ciphertext)
 
633
 
 
634
        # Validate decrypted secret to know if it was succesful
 
635
        hasheng = hashlib.sha256()
 
636
        validationhash = plain[:hasheng.digest_size]
 
637
        secret = plain[hasheng.digest_size:]
 
638
        hasheng.update(secret)
 
639
 
 
640
        # If validation fails, we use key as new secret. Otherwise, we
 
641
        # use the decrypted secret
 
642
        if hasheng.digest() == validationhash:
 
643
            self.secret = secret
 
644
        else:
 
645
            self.secret = key
 
646
        del self.encrypted_secret
 
647
 
565
648
 
566
649
def dbus_service_property(dbus_interface, signature="v",
567
650
                          access="readwrite", byte_arrays=False):
629
712
        """
630
713
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
714
                for cls in self.__class__.__mro__
632
 
                for name, prop in inspect.getmembers(cls, self._is_dbus_property))
 
715
                for name, prop in
 
716
                inspect.getmembers(cls, self._is_dbus_property))
633
717
    
634
718
    def _get_dbus_property(self, interface_name, property_name):
635
719
        """Returns a bound method if one exists which is a D-Bus
636
720
        property with the specified name and interface.
637
721
        """
638
722
        for cls in  self.__class__.__mro__:
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:
 
723
            for name, value in (inspect.getmembers
 
724
                                (cls, self._is_dbus_property)):
 
725
                if (value._dbus_name == property_name
 
726
                    and value._dbus_interface == interface_name):
641
727
                    return value.__get__(self)
642
728
        
643
729
        # No such property
644
730
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
645
731
                                   + interface_name + "."
646
732
                                   + property_name)
647
 
 
648
733
    
649
734
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
650
735
                         out_signature="v")
755
840
    return dbus.String(dt.isoformat(),
756
841
                       variant_level=variant_level)
757
842
 
758
 
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
 
843
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
 
844
                                  .__metaclass__):
 
845
    """Applied to an empty subclass of a D-Bus object, this metaclass
 
846
    will add additional D-Bus attributes matching a certain pattern.
 
847
    """
759
848
    def __new__(mcs, name, bases, attr):
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
 
849
        # Go through all the base classes which could have D-Bus
 
850
        # methods, signals, or properties in them
 
851
        for base in (b for b in bases
 
852
                     if issubclass(b, dbus.service.Object)):
 
853
            # Go though all attributes of the base class
 
854
            for attrname, attribute in inspect.getmembers(base):
 
855
                # Ignore non-D-Bus attributes, and D-Bus attributes
 
856
                # with the wrong interface name
 
857
                if (not hasattr(attribute, "_dbus_interface")
 
858
                    or not attribute._dbus_interface
 
859
                    .startswith("se.recompile.Mandos")):
 
860
                    continue
 
861
                # Create an alternate D-Bus interface name based on
 
862
                # the current name
 
863
                alt_interface = (attribute._dbus_interface
 
864
                                 .replace("se.recompile.Mandos",
 
865
                                          "se.bsnet.fukt.Mandos"))
 
866
                # Is this a D-Bus signal?
 
867
                if getattr(attribute, "_dbus_is_signal", False):
 
868
                    # Extract the original non-method function by
 
869
                    # black magic
 
870
                    nonmethod_func = (dict(
 
871
                            zip(attribute.func_code.co_freevars,
 
872
                                attribute.__closure__))["func"]
 
873
                                      .cell_contents)
 
874
                    # Create a new, but exactly alike, function
 
875
                    # object, and decorate it to be a new D-Bus signal
 
876
                    # with the alternate D-Bus interface name
 
877
                    new_function = (dbus.service.signal
 
878
                                    (alt_interface,
 
879
                                     attribute._dbus_signature)
 
880
                                    (types.FunctionType(
 
881
                                nonmethod_func.func_code,
 
882
                                nonmethod_func.func_globals,
 
883
                                nonmethod_func.func_name,
 
884
                                nonmethod_func.func_defaults,
 
885
                                nonmethod_func.func_closure)))
 
886
                    # Define a creator of a function to call both the
 
887
                    # old and new functions, so both the old and new
 
888
                    # signals gets sent when the function is called
 
889
                    def fixscope(func1, func2):
 
890
                        """This function is a scope container to pass
 
891
                        func1 and func2 to the "call_both" function
 
892
                        outside of its arguments"""
 
893
                        def call_both(*args, **kwargs):
 
894
                            """This function will emit two D-Bus
 
895
                            signals by calling func1 and func2"""
 
896
                            func1(*args, **kwargs)
 
897
                            func2(*args, **kwargs)
 
898
                        return call_both
 
899
                    # Create the "call_both" function and add it to
 
900
                    # the class
 
901
                    attr[attrname] = fixscope(attribute,
 
902
                                              new_function)
 
903
                # Is this a D-Bus method?
 
904
                elif getattr(attribute, "_dbus_is_method", False):
 
905
                    # Create a new, but exactly alike, function
 
906
                    # object.  Decorate it to be a new D-Bus method
 
907
                    # with the alternate D-Bus interface name.  Add it
 
908
                    # to the class.
 
909
                    attr[attrname] = (dbus.service.method
 
910
                                      (alt_interface,
 
911
                                       attribute._dbus_in_signature,
 
912
                                       attribute._dbus_out_signature)
 
913
                                      (types.FunctionType
 
914
                                       (attribute.func_code,
 
915
                                        attribute.func_globals,
 
916
                                        attribute.func_name,
 
917
                                        attribute.func_defaults,
 
918
                                        attribute.func_closure)))
 
919
                # Is this a D-Bus property?
 
920
                elif getattr(attribute, "_dbus_is_property", False):
 
921
                    # Create a new, but exactly alike, function
 
922
                    # object, and decorate it to be a new D-Bus
 
923
                    # property with the alternate D-Bus interface
 
924
                    # name.  Add it to the class.
 
925
                    attr[attrname] = (dbus_service_property
 
926
                                      (alt_interface,
 
927
                                       attribute._dbus_signature,
 
928
                                       attribute._dbus_access,
 
929
                                       attribute
 
930
                                       ._dbus_get_args_options
 
931
                                       ["byte_arrays"])
 
932
                                      (types.FunctionType
 
933
                                       (attribute.func_code,
 
934
                                        attribute.func_globals,
 
935
                                        attribute.func_name,
 
936
                                        attribute.func_defaults,
 
937
                                        attribute.func_closure)))
812
938
        return type.__new__(mcs, name, bases, attr)
813
939
 
814
940
class ClientDBus(Client, DBusObjectWithProperties):
825
951
    # dbus.service.Object doesn't use super(), so we can't either.
826
952
    
827
953
    def __init__(self, bus = None, *args, **kwargs):
 
954
        self.bus = bus
828
955
        self._approvals_pending = 0
829
 
        self.bus = bus
830
956
        Client.__init__(self, *args, **kwargs)
 
957
        self.add_to_dbus()
 
958
    
 
959
    def add_to_dbus(self):
831
960
        # Only now, when this client is initialized, can it show up on
832
961
        # the D-Bus
833
962
        client_object_name = unicode(self.name).translate(
841
970
    def notifychangeproperty(transform_func,
842
971
                             dbus_name, type_func=lambda x: x,
843
972
                             variant_level=1):
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
 
973
        """ Modify a variable so that it's a property which announces
 
974
        its changes to DBus.
 
975
 
 
976
        transform_fun: Function that takes a value and a variant_level
 
977
                       and transforms it to a D-Bus type.
 
978
        dbus_name: D-Bus name of the variable
849
979
        type_func: Function that transform the value before sending it
850
 
                   to DBus
851
 
        variant_level: DBus variant level. default: 1
 
980
                   to the D-Bus.  Default: no transform
 
981
        variant_level: D-Bus variant level.  Default: 1
852
982
        """
853
 
        real_value = [None,]
 
983
        attrname = "_{0}".format(dbus_name)
854
984
        def setter(self, value):
855
 
            old_value = real_value[0]
856
 
            real_value[0] = value
857
985
            if hasattr(self, "dbus_object_path"):
858
 
                if type_func(old_value) != type_func(real_value[0]):
859
 
                    dbus_value = transform_func(type_func(real_value[0]),
860
 
                                                variant_level)
 
986
                if (not hasattr(self, attrname) or
 
987
                    type_func(getattr(self, attrname, None))
 
988
                    != type_func(value)):
 
989
                    dbus_value = transform_func(type_func(value),
 
990
                                                variant_level
 
991
                                                =variant_level)
861
992
                    self.PropertyChanged(dbus.String(dbus_name),
862
993
                                         dbus_value)
 
994
            setattr(self, attrname, value)
863
995
        
864
 
        return property(lambda self: real_value[0], setter)
 
996
        return property(lambda self: getattr(self, attrname), setter)
865
997
    
866
998
    
867
999
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
872
1004
    last_enabled = notifychangeproperty(datetime_to_dbus,
873
1005
                                        "LastEnabled")
874
1006
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
875
 
                                   type_func = lambda checker: checker is not None)
 
1007
                                   type_func = lambda checker:
 
1008
                                       checker is not None)
876
1009
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
877
1010
                                           "LastCheckedOK")
878
 
    last_approval_request = notifychangeproperty(datetime_to_dbus,
879
 
                                                 "LastApprovalRequest")
 
1011
    last_approval_request = notifychangeproperty(
 
1012
        datetime_to_dbus, "LastApprovalRequest")
880
1013
    approved_by_default = notifychangeproperty(dbus.Boolean,
881
1014
                                               "ApprovedByDefault")
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)
 
1015
    approval_delay = notifychangeproperty(dbus.UInt16,
 
1016
                                          "ApprovalDelay",
 
1017
                                          type_func =
 
1018
                                          _timedelta_to_milliseconds)
 
1019
    approval_duration = notifychangeproperty(
 
1020
        dbus.UInt16, "ApprovalDuration",
 
1021
        type_func = _timedelta_to_milliseconds)
886
1022
    host = notifychangeproperty(dbus.String, "Host")
887
1023
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
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)
 
1024
                                   type_func =
 
1025
                                   _timedelta_to_milliseconds)
 
1026
    extended_timeout = notifychangeproperty(
 
1027
        dbus.UInt16, "ExtendedTimeout",
 
1028
        type_func = _timedelta_to_milliseconds)
 
1029
    interval = notifychangeproperty(dbus.UInt16,
 
1030
                                    "Interval",
 
1031
                                    type_func =
 
1032
                                    _timedelta_to_milliseconds)
893
1033
    checker_command = notifychangeproperty(dbus.String, "Checker")
894
1034
    
895
1035
    del notifychangeproperty
949
1089
    
950
1090
    
951
1091
    ## D-Bus methods, signals & properties
952
 
    _interface = "se.bsnet.fukt.Mandos.Client"
953
 
 
 
1092
    _interface = "se.recompile.Mandos.Client"
 
1093
    
954
1094
    ## Signals
955
1095
    
956
1096
    # CheckerCompleted - signal
1130
1270
        gobject.source_remove(self.disable_initiator_tag)
1131
1271
        self.disable_initiator_tag = None
1132
1272
        self.expires = None
1133
 
        time_to_die = (self.
1134
 
                       _timedelta_to_milliseconds((self
1135
 
                                                   .last_checked_ok
1136
 
                                                   + self.timeout)
1137
 
                                                  - datetime.datetime
1138
 
                                                  .utcnow()))
 
1273
        time_to_die = _timedelta_to_milliseconds((self
 
1274
                                                  .last_checked_ok
 
1275
                                                  + self.timeout)
 
1276
                                                 - datetime.datetime
 
1277
                                                 .utcnow())
1139
1278
        if time_to_die <= 0:
1140
1279
            # The timeout has passed
1141
1280
            self.disable()
1142
1281
        else:
1143
1282
            self.expires = (datetime.datetime.utcnow()
1144
 
                            + datetime.timedelta(milliseconds = time_to_die))
 
1283
                            + datetime.timedelta(milliseconds =
 
1284
                                                 time_to_die))
1145
1285
            self.disable_initiator_tag = (gobject.timeout_add
1146
1286
                                          (time_to_die, self.disable))
1147
1287
    
1227
1367
        self._pipe.send(('setattr', name, value))
1228
1368
 
1229
1369
class ClientDBusTransitional(ClientDBus):
1230
 
    __metaclass__ = transitional_dbus_metaclass
 
1370
    __metaclass__ = AlternateDBusNamesMetaclass
1231
1371
 
1232
1372
class ClientHandler(socketserver.BaseRequestHandler, object):
1233
1373
    """A class to handle client connections.
1313
1453
                                       client.name)
1314
1454
                        if self.server.use_dbus:
1315
1455
                            # Emit D-Bus signal
1316
 
                            client.Rejected("Disabled")                    
 
1456
                            client.Rejected("Disabled")
1317
1457
                        return
1318
1458
                    
1319
1459
                    if client._approved or not client.approval_delay:
1336
1476
                        return
1337
1477
                    
1338
1478
                    #wait until timeout or approved
1339
 
                    #x = float(client._timedelta_to_milliseconds(delay))
1340
1479
                    time = datetime.datetime.now()
1341
1480
                    client.changedstate.acquire()
1342
 
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1481
                    (client.changedstate.wait
 
1482
                     (float(client._timedelta_to_milliseconds(delay)
 
1483
                            / 1000)))
1343
1484
                    client.changedstate.release()
1344
1485
                    time2 = datetime.datetime.now()
1345
1486
                    if (time2 - time) >= delay:
1369
1510
                    sent_size += sent
1370
1511
                
1371
1512
                logger.info("Sending secret to %s", client.name)
1372
 
                # bump the timeout as if seen
 
1513
                # bump the timeout using extended_timeout
1373
1514
                client.checked_ok(client.extended_timeout)
1374
1515
                if self.server.use_dbus:
1375
1516
                    # Emit D-Bus signal
1455
1596
        except:
1456
1597
            self.handle_error(request, address)
1457
1598
        self.close_request(request)
1458
 
            
 
1599
    
1459
1600
    def process_request(self, request, address):
1460
1601
        """Start a new process to process the request."""
1461
 
        multiprocessing.Process(target = self.sub_process_main,
1462
 
                                args = (request, address)).start()
 
1602
        proc = multiprocessing.Process(target = self.sub_process_main,
 
1603
                                       args = (request,
 
1604
                                               address))
 
1605
        proc.start()
 
1606
        return proc
1463
1607
 
1464
1608
 
1465
1609
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1471
1615
        """
1472
1616
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1473
1617
        
1474
 
        super(MultiprocessingMixInWithPipe,
1475
 
              self).process_request(request, client_address)
 
1618
        proc = MultiprocessingMixIn.process_request(self, request,
 
1619
                                                    client_address)
1476
1620
        self.child_pipe.close()
1477
 
        self.add_pipe(parent_pipe)
 
1621
        self.add_pipe(parent_pipe, proc)
1478
1622
    
1479
 
    def add_pipe(self, parent_pipe):
 
1623
    def add_pipe(self, parent_pipe, proc):
1480
1624
        """Dummy function; override as necessary"""
1481
1625
        raise NotImplementedError
1482
1626
 
1560
1704
        self.enabled = False
1561
1705
        self.clients = clients
1562
1706
        if self.clients is None:
1563
 
            self.clients = set()
 
1707
            self.clients = {}
1564
1708
        self.use_dbus = use_dbus
1565
1709
        self.gnutls_priority = gnutls_priority
1566
1710
        IPv6_TCPServer.__init__(self, server_address,
1570
1714
    def server_activate(self):
1571
1715
        if self.enabled:
1572
1716
            return socketserver.TCPServer.server_activate(self)
 
1717
    
1573
1718
    def enable(self):
1574
1719
        self.enabled = True
1575
 
    def add_pipe(self, parent_pipe):
 
1720
    
 
1721
    def add_pipe(self, parent_pipe, proc):
1576
1722
        # Call "handle_ipc" for both data and EOF events
1577
1723
        gobject.io_add_watch(parent_pipe.fileno(),
1578
1724
                             gobject.IO_IN | gobject.IO_HUP,
1579
1725
                             functools.partial(self.handle_ipc,
1580
 
                                               parent_pipe = parent_pipe))
1581
 
        
 
1726
                                               parent_pipe =
 
1727
                                               parent_pipe,
 
1728
                                               proc = proc))
 
1729
    
1582
1730
    def handle_ipc(self, source, condition, parent_pipe=None,
1583
 
                   client_object=None):
 
1731
                   proc = None, client_object=None):
1584
1732
        condition_names = {
1585
1733
            gobject.IO_IN: "IN",   # There is data to read.
1586
1734
            gobject.IO_OUT: "OUT", # Data can be written (without
1595
1743
                                       for cond, name in
1596
1744
                                       condition_names.iteritems()
1597
1745
                                       if cond & condition)
1598
 
        # error or the other end of multiprocessing.Pipe has closed
 
1746
        # error, or the other end of multiprocessing.Pipe has closed
1599
1747
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1748
            # Wait for other process to exit
 
1749
            proc.join()
1600
1750
            return False
1601
1751
        
1602
1752
        # Read a request from the child
1607
1757
            fpr = request[1]
1608
1758
            address = request[2]
1609
1759
            
1610
 
            for c in self.clients:
 
1760
            for c in self.clients.itervalues():
1611
1761
                if c.fingerprint == fpr:
1612
1762
                    client = c
1613
1763
                    break
1616
1766
                            "dress: %s", fpr, address)
1617
1767
                if self.use_dbus:
1618
1768
                    # Emit D-Bus signal
1619
 
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
 
1769
                    mandos_dbus_service.ClientNotFound(fpr,
 
1770
                                                       address[0])
1620
1771
                parent_pipe.send(False)
1621
1772
                return False
1622
1773
            
1623
1774
            gobject.io_add_watch(parent_pipe.fileno(),
1624
1775
                                 gobject.IO_IN | gobject.IO_HUP,
1625
1776
                                 functools.partial(self.handle_ipc,
1626
 
                                                   parent_pipe = parent_pipe,
1627
 
                                                   client_object = client))
 
1777
                                                   parent_pipe =
 
1778
                                                   parent_pipe,
 
1779
                                                   proc = proc,
 
1780
                                                   client_object =
 
1781
                                                   client))
1628
1782
            parent_pipe.send(True)
1629
 
            # remove the old hook in favor of the new above hook on same fileno
 
1783
            # remove the old hook in favor of the new above hook on
 
1784
            # same fileno
1630
1785
            return False
1631
1786
        if command == 'funcall':
1632
1787
            funcname = request[1]
1633
1788
            args = request[2]
1634
1789
            kwargs = request[3]
1635
1790
            
1636
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1791
            parent_pipe.send(('data', getattr(client_object,
 
1792
                                              funcname)(*args,
 
1793
                                                         **kwargs)))
1637
1794
        
1638
1795
        if command == 'getattr':
1639
1796
            attrname = request[1]
1640
1797
            if callable(client_object.__getattribute__(attrname)):
1641
1798
                parent_pipe.send(('function',))
1642
1799
            else:
1643
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1800
                parent_pipe.send(('data', client_object
 
1801
                                  .__getattribute__(attrname)))
1644
1802
        
1645
1803
        if command == 'setattr':
1646
1804
            attrname = request[1]
1773
1931
                        " system bus interface")
1774
1932
    parser.add_argument("--no-ipv6", action="store_false",
1775
1933
                        dest="use_ipv6", help="Do not use IPv6")
 
1934
    parser.add_argument("--no-restore", action="store_false",
 
1935
                        dest="restore",
 
1936
                        help="Do not restore stored state",
 
1937
                        default=True)
 
1938
 
1776
1939
    options = parser.parse_args()
1777
1940
    
1778
1941
    if options.check:
1813
1976
    # options, if set.
1814
1977
    for option in ("interface", "address", "port", "debug",
1815
1978
                   "priority", "servicename", "configdir",
1816
 
                   "use_dbus", "use_ipv6", "debuglevel"):
 
1979
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
1817
1980
        value = getattr(options, option)
1818
1981
        if value is not None:
1819
1982
            server_settings[option] = value
1892
2055
            raise error
1893
2056
    
1894
2057
    if not debug and not debuglevel:
1895
 
        syslogger.setLevel(logging.WARNING)
1896
 
        console.setLevel(logging.WARNING)
 
2058
        logger.setLevel(logging.WARNING)
1897
2059
    if debuglevel:
1898
2060
        level = getattr(logging, debuglevel.upper())
1899
 
        syslogger.setLevel(level)
1900
 
        console.setLevel(level)
 
2061
        logger.setLevel(level)
1901
2062
    
1902
2063
    if debug:
 
2064
        logger.setLevel(logging.DEBUG)
1903
2065
        # Enable all possible GnuTLS debugging
1904
2066
        
1905
2067
        # "Use a log level over 10 to enable all debugging options."
1935
2097
    # End of Avahi example code
1936
2098
    if use_dbus:
1937
2099
        try:
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)
 
2100
            bus_name = dbus.service.BusName("se.recompile.Mandos",
 
2101
                                            bus, do_not_queue=True)
 
2102
            old_bus_name = (dbus.service.BusName
 
2103
                            ("se.bsnet.fukt.Mandos", bus,
 
2104
                             do_not_queue=True))
1942
2105
        except dbus.exceptions.NameExistsException as e:
1943
2106
            logger.error(unicode(e) + ", disabling D-Bus")
1944
2107
            use_dbus = False
1945
2108
            server_settings["use_dbus"] = False
1946
2109
            tcp_server.use_dbus = False
1947
2110
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1948
 
    service = AvahiService(name = server_settings["servicename"],
1949
 
                           servicetype = "_mandos._tcp",
1950
 
                           protocol = protocol, bus = bus)
 
2111
    service = AvahiServiceToSyslog(name =
 
2112
                                   server_settings["servicename"],
 
2113
                                   servicetype = "_mandos._tcp",
 
2114
                                   protocol = protocol, bus = bus)
1951
2115
    if server_settings["interface"]:
1952
2116
        service.interface = (if_nametoindex
1953
2117
                             (str(server_settings["interface"])))
1957
2121
    
1958
2122
    client_class = Client
1959
2123
    if use_dbus:
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):
 
2124
        client_class = functools.partial(ClientDBusTransitional,
 
2125
                                         bus = bus)
 
2126
    
 
2127
    special_settings = {
 
2128
        # Some settings need to be accessd by special methods;
 
2129
        # booleans need .getboolean(), etc.  Here is a list of them:
 
2130
        "approved_by_default":
 
2131
            lambda section:
 
2132
            client_config.getboolean(section, "approved_by_default"),
 
2133
        }
 
2134
    # Construct a new dict of client settings of this form:
 
2135
    # { client_name: {setting_name: value, ...}, ...}
 
2136
    # with exceptions for any special settings as defined above
 
2137
    client_settings = dict((clientname,
 
2138
                           dict((setting,
 
2139
                                 (value if
 
2140
                                  setting not in special_settings
 
2141
                                  else special_settings[setting]
 
2142
                                  (clientname)))
 
2143
                                for setting, value
 
2144
                                in client_config.items(clientname)))
 
2145
                          for clientname in client_config.sections())
 
2146
    
 
2147
    old_client_settings = {}
 
2148
    clients_data = []
 
2149
 
 
2150
    # Get client data and settings from last running state. 
 
2151
    if server_settings["restore"]:
 
2152
        try:
 
2153
            with open(stored_state_path, "rb") as stored_state:
 
2154
                clients_data, old_client_settings = (
 
2155
                    pickle.load(stored_state))
 
2156
            os.remove(stored_state_path)
 
2157
        except IOError as e:
 
2158
            logger.warning("Could not load persistant state: {0}"
 
2159
                           .format(e))
 
2160
            if e.errno != errno.ENOENT:
 
2161
                raise
 
2162
 
 
2163
    for client in clients_data:
 
2164
        client_name = client["name"]
 
2165
        
 
2166
        # Decide which value to use after restoring saved state.
 
2167
        # We have three different values: Old config file,
 
2168
        # new config file, and saved state.
 
2169
        # New config value takes precedence if it differs from old
 
2170
        # config value, otherwise use saved state.
 
2171
        for name, value in client_settings[client_name].items():
1968
2172
            try:
1969
 
                yield (name, special_settings[name]())
 
2173
                # For each value in new config, check if it differs
 
2174
                # from the old config value (Except for the "secret"
 
2175
                # attribute)
 
2176
                if (name != "secret" and
 
2177
                    value != old_client_settings[client_name][name]):
 
2178
                    setattr(client, name, value)
1970
2179
            except KeyError:
1971
 
                yield (name, value)
 
2180
                pass
 
2181
 
 
2182
        # Clients who has passed its expire date, can still be enabled
 
2183
        # if its last checker was sucessful. Clients who checkers
 
2184
        # failed before we stored it state is asumed to had failed
 
2185
        # checker during downtime.
 
2186
        if client["enabled"] and client["last_checked_ok"]:
 
2187
            if ((datetime.datetime.utcnow()
 
2188
                 - client["last_checked_ok"]) > client["interval"]):
 
2189
                if client["last_checker_status"] != 0:
 
2190
                    client["enabled"] = False
 
2191
                else:
 
2192
                    client["expires"] = (datetime.datetime.utcnow()
 
2193
                                         + client["timeout"])
 
2194
 
 
2195
        client["changedstate"] = (multiprocessing_manager
 
2196
                                  .Condition(multiprocessing_manager
 
2197
                                             .Lock()))
 
2198
        if use_dbus:
 
2199
            new_client = ClientDBusTransitional.__new__(
 
2200
                ClientDBusTransitional)
 
2201
            tcp_server.clients[client_name] = new_client
 
2202
            new_client.bus = bus
 
2203
            for name, value in client.iteritems():
 
2204
                setattr(new_client, name, value)
 
2205
            new_client._approvals_pending = 0
 
2206
            new_client.add_to_dbus()
 
2207
        else:
 
2208
            tcp_server.clients[client_name] = Client.__new__(Client)
 
2209
            for name, value in client.iteritems():
 
2210
                setattr(tcp_server.clients[client_name], name, value)
 
2211
                
 
2212
        tcp_server.clients[client_name].decrypt_secret(
 
2213
            client_settings[client_name]["secret"])            
 
2214
        
 
2215
    # Create/remove clients based on new changes made to config
 
2216
    for clientname in set(old_client_settings) - set(client_settings):
 
2217
        del tcp_server.clients[clientname]
 
2218
    for clientname in set(client_settings) - set(old_client_settings):
 
2219
        tcp_server.clients[clientname] = client_class(name
 
2220
                                                      = clientname,
 
2221
                                                      config =
 
2222
                                                      client_settings
 
2223
                                                      [clientname])
1972
2224
    
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()))
1978
2225
    if not tcp_server.clients:
1979
2226
        logger.warning("No clients defined")
1980
2227
        
2002
2249
            """A D-Bus proxy object"""
2003
2250
            def __init__(self):
2004
2251
                dbus.service.Object.__init__(self, bus, "/")
2005
 
            _interface = "se.bsnet.fukt.Mandos"
 
2252
            _interface = "se.recompile.Mandos"
2006
2253
            
2007
2254
            @dbus.service.signal(_interface, signature="o")
2008
2255
            def ClientAdded(self, objpath):
2023
2270
            def GetAllClients(self):
2024
2271
                "D-Bus method"
2025
2272
                return dbus.Array(c.dbus_object_path
2026
 
                                  for c in tcp_server.clients)
 
2273
                                  for c in
 
2274
                                  tcp_server.clients.itervalues())
2027
2275
            
2028
2276
            @dbus.service.method(_interface,
2029
2277
                                 out_signature="a{oa{sv}}")
2031
2279
                "D-Bus method"
2032
2280
                return dbus.Dictionary(
2033
2281
                    ((c.dbus_object_path, c.GetAll(""))
2034
 
                     for c in tcp_server.clients),
 
2282
                     for c in tcp_server.clients.itervalues()),
2035
2283
                    signature="oa{sv}")
2036
2284
            
2037
2285
            @dbus.service.method(_interface, in_signature="o")
2038
2286
            def RemoveClient(self, object_path):
2039
2287
                "D-Bus method"
2040
 
                for c in tcp_server.clients:
 
2288
                for c in tcp_server.clients.itervalues():
2041
2289
                    if c.dbus_object_path == object_path:
2042
 
                        tcp_server.clients.remove(c)
 
2290
                        del tcp_server.clients[c.name]
2043
2291
                        c.remove_from_connection()
2044
2292
                        # Don't signal anything except ClientRemoved
2045
2293
                        c.disable(quiet=True)
2051
2299
            del _interface
2052
2300
        
2053
2301
        class MandosDBusServiceTransitional(MandosDBusService):
2054
 
            __metaclass__ = transitional_dbus_metaclass
 
2302
            __metaclass__ = AlternateDBusNamesMetaclass
2055
2303
        mandos_dbus_service = MandosDBusServiceTransitional()
2056
2304
    
2057
2305
    def cleanup():
2058
2306
        "Cleanup function; run on exit"
2059
2307
        service.cleanup()
2060
2308
        
 
2309
        multiprocessing.active_children()
 
2310
        if not (tcp_server.clients or client_settings):
 
2311
            return
 
2312
 
 
2313
        # Store client before exiting. Secrets are encrypted with key
 
2314
        # based on what config file has. If config file is
 
2315
        # removed/edited, old secret will thus be unrecovable.
 
2316
        clients = []
 
2317
        for client in tcp_server.clients.itervalues():
 
2318
            client.encrypt_secret(
 
2319
                client_settings[client.name]["secret"])
 
2320
 
 
2321
            client_dict = {}
 
2322
 
 
2323
            # A list of attributes that will not be stored when
 
2324
            # shutting down.
 
2325
            exclude = set(("bus", "changedstate", "secret"))
 
2326
            for name, typ in inspect.getmembers(dbus.service.Object):
 
2327
                exclude.add(name)
 
2328
                
 
2329
            client_dict["encrypted_secret"] = client.encrypted_secret
 
2330
            for attr in client.client_structure:
 
2331
                if attr not in exclude:
 
2332
                    client_dict[attr] = getattr(client, attr)
 
2333
 
 
2334
            clients.append(client_dict) 
 
2335
            del client_settings[client.name]["secret"]
 
2336
            
 
2337
        try:
 
2338
            with os.fdopen(os.open(stored_state_path,
 
2339
                                   os.O_CREAT|os.O_WRONLY|os.O_TRUNC,
 
2340
                                   stat.S_IRUSR | stat.S_IWUSR),
 
2341
                           "wb") as stored_state:
 
2342
                pickle.dump((clients, client_settings), stored_state)
 
2343
        except IOError as e:
 
2344
            logger.warning("Could not save persistant state: {0}"
 
2345
                           .format(e))
 
2346
            if e.errno != errno.ENOENT:
 
2347
                raise
 
2348
 
 
2349
        # Delete all clients, and settings from config
2061
2350
        while tcp_server.clients:
2062
 
            client = tcp_server.clients.pop()
 
2351
            name, client = tcp_server.clients.popitem()
2063
2352
            if use_dbus:
2064
2353
                client.remove_from_connection()
2065
 
            client.disable_hook = None
2066
2354
            # Don't signal anything except ClientRemoved
2067
2355
            client.disable(quiet=True)
2068
2356
            if use_dbus:
2069
2357
                # Emit D-Bus signal
2070
 
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
2358
                mandos_dbus_service.ClientRemoved(client
 
2359
                                                  .dbus_object_path,
2071
2360
                                                  client.name)
 
2361
        client_settings.clear()
2072
2362
    
2073
2363
    atexit.register(cleanup)
2074
2364
    
2075
 
    for client in tcp_server.clients:
 
2365
    for client in tcp_server.clients.itervalues():
2076
2366
        if use_dbus:
2077
2367
            # Emit D-Bus signal
2078
2368
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2079
 
        client.enable()
 
2369
        # Need to initiate checking of clients
 
2370
        if client.enabled:
 
2371
            client.init_checker()
 
2372
 
2080
2373
    
2081
2374
    tcp_server.enable()
2082
2375
    tcp_server.server_activate()