/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Björn Påhlsson
  • Date: 2011-11-24 19:27:53 UTC
  • mto: (518.2.5 persistent-state-gpgme)
  • mto: This revision was merged to the branch mainline in revision 524.
  • Revision ID: belorn@fukt.bsnet.se-20111124192753-y5jxlc1h3tcxpubc
First run of python-lint. Fixed some *obviously* bad code and turned
them into good code.

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"
 
88
version = "1.4.1"
 
89
stored_state_path = "/var/lib/mandos/clients.pickle"
87
90
 
88
 
#logger = logging.getLogger('mandos')
89
 
logger = logging.Logger('mandos')
 
91
logger = logging.getLogger()
90
92
syslogger = (logging.handlers.SysLogHandler
91
93
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
92
94
              address = str("/dev/log")))
93
 
syslogger.setFormatter(logging.Formatter
94
 
                       ('Mandos [%(process)d]: %(levelname)s:'
95
 
                        ' %(message)s'))
96
 
logger.addHandler(syslogger)
97
 
 
98
 
console = logging.StreamHandler()
99
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
100
 
                                       ' %(levelname)s:'
101
 
                                       ' %(message)s'))
102
 
logger.addHandler(console)
 
95
 
 
96
try:
 
97
    if_nametoindex = (ctypes.cdll.LoadLibrary
 
98
                      (ctypes.util.find_library("c"))
 
99
                      .if_nametoindex)
 
100
except (OSError, AttributeError):
 
101
    def if_nametoindex(interface):
 
102
        "Get an interface index the hard way, i.e. using fcntl()"
 
103
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
 
104
        with contextlib.closing(socket.socket()) as s:
 
105
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
106
                                struct.pack(str("16s16x"),
 
107
                                            interface))
 
108
        interface_index = struct.unpack(str("I"),
 
109
                                        ifreq[16:20])[0]
 
110
        return interface_index
 
111
 
 
112
 
 
113
def initlogger(level=logging.WARNING):
 
114
    """init logger and add loglevel"""
 
115
    
 
116
    syslogger.setFormatter(logging.Formatter
 
117
                           ('Mandos [%(process)d]: %(levelname)s:'
 
118
                            ' %(message)s'))
 
119
    logger.addHandler(syslogger)
 
120
    
 
121
    console = logging.StreamHandler()
 
122
    console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
123
                                           ' [%(process)d]:'
 
124
                                           ' %(levelname)s:'
 
125
                                           ' %(message)s'))
 
126
    logger.addHandler(console)
 
127
    logger.setLevel(level)
 
128
 
103
129
 
104
130
class AvahiError(Exception):
105
131
    def __init__(self, value, *args, **kwargs):
160
186
                            " after %i retries, exiting.",
161
187
                            self.rename_count)
162
188
            raise AvahiServiceError("Too many renames")
163
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
189
        self.name = unicode(self.server
 
190
                            .GetAlternativeServiceName(self.name))
164
191
        logger.info("Changing Zeroconf service name to %r ...",
165
192
                    self.name)
166
 
        syslogger.setFormatter(logging.Formatter
167
 
                               ('Mandos (%s) [%%(process)d]:'
168
 
                                ' %%(levelname)s: %%(message)s'
169
 
                                % self.name))
170
193
        self.remove()
171
194
        try:
172
195
            self.add()
192
215
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
193
216
        self.entry_group_state_changed_match = (
194
217
            self.group.connect_to_signal(
195
 
                'StateChanged', self .entry_group_state_changed))
 
218
                'StateChanged', self.entry_group_state_changed))
196
219
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
197
220
                     self.name, self.type)
198
221
        self.group.AddService(
224
247
            try:
225
248
                self.group.Free()
226
249
            except (dbus.exceptions.UnknownMethodException,
227
 
                    dbus.exceptions.DBusException) as e:
 
250
                    dbus.exceptions.DBusException):
228
251
                pass
229
252
            self.group = None
230
253
        self.remove()
264
287
                                 self.server_state_changed)
265
288
        self.server_state_changed(self.server.GetState())
266
289
 
 
290
class AvahiServiceToSyslog(AvahiService):
 
291
    def rename(self):
 
292
        """Add the new name to the syslog messages"""
 
293
        ret = AvahiService.rename(self)
 
294
        syslogger.setFormatter(logging.Formatter
 
295
                               ('Mandos (%s) [%%(process)d]:'
 
296
                                ' %%(levelname)s: %%(message)s'
 
297
                                % self.name))
 
298
        return ret
267
299
 
268
300
def _timedelta_to_milliseconds(td):
269
301
    "Convert a datetime.timedelta() to milliseconds"
288
320
                     instance %(name)s can be used in the command.
289
321
    checker_initiator_tag: a gobject event source tag, or None
290
322
    created:    datetime.datetime(); (UTC) object creation
 
323
    client_structure: Object describing what attributes a client has
 
324
                      and is used for storing the client at exit
291
325
    current_checker_command: string; current running checker_command
292
 
    disable_hook:  If set, called by disable() as disable_hook(self)
293
326
    disable_initiator_tag: a gobject event source tag, or None
294
327
    enabled:    bool()
295
328
    fingerprint: string (40 or 32 hexadecimal digits); used to
298
331
    interval:   datetime.timedelta(); How often to start a new checker
299
332
    last_approval_request: datetime.datetime(); (UTC) or None
300
333
    last_checked_ok: datetime.datetime(); (UTC) or None
 
334
    last_checker_status: integer between 0 and 255 reflecting exit status
 
335
                         of last checker. -1 reflect crashed checker,
 
336
                         or None.
301
337
    last_enabled: datetime.datetime(); (UTC)
302
338
    name:       string; from the config file, used in log messages and
303
339
                        D-Bus identifiers
321
357
    
322
358
    def extended_timeout_milliseconds(self):
323
359
        "Return the 'extended_timeout' attribute in milliseconds"
324
 
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
360
        return _timedelta_to_milliseconds(self.extended_timeout)
325
361
    
326
362
    def interval_milliseconds(self):
327
363
        "Return the 'interval' attribute in milliseconds"
330
366
    def approval_delay_milliseconds(self):
331
367
        return _timedelta_to_milliseconds(self.approval_delay)
332
368
    
333
 
    def __init__(self, name = None, disable_hook=None, config=None):
 
369
    def __init__(self, name = None, config=None):
334
370
        """Note: the 'checker' key in 'config' sets the
335
371
        'checker_command' attribute and *not* the 'checker'
336
372
        attribute."""
356
392
                            % self.name)
357
393
        self.host = config.get("host", "")
358
394
        self.created = datetime.datetime.utcnow()
359
 
        self.enabled = False
 
395
        self.enabled = True
360
396
        self.last_approval_request = None
361
 
        self.last_enabled = None
 
397
        self.last_enabled = datetime.datetime.utcnow()
362
398
        self.last_checked_ok = None
 
399
        self.last_checker_status = None
363
400
        self.timeout = string_to_delta(config["timeout"])
364
 
        self.extended_timeout = string_to_delta(config["extended_timeout"])
 
401
        self.extended_timeout = string_to_delta(config
 
402
                                                ["extended_timeout"])
365
403
        self.interval = string_to_delta(config["interval"])
366
 
        self.disable_hook = disable_hook
367
404
        self.checker = None
368
405
        self.checker_initiator_tag = None
369
406
        self.disable_initiator_tag = None
370
 
        self.expires = None
 
407
        self.expires = datetime.datetime.utcnow() + self.timeout
371
408
        self.checker_callback_tag = None
372
409
        self.checker_command = config["checker"]
373
410
        self.current_checker_command = None
374
 
        self.last_connect = None
375
411
        self._approved = None
376
412
        self.approved_by_default = config.get("approved_by_default",
377
413
                                              True)
380
416
            config["approval_delay"])
381
417
        self.approval_duration = string_to_delta(
382
418
            config["approval_duration"])
383
 
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
 
419
        self.changedstate = (multiprocessing_manager
 
420
                             .Condition(multiprocessing_manager
 
421
                                        .Lock()))
 
422
        self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
 
423
        self.client_structure.append("client_structure")
 
424
 
 
425
 
 
426
        for name, t in inspect.getmembers(type(self),
 
427
                                          lambda obj: isinstance(obj, property)):
 
428
            if not name.startswith("_"):
 
429
                self.client_structure.append(name)
384
430
    
 
431
    # Send notice to process children that client state has changed
385
432
    def send_changedstate(self):
386
 
        self.changedstate.acquire()
387
 
        self.changedstate.notify_all()
388
 
        self.changedstate.release()
389
 
        
 
433
        with self.changedstate:
 
434
            self.changedstate.notify_all()
 
435
    
390
436
    def enable(self):
391
437
        """Start this client's checker and timeout hooks"""
392
438
        if getattr(self, "enabled", False):
393
439
            # Already enabled
394
440
            return
395
441
        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
442
        self.expires = datetime.datetime.utcnow() + self.timeout
403
 
        self.disable_initiator_tag = (gobject.timeout_add
404
 
                                   (self.timeout_milliseconds(),
405
 
                                    self.disable))
406
443
        self.enabled = True
407
444
        self.last_enabled = datetime.datetime.utcnow()
408
 
        # Also start a new checker *right now*.
409
 
        self.start_checker()
 
445
        self.init_checker()
410
446
    
411
447
    def disable(self, quiet=True):
412
448
        """Disable this client."""
424
460
            gobject.source_remove(self.checker_initiator_tag)
425
461
            self.checker_initiator_tag = None
426
462
        self.stop_checker()
427
 
        if self.disable_hook:
428
 
            self.disable_hook(self)
429
463
        self.enabled = False
430
464
        # Do not run this again if called by a gobject.timeout_add
431
465
        return False
432
466
    
433
467
    def __del__(self):
434
 
        self.disable_hook = None
435
468
        self.disable()
436
 
    
 
469
 
 
470
    def init_checker(self):
 
471
        # Schedule a new checker to be started an 'interval' from now,
 
472
        # and every interval from then on.
 
473
        self.checker_initiator_tag = (gobject.timeout_add
 
474
                                      (self.interval_milliseconds(),
 
475
                                       self.start_checker))
 
476
        # Schedule a disable() when 'timeout' has passed
 
477
        self.disable_initiator_tag = (gobject.timeout_add
 
478
                                   (self.timeout_milliseconds(),
 
479
                                    self.disable))
 
480
        # Also start a new checker *right now*.
 
481
        self.start_checker()
 
482
 
 
483
        
437
484
    def checker_callback(self, pid, condition, command):
438
485
        """The checker has completed, so take appropriate actions."""
439
486
        self.checker_callback_tag = None
440
487
        self.checker = None
441
488
        if os.WIFEXITED(condition):
442
 
            exitstatus = os.WEXITSTATUS(condition)
443
 
            if exitstatus == 0:
 
489
            self.last_checker_status =  os.WEXITSTATUS(condition)
 
490
            if self.last_checker_status == 0:
444
491
                logger.info("Checker for %(name)s succeeded",
445
492
                            vars(self))
446
493
                self.checked_ok()
448
495
                logger.info("Checker for %(name)s failed",
449
496
                            vars(self))
450
497
        else:
 
498
            self.last_checker_status = -1
451
499
            logger.warning("Checker for %(name)s crashed?",
452
500
                           vars(self))
453
501
    
460
508
        if timeout is None:
461
509
            timeout = self.timeout
462
510
        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))
 
511
        if self.disable_initiator_tag is not None:
 
512
            gobject.source_remove(self.disable_initiator_tag)
 
513
        if getattr(self, "enabled", False):
 
514
            self.disable_initiator_tag = (gobject.timeout_add
 
515
                                          (_timedelta_to_milliseconds
 
516
                                           (timeout), self.disable))
 
517
            self.expires = datetime.datetime.utcnow() + timeout
468
518
    
469
519
    def need_approval(self):
470
520
        self.last_approval_request = datetime.datetime.utcnow()
562
612
                raise
563
613
        self.checker = None
564
614
 
 
615
    # Encrypts a client secret and stores it in a varible encrypted_secret
 
616
    def encrypt_secret(self, key):
 
617
        # Encryption-key need to be of a specific size, so we hash inputed key
 
618
        hasheng = hashlib.sha256()
 
619
        hasheng.update(key)
 
620
        encryptionkey = hasheng.digest()
 
621
 
 
622
        # Create validation hash so we know at decryption if it was sucessful
 
623
        hasheng = hashlib.sha256()
 
624
        hasheng.update(self.secret)
 
625
        validationhash = hasheng.digest()
 
626
 
 
627
        # Encrypt secret
 
628
        iv = os.urandom(Crypto.Cipher.AES.block_size)
 
629
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
630
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
631
        ciphertext = ciphereng.encrypt(validationhash+self.secret)
 
632
        self.encrypted_secret = (ciphertext, iv)
 
633
 
 
634
    # Decrypt a encrypted client secret
 
635
    def decrypt_secret(self, key):
 
636
        # Decryption-key need to be of a specific size, so we hash inputed key
 
637
        hasheng = hashlib.sha256()
 
638
        hasheng.update(key)
 
639
        encryptionkey = hasheng.digest()
 
640
 
 
641
        # Decrypt encrypted secret
 
642
        ciphertext, iv = self.encrypted_secret
 
643
        ciphereng = Crypto.Cipher.AES.new(encryptionkey,
 
644
                                        Crypto.Cipher.AES.MODE_CFB, iv)
 
645
        plain = ciphereng.decrypt(ciphertext)
 
646
 
 
647
        # Validate decrypted secret to know if it was succesful
 
648
        hasheng = hashlib.sha256()
 
649
        validationhash = plain[:hasheng.digest_size]
 
650
        secret = plain[hasheng.digest_size:]
 
651
        hasheng.update(secret)
 
652
 
 
653
        # if validation fails, we use key as new secret. Otherwhise, we use
 
654
        # the decrypted secret
 
655
        if hasheng.digest() == validationhash:
 
656
            self.secret = secret
 
657
        else:
 
658
            self.secret = key
 
659
        del self.encrypted_secret
 
660
 
565
661
 
566
662
def dbus_service_property(dbus_interface, signature="v",
567
663
                          access="readwrite", byte_arrays=False):
629
725
        """
630
726
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
727
                for cls in self.__class__.__mro__
632
 
                for name, prop in inspect.getmembers(cls, self._is_dbus_property))
 
728
                for name, prop in
 
729
                inspect.getmembers(cls, self._is_dbus_property))
633
730
    
634
731
    def _get_dbus_property(self, interface_name, property_name):
635
732
        """Returns a bound method if one exists which is a D-Bus
636
733
        property with the specified name and interface.
637
734
        """
638
735
        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:
 
736
            for name, value in (inspect.getmembers
 
737
                                (cls, self._is_dbus_property)):
 
738
                if (value._dbus_name == property_name
 
739
                    and value._dbus_interface == interface_name):
641
740
                    return value.__get__(self)
642
741
        
643
742
        # No such property
682
781
        
683
782
        Note: Will not include properties with access="write".
684
783
        """
685
 
        all = {}
 
784
        properties = {}
686
785
        for name, prop in self._get_all_dbus_properties():
687
786
            if (interface_name
688
787
                and interface_name != prop._dbus_interface):
693
792
                continue
694
793
            value = prop()
695
794
            if not hasattr(value, "variant_level"):
696
 
                all[name] = value
 
795
                properties[name] = value
697
796
                continue
698
 
            all[name] = type(value)(value, variant_level=
699
 
                                    value.variant_level+1)
700
 
        return dbus.Dictionary(all, signature="sv")
 
797
            properties[name] = type(value)(value, variant_level=
 
798
                                           value.variant_level+1)
 
799
        return dbus.Dictionary(properties, signature="sv")
701
800
    
702
801
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
703
802
                         out_signature="s",
754
853
    return dbus.String(dt.isoformat(),
755
854
                       variant_level=variant_level)
756
855
 
757
 
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
 
856
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
 
857
                                  .__metaclass__):
 
858
    """Applied to an empty subclass of a D-Bus object, this metaclass
 
859
    will add additional D-Bus attributes matching a certain pattern.
 
860
    """
758
861
    def __new__(mcs, name, bases, attr):
759
 
        for attrname, old_dbusobj in inspect.getmembers(bases[0]):
760
 
            new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
761
 
            if (getattr(old_dbusobj, "_dbus_is_signal", False)
762
 
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
763
 
                unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
764
 
                                    old_dbusobj.__closure__))["func"].cell_contents
765
 
                newfunc = types.FunctionType(unwrappedfunc.func_code,
766
 
                                             unwrappedfunc.func_globals,
767
 
                                             unwrappedfunc.func_name,
768
 
                                             unwrappedfunc.func_defaults,
769
 
                                             unwrappedfunc.func_closure)
770
 
                new_dbusfunc = dbus.service.signal(
771
 
                    new_interface, old_dbusobj._dbus_signature)(newfunc)            
772
 
                attr["_transitional_" + attrname] = new_dbusfunc
773
 
 
774
 
                def fixscope(func1, func2):
775
 
                    def newcall(*args, **kwargs):
776
 
                        func1(*args, **kwargs)
777
 
                        func2(*args, **kwargs)
778
 
                    return newcall
779
 
 
780
 
                attr[attrname] = fixscope(old_dbusobj, new_dbusfunc)
781
 
            
782
 
            elif (getattr(old_dbusobj, "_dbus_is_method", False)
783
 
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
784
 
                new_dbusfunc = (dbus.service.method
785
 
                                (new_interface,
786
 
                                 old_dbusobj._dbus_in_signature,
787
 
                                 old_dbusobj._dbus_out_signature)
788
 
                                (types.FunctionType
789
 
                                 (old_dbusobj.func_code,
790
 
                                  old_dbusobj.func_globals,
791
 
                                  old_dbusobj.func_name,
792
 
                                  old_dbusobj.func_defaults,
793
 
                                  old_dbusobj.func_closure)))
794
 
 
795
 
                attr[attrname] = new_dbusfunc
796
 
            elif (getattr(old_dbusobj, "_dbus_is_property", False)
797
 
                  and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
798
 
                new_dbusfunc = (dbus_service_property
799
 
                                (new_interface,
800
 
                                 old_dbusobj._dbus_signature,
801
 
                                 old_dbusobj._dbus_access,
802
 
                                 old_dbusobj._dbus_get_args_options["byte_arrays"])
803
 
                                (types.FunctionType
804
 
                                 (old_dbusobj.func_code,
805
 
                                  old_dbusobj.func_globals,
806
 
                                  old_dbusobj.func_name,
807
 
                                  old_dbusobj.func_defaults,
808
 
                                  old_dbusobj.func_closure)))
809
 
 
810
 
                attr[attrname] = new_dbusfunc
 
862
        # Go through all the base classes which could have D-Bus
 
863
        # methods, signals, or properties in them
 
864
        for base in (b for b in bases
 
865
                     if issubclass(b, dbus.service.Object)):
 
866
            # Go though all attributes of the base class
 
867
            for attrname, attribute in inspect.getmembers(base):
 
868
                # Ignore non-D-Bus attributes, and D-Bus attributes
 
869
                # with the wrong interface name
 
870
                if (not hasattr(attribute, "_dbus_interface")
 
871
                    or not attribute._dbus_interface
 
872
                    .startswith("se.recompile.Mandos")):
 
873
                    continue
 
874
                # Create an alternate D-Bus interface name based on
 
875
                # the current name
 
876
                alt_interface = (attribute._dbus_interface
 
877
                                 .replace("se.recompile.Mandos",
 
878
                                          "se.bsnet.fukt.Mandos"))
 
879
                # Is this a D-Bus signal?
 
880
                if getattr(attribute, "_dbus_is_signal", False):
 
881
                    # Extract the original non-method function by
 
882
                    # black magic
 
883
                    nonmethod_func = (dict(
 
884
                            zip(attribute.func_code.co_freevars,
 
885
                                attribute.__closure__))["func"]
 
886
                                      .cell_contents)
 
887
                    # Create a new, but exactly alike, function
 
888
                    # object, and decorate it to be a new D-Bus signal
 
889
                    # with the alternate D-Bus interface name
 
890
                    new_function = (dbus.service.signal
 
891
                                    (alt_interface,
 
892
                                     attribute._dbus_signature)
 
893
                                    (types.FunctionType(
 
894
                                nonmethod_func.func_code,
 
895
                                nonmethod_func.func_globals,
 
896
                                nonmethod_func.func_name,
 
897
                                nonmethod_func.func_defaults,
 
898
                                nonmethod_func.func_closure)))
 
899
                    # Define a creator of a function to call both the
 
900
                    # old and new functions, so both the old and new
 
901
                    # signals gets sent when the function is called
 
902
                    def fixscope(func1, func2):
 
903
                        """This function is a scope container to pass
 
904
                        func1 and func2 to the "call_both" function
 
905
                        outside of its arguments"""
 
906
                        def call_both(*args, **kwargs):
 
907
                            """This function will emit two D-Bus
 
908
                            signals by calling func1 and func2"""
 
909
                            func1(*args, **kwargs)
 
910
                            func2(*args, **kwargs)
 
911
                        return call_both
 
912
                    # Create the "call_both" function and add it to
 
913
                    # the class
 
914
                    attr[attrname] = fixscope(attribute,
 
915
                                              new_function)
 
916
                # Is this a D-Bus method?
 
917
                elif getattr(attribute, "_dbus_is_method", False):
 
918
                    # Create a new, but exactly alike, function
 
919
                    # object.  Decorate it to be a new D-Bus method
 
920
                    # with the alternate D-Bus interface name.  Add it
 
921
                    # to the class.
 
922
                    attr[attrname] = (dbus.service.method
 
923
                                      (alt_interface,
 
924
                                       attribute._dbus_in_signature,
 
925
                                       attribute._dbus_out_signature)
 
926
                                      (types.FunctionType
 
927
                                       (attribute.func_code,
 
928
                                        attribute.func_globals,
 
929
                                        attribute.func_name,
 
930
                                        attribute.func_defaults,
 
931
                                        attribute.func_closure)))
 
932
                # Is this a D-Bus property?
 
933
                elif getattr(attribute, "_dbus_is_property", False):
 
934
                    # Create a new, but exactly alike, function
 
935
                    # object, and decorate it to be a new D-Bus
 
936
                    # property with the alternate D-Bus interface
 
937
                    # name.  Add it to the class.
 
938
                    attr[attrname] = (dbus_service_property
 
939
                                      (alt_interface,
 
940
                                       attribute._dbus_signature,
 
941
                                       attribute._dbus_access,
 
942
                                       attribute
 
943
                                       ._dbus_get_args_options
 
944
                                       ["byte_arrays"])
 
945
                                      (types.FunctionType
 
946
                                       (attribute.func_code,
 
947
                                        attribute.func_globals,
 
948
                                        attribute.func_name,
 
949
                                        attribute.func_defaults,
 
950
                                        attribute.func_closure)))
811
951
        return type.__new__(mcs, name, bases, attr)
812
952
 
813
953
class ClientDBus(Client, DBusObjectWithProperties):
824
964
    # dbus.service.Object doesn't use super(), so we can't either.
825
965
    
826
966
    def __init__(self, bus = None, *args, **kwargs):
 
967
        self.bus = bus
 
968
        Client.__init__(self, *args, **kwargs)
 
969
 
827
970
        self._approvals_pending = 0
828
 
        self.bus = bus
829
 
        Client.__init__(self, *args, **kwargs)
830
971
        # Only now, when this client is initialized, can it show up on
831
972
        # the D-Bus
832
973
        client_object_name = unicode(self.name).translate(
840
981
    def notifychangeproperty(transform_func,
841
982
                             dbus_name, type_func=lambda x: x,
842
983
                             variant_level=1):
843
 
        """ Modify a variable so that its a property that announce its
844
 
        changes to DBus.
845
 
        transform_fun: Function that takes a value and transform it to
846
 
                       DBus type.
847
 
        dbus_name: DBus name of the variable
 
984
        """ Modify a variable so that it's a property which announces
 
985
        its changes to DBus.
 
986
 
 
987
        transform_fun: Function that takes a value and a variant_level
 
988
                       and transforms it to a D-Bus type.
 
989
        dbus_name: D-Bus name of the variable
848
990
        type_func: Function that transform the value before sending it
849
 
                   to DBus
850
 
        variant_level: DBus variant level. default: 1
 
991
                   to the D-Bus.  Default: no transform
 
992
        variant_level: D-Bus variant level.  Default: 1
851
993
        """
852
 
        real_value = [None,]
 
994
        attrname = "_{0}".format(dbus_name)
853
995
        def setter(self, value):
854
 
            old_value = real_value[0]
855
 
            real_value[0] = value
856
996
            if hasattr(self, "dbus_object_path"):
857
 
                if type_func(old_value) != type_func(real_value[0]):
858
 
                    dbus_value = transform_func(type_func(real_value[0]),
859
 
                                                variant_level)
 
997
                if (not hasattr(self, attrname) or
 
998
                    type_func(getattr(self, attrname, None))
 
999
                    != type_func(value)):
 
1000
                    dbus_value = transform_func(type_func(value),
 
1001
                                                variant_level
 
1002
                                                =variant_level)
860
1003
                    self.PropertyChanged(dbus.String(dbus_name),
861
1004
                                         dbus_value)
 
1005
            setattr(self, attrname, value)
862
1006
        
863
 
        return property(lambda self: real_value[0], setter)
 
1007
        return property(lambda self: getattr(self, attrname), setter)
864
1008
    
865
1009
    
866
1010
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
871
1015
    last_enabled = notifychangeproperty(datetime_to_dbus,
872
1016
                                        "LastEnabled")
873
1017
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
874
 
                                   type_func = lambda checker: checker is not None)
 
1018
                                   type_func = lambda checker:
 
1019
                                       checker is not None)
875
1020
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
876
1021
                                           "LastCheckedOK")
877
 
    last_approval_request = notifychangeproperty(datetime_to_dbus,
878
 
                                                 "LastApprovalRequest")
 
1022
    last_approval_request = notifychangeproperty(
 
1023
        datetime_to_dbus, "LastApprovalRequest")
879
1024
    approved_by_default = notifychangeproperty(dbus.Boolean,
880
1025
                                               "ApprovedByDefault")
881
 
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
882
 
                                          type_func = _timedelta_to_milliseconds)
883
 
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
884
 
                                             type_func = _timedelta_to_milliseconds)
 
1026
    approval_delay = notifychangeproperty(dbus.UInt16,
 
1027
                                          "ApprovalDelay",
 
1028
                                          type_func =
 
1029
                                          _timedelta_to_milliseconds)
 
1030
    approval_duration = notifychangeproperty(
 
1031
        dbus.UInt16, "ApprovalDuration",
 
1032
        type_func = _timedelta_to_milliseconds)
885
1033
    host = notifychangeproperty(dbus.String, "Host")
886
1034
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
887
 
                                   type_func = _timedelta_to_milliseconds)
888
 
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
889
 
                                            type_func = _timedelta_to_milliseconds)
890
 
    interval = notifychangeproperty(dbus.UInt16, "Interval",
891
 
                                    type_func = _timedelta_to_milliseconds)
 
1035
                                   type_func =
 
1036
                                   _timedelta_to_milliseconds)
 
1037
    extended_timeout = notifychangeproperty(
 
1038
        dbus.UInt16, "ExtendedTimeout",
 
1039
        type_func = _timedelta_to_milliseconds)
 
1040
    interval = notifychangeproperty(dbus.UInt16,
 
1041
                                    "Interval",
 
1042
                                    type_func =
 
1043
                                    _timedelta_to_milliseconds)
892
1044
    checker_command = notifychangeproperty(dbus.String, "Checker")
893
1045
    
894
1046
    del notifychangeproperty
948
1100
    
949
1101
    
950
1102
    ## D-Bus methods, signals & properties
951
 
    _interface = "se.bsnet.fukt.Mandos.Client"
 
1103
    _interface = "se.recompile.Mandos.Client"
952
1104
    
953
1105
    ## Signals
954
1106
    
991
1143
        "D-Bus signal"
992
1144
        return self.need_approval()
993
1145
    
 
1146
    # NeRwequest - signal
 
1147
    @dbus.service.signal(_interface, signature="s")
 
1148
    def NewRequest(self, ip):
 
1149
        """D-Bus signal
 
1150
        Is sent after a client request a password.
 
1151
        """
 
1152
        pass
 
1153
 
994
1154
    ## Methods
995
1155
    
996
1156
    # Approve - method
1129
1289
        gobject.source_remove(self.disable_initiator_tag)
1130
1290
        self.disable_initiator_tag = None
1131
1291
        self.expires = None
1132
 
        time_to_die = (self.
1133
 
                       _timedelta_to_milliseconds((self
1134
 
                                                   .last_checked_ok
1135
 
                                                   + self.timeout)
1136
 
                                                  - datetime.datetime
1137
 
                                                  .utcnow()))
 
1292
        time_to_die = _timedelta_to_milliseconds((self
 
1293
                                                  .last_checked_ok
 
1294
                                                  + self.timeout)
 
1295
                                                 - datetime.datetime
 
1296
                                                 .utcnow())
1138
1297
        if time_to_die <= 0:
1139
1298
            # The timeout has passed
1140
1299
            self.disable()
1141
1300
        else:
1142
1301
            self.expires = (datetime.datetime.utcnow()
1143
 
                            + datetime.timedelta(milliseconds = time_to_die))
 
1302
                            + datetime.timedelta(milliseconds =
 
1303
                                                 time_to_die))
1144
1304
            self.disable_initiator_tag = (gobject.timeout_add
1145
1305
                                          (time_to_die, self.disable))
1146
1306
    
1226
1386
        self._pipe.send(('setattr', name, value))
1227
1387
 
1228
1388
class ClientDBusTransitional(ClientDBus):
1229
 
    __metaclass__ = transitional_dbus_metaclass
 
1389
    __metaclass__ = AlternateDBusNamesMetaclass
1230
1390
 
1231
1391
class ClientHandler(socketserver.BaseRequestHandler, object):
1232
1392
    """A class to handle client connections.
1294
1454
                    logger.warning("Bad certificate: %s", error)
1295
1455
                    return
1296
1456
                logger.debug("Fingerprint: %s", fpr)
 
1457
                if self.server.use_dbus:
 
1458
                    # Emit D-Bus signal
 
1459
                    client.NewRequest(str(self.client_address))
1297
1460
                
1298
1461
                try:
1299
1462
                    client = ProxyClient(child_pipe, fpr,
1312
1475
                                       client.name)
1313
1476
                        if self.server.use_dbus:
1314
1477
                            # Emit D-Bus signal
1315
 
                            client.Rejected("Disabled")                    
 
1478
                            client.Rejected("Disabled")
1316
1479
                        return
1317
1480
                    
1318
1481
                    if client._approved or not client.approval_delay:
1335
1498
                        return
1336
1499
                    
1337
1500
                    #wait until timeout or approved
1338
 
                    #x = float(client._timedelta_to_milliseconds(delay))
1339
1501
                    time = datetime.datetime.now()
1340
1502
                    client.changedstate.acquire()
1341
 
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1503
                    (client.changedstate.wait
 
1504
                     (float(client._timedelta_to_milliseconds(delay)
 
1505
                            / 1000)))
1342
1506
                    client.changedstate.release()
1343
1507
                    time2 = datetime.datetime.now()
1344
1508
                    if (time2 - time) >= delay:
1368
1532
                    sent_size += sent
1369
1533
                
1370
1534
                logger.info("Sending secret to %s", client.name)
1371
 
                # bump the timeout as if seen
 
1535
                # bump the timeout using extended_timeout
1372
1536
                client.checked_ok(client.extended_timeout)
1373
1537
                if self.server.use_dbus:
1374
1538
                    # Emit D-Bus signal
1454
1618
        except:
1455
1619
            self.handle_error(request, address)
1456
1620
        self.close_request(request)
1457
 
            
 
1621
    
1458
1622
    def process_request(self, request, address):
1459
1623
        """Start a new process to process the request."""
1460
 
        multiprocessing.Process(target = self.sub_process_main,
1461
 
                                args = (request, address)).start()
 
1624
        proc = multiprocessing.Process(target = self.sub_process_main,
 
1625
                                       args = (request,
 
1626
                                               address))
 
1627
        proc.start()
 
1628
        return proc
1462
1629
 
1463
1630
 
1464
1631
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1470
1637
        """
1471
1638
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1472
1639
        
1473
 
        super(MultiprocessingMixInWithPipe,
1474
 
              self).process_request(request, client_address)
 
1640
        proc = MultiprocessingMixIn.process_request(self, request,
 
1641
                                                    client_address)
1475
1642
        self.child_pipe.close()
1476
 
        self.add_pipe(parent_pipe)
 
1643
        self.add_pipe(parent_pipe, proc)
1477
1644
    
1478
 
    def add_pipe(self, parent_pipe):
 
1645
    def add_pipe(self, parent_pipe, proc):
1479
1646
        """Dummy function; override as necessary"""
1480
1647
        raise NotImplementedError
1481
1648
 
1559
1726
        self.enabled = False
1560
1727
        self.clients = clients
1561
1728
        if self.clients is None:
1562
 
            self.clients = set()
 
1729
            self.clients = {}
1563
1730
        self.use_dbus = use_dbus
1564
1731
        self.gnutls_priority = gnutls_priority
1565
1732
        IPv6_TCPServer.__init__(self, server_address,
1569
1736
    def server_activate(self):
1570
1737
        if self.enabled:
1571
1738
            return socketserver.TCPServer.server_activate(self)
 
1739
    
1572
1740
    def enable(self):
1573
1741
        self.enabled = True
1574
 
    def add_pipe(self, parent_pipe):
 
1742
    
 
1743
    def add_pipe(self, parent_pipe, proc):
1575
1744
        # Call "handle_ipc" for both data and EOF events
1576
1745
        gobject.io_add_watch(parent_pipe.fileno(),
1577
1746
                             gobject.IO_IN | gobject.IO_HUP,
1578
1747
                             functools.partial(self.handle_ipc,
1579
 
                                               parent_pipe = parent_pipe))
1580
 
        
 
1748
                                               parent_pipe =
 
1749
                                               parent_pipe,
 
1750
                                               proc = proc))
 
1751
    
1581
1752
    def handle_ipc(self, source, condition, parent_pipe=None,
1582
 
                   client_object=None):
 
1753
                   proc = None, client_object=None):
1583
1754
        condition_names = {
1584
1755
            gobject.IO_IN: "IN",   # There is data to read.
1585
1756
            gobject.IO_OUT: "OUT", # Data can be written (without
1594
1765
                                       for cond, name in
1595
1766
                                       condition_names.iteritems()
1596
1767
                                       if cond & condition)
1597
 
        # error or the other end of multiprocessing.Pipe has closed
 
1768
        # error, or the other end of multiprocessing.Pipe has closed
1598
1769
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1770
            # Wait for other process to exit
 
1771
            proc.join()
1599
1772
            return False
1600
1773
        
1601
1774
        # Read a request from the child
1606
1779
            fpr = request[1]
1607
1780
            address = request[2]
1608
1781
            
1609
 
            for c in self.clients:
 
1782
            for c in self.clients.itervalues():
1610
1783
                if c.fingerprint == fpr:
1611
1784
                    client = c
1612
1785
                    break
1615
1788
                            "dress: %s", fpr, address)
1616
1789
                if self.use_dbus:
1617
1790
                    # Emit D-Bus signal
1618
 
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
 
1791
                    mandos_dbus_service.ClientNotFound(fpr,
 
1792
                                                       address[0])
1619
1793
                parent_pipe.send(False)
1620
1794
                return False
1621
1795
            
1622
1796
            gobject.io_add_watch(parent_pipe.fileno(),
1623
1797
                                 gobject.IO_IN | gobject.IO_HUP,
1624
1798
                                 functools.partial(self.handle_ipc,
1625
 
                                                   parent_pipe = parent_pipe,
1626
 
                                                   client_object = client))
 
1799
                                                   parent_pipe =
 
1800
                                                   parent_pipe,
 
1801
                                                   proc = proc,
 
1802
                                                   client_object =
 
1803
                                                   client))
1627
1804
            parent_pipe.send(True)
1628
 
            # remove the old hook in favor of the new above hook on same fileno
 
1805
            # remove the old hook in favor of the new above hook on
 
1806
            # same fileno
1629
1807
            return False
1630
1808
        if command == 'funcall':
1631
1809
            funcname = request[1]
1632
1810
            args = request[2]
1633
1811
            kwargs = request[3]
1634
1812
            
1635
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1813
            parent_pipe.send(('data', getattr(client_object,
 
1814
                                              funcname)(*args,
 
1815
                                                         **kwargs)))
1636
1816
        
1637
1817
        if command == 'getattr':
1638
1818
            attrname = request[1]
1639
1819
            if callable(client_object.__getattribute__(attrname)):
1640
1820
                parent_pipe.send(('function',))
1641
1821
            else:
1642
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1822
                parent_pipe.send(('data', client_object
 
1823
                                  .__getattribute__(attrname)))
1643
1824
        
1644
1825
        if command == 'setattr':
1645
1826
            attrname = request[1]
1688
1869
    return timevalue
1689
1870
 
1690
1871
 
1691
 
def if_nametoindex(interface):
1692
 
    """Call the C function if_nametoindex(), or equivalent
1693
 
    
1694
 
    Note: This function cannot accept a unicode string."""
1695
 
    global if_nametoindex
1696
 
    try:
1697
 
        if_nametoindex = (ctypes.cdll.LoadLibrary
1698
 
                          (ctypes.util.find_library("c"))
1699
 
                          .if_nametoindex)
1700
 
    except (OSError, AttributeError):
1701
 
        logger.warning("Doing if_nametoindex the hard way")
1702
 
        def if_nametoindex(interface):
1703
 
            "Get an interface index the hard way, i.e. using fcntl()"
1704
 
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
1705
 
            with contextlib.closing(socket.socket()) as s:
1706
 
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1707
 
                                    struct.pack(str("16s16x"),
1708
 
                                                interface))
1709
 
            interface_index = struct.unpack(str("I"),
1710
 
                                            ifreq[16:20])[0]
1711
 
            return interface_index
1712
 
    return if_nametoindex(interface)
1713
 
 
1714
 
 
1715
1872
def daemon(nochdir = False, noclose = False):
1716
1873
    """See daemon(3).  Standard BSD Unix function.
1717
1874
    
1772
1929
                        " system bus interface")
1773
1930
    parser.add_argument("--no-ipv6", action="store_false",
1774
1931
                        dest="use_ipv6", help="Do not use IPv6")
 
1932
    parser.add_argument("--no-restore", action="store_false",
 
1933
                        dest="restore", help="Do not restore stored state",
 
1934
                        default=True)
 
1935
 
1775
1936
    options = parser.parse_args()
1776
1937
    
1777
1938
    if options.check:
1812
1973
    # options, if set.
1813
1974
    for option in ("interface", "address", "port", "debug",
1814
1975
                   "priority", "servicename", "configdir",
1815
 
                   "use_dbus", "use_ipv6", "debuglevel"):
 
1976
                   "use_dbus", "use_ipv6", "debuglevel", "restore"):
1816
1977
        value = getattr(options, option)
1817
1978
        if value is not None:
1818
1979
            server_settings[option] = value
1831
1992
    use_dbus = server_settings["use_dbus"]
1832
1993
    use_ipv6 = server_settings["use_ipv6"]
1833
1994
    
 
1995
    if debug:
 
1996
        initlogger(logging.DEBUG)
 
1997
    else:
 
1998
        if not debuglevel:
 
1999
            initlogger()
 
2000
        else:
 
2001
            level = getattr(logging, debuglevel.upper())
 
2002
            initlogger(level)    
 
2003
    
1834
2004
    if server_settings["servicename"] != "Mandos":
1835
2005
        syslogger.setFormatter(logging.Formatter
1836
2006
                               ('Mandos (%s) [%%(process)d]:'
1890
2060
        if error[0] != errno.EPERM:
1891
2061
            raise error
1892
2062
    
1893
 
    if not debug and not debuglevel:
1894
 
        syslogger.setLevel(logging.WARNING)
1895
 
        console.setLevel(logging.WARNING)
1896
 
    if debuglevel:
1897
 
        level = getattr(logging, debuglevel.upper())
1898
 
        syslogger.setLevel(level)
1899
 
        console.setLevel(level)
1900
 
    
1901
2063
    if debug:
1902
2064
        # Enable all possible GnuTLS debugging
1903
2065
        
1936
2098
        try:
1937
2099
            bus_name = dbus.service.BusName("se.recompile.Mandos",
1938
2100
                                            bus, do_not_queue=True)
1939
 
            bus_name_transitional = dbus.service.BusName("se.bsnet.fukt.Mandos",
1940
 
                                                         bus, do_not_queue=True)
 
2101
            old_bus_name = (dbus.service.BusName
 
2102
                            ("se.bsnet.fukt.Mandos", bus,
 
2103
                             do_not_queue=True))
1941
2104
        except dbus.exceptions.NameExistsException as e:
1942
2105
            logger.error(unicode(e) + ", disabling D-Bus")
1943
2106
            use_dbus = False
1944
2107
            server_settings["use_dbus"] = False
1945
2108
            tcp_server.use_dbus = False
1946
2109
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1947
 
    service = AvahiService(name = server_settings["servicename"],
1948
 
                           servicetype = "_mandos._tcp",
1949
 
                           protocol = protocol, bus = bus)
 
2110
    service = AvahiServiceToSyslog(name =
 
2111
                                   server_settings["servicename"],
 
2112
                                   servicetype = "_mandos._tcp",
 
2113
                                   protocol = protocol, bus = bus)
1950
2114
    if server_settings["interface"]:
1951
2115
        service.interface = (if_nametoindex
1952
2116
                             (str(server_settings["interface"])))
1956
2120
    
1957
2121
    client_class = Client
1958
2122
    if use_dbus:
1959
 
        client_class = functools.partial(ClientDBusTransitional, bus = bus)        
1960
 
    def client_config_items(config, section):
1961
 
        special_settings = {
1962
 
            "approved_by_default":
1963
 
                lambda: config.getboolean(section,
1964
 
                                          "approved_by_default"),
1965
 
            }
1966
 
        for name, value in config.items(section):
 
2123
        client_class = functools.partial(ClientDBusTransitional,
 
2124
                                         bus = bus)
 
2125
    
 
2126
    special_settings = {
 
2127
        # Some settings need to be accessd by special methods;
 
2128
        # booleans need .getboolean(), etc.  Here is a list of them:
 
2129
        "approved_by_default":
 
2130
            lambda section:
 
2131
            client_config.getboolean(section, "approved_by_default"),
 
2132
        }
 
2133
    # Construct a new dict of client settings of this form:
 
2134
    # { client_name: {setting_name: value, ...}, ...}
 
2135
    # with exceptions for any special settings as defined above
 
2136
    client_settings = dict((clientname,
 
2137
                           dict((setting,
 
2138
                                 (value if setting not in special_settings
 
2139
                                  else special_settings[setting](clientname)))
 
2140
                                for setting, value in client_config.items(clientname)))
 
2141
                          for clientname in client_config.sections())
 
2142
    
 
2143
    old_client_settings = {}
 
2144
    clients_data = []
 
2145
 
 
2146
    # Get client data and settings from last running state. 
 
2147
    if server_settings["restore"]:
 
2148
        try:
 
2149
            with open(stored_state_path, "rb") as stored_state:
 
2150
                clients_data, old_client_settings = pickle.load(stored_state)
 
2151
            os.remove(stored_state_path)
 
2152
        except IOError as e:
 
2153
            logger.warning("Could not load persistant state: {0}".format(e))
 
2154
            if e.errno != errno.ENOENT:
 
2155
                raise
 
2156
 
 
2157
    for client in clients_data:
 
2158
        client_name = client["name"]
 
2159
        
 
2160
        # Decide which value to use after restoring saved state.
 
2161
        # We have three different values: Old config file,
 
2162
        # new config file, and saved state.
 
2163
        # New config value takes precedence if it differs from old
 
2164
        # config value, otherwise use saved state.
 
2165
        for name, value in client_settings[client_name].items():
1967
2166
            try:
1968
 
                yield (name, special_settings[name]())
 
2167
                # For each value in new config, check if it differs
 
2168
                # from the old config value (Except for the "secret"
 
2169
                # attribute)
 
2170
                if name != "secret" and value != old_client_settings[client_name][name]:
 
2171
                    setattr(client, name, value)
1969
2172
            except KeyError:
1970
 
                yield (name, value)
 
2173
                pass
 
2174
 
 
2175
        # Clients who has passed its expire date, can still be enabled if its
 
2176
        # last checker was sucessful. Clients who checkers failed before we
 
2177
        # stored it state is asumed to had failed checker during downtime.
 
2178
        if client["enabled"] and client["last_checked_ok"]:
 
2179
            if ((datetime.datetime.utcnow() - client["last_checked_ok"])
 
2180
                > client["interval"]):
 
2181
                if client["last_checker_status"] != 0:
 
2182
                    client["enabled"] = False
 
2183
                else:
 
2184
                    client["expires"] = datetime.datetime.utcnow() + client["timeout"]
 
2185
 
 
2186
        client["changedstate"] = (multiprocessing_manager
 
2187
                                  .Condition(multiprocessing_manager
 
2188
                                             .Lock()))
 
2189
        if use_dbus:
 
2190
            new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
 
2191
            tcp_server.clients[client_name] = new_client
 
2192
            new_client.bus = bus
 
2193
            for name, value in client.iteritems():
 
2194
                setattr(new_client, name, value)
 
2195
            client_object_name = unicode(client_name).translate(
 
2196
                {ord("."): ord("_"),
 
2197
                 ord("-"): ord("_")})
 
2198
            new_client.dbus_object_path = (dbus.ObjectPath
 
2199
                                     ("/clients/" + client_object_name))
 
2200
            DBusObjectWithProperties.__init__(new_client,
 
2201
                                              new_client.bus,
 
2202
                                              new_client.dbus_object_path)
 
2203
        else:
 
2204
            tcp_server.clients[client_name] = Client.__new__(Client)
 
2205
            for name, value in client.iteritems():
 
2206
                setattr(tcp_server.clients[client_name], name, value)
 
2207
                
 
2208
        tcp_server.clients[client_name].decrypt_secret(
 
2209
            client_settings[client_name]["secret"])            
 
2210
        
 
2211
    # Create/remove clients based on new changes made to config
 
2212
    for clientname in set(old_client_settings) - set(client_settings):
 
2213
        del tcp_server.clients[clientname]
 
2214
    for clientname in set(client_settings) - set(old_client_settings):
 
2215
        tcp_server.clients[clientname] = (client_class(name = clientname,
 
2216
                                                       config =
 
2217
                                                       client_settings
 
2218
                                                       [clientname]))
1971
2219
    
1972
 
    tcp_server.clients.update(set(
1973
 
            client_class(name = section,
1974
 
                         config= dict(client_config_items(
1975
 
                        client_config, section)))
1976
 
            for section in client_config.sections()))
 
2220
 
1977
2221
    if not tcp_server.clients:
1978
2222
        logger.warning("No clients defined")
1979
2223
        
2001
2245
            """A D-Bus proxy object"""
2002
2246
            def __init__(self):
2003
2247
                dbus.service.Object.__init__(self, bus, "/")
2004
 
            _interface = "se.bsnet.fukt.Mandos"
 
2248
            _interface = "se.recompile.Mandos"
2005
2249
            
2006
2250
            @dbus.service.signal(_interface, signature="o")
2007
2251
            def ClientAdded(self, objpath):
2022
2266
            def GetAllClients(self):
2023
2267
                "D-Bus method"
2024
2268
                return dbus.Array(c.dbus_object_path
2025
 
                                  for c in tcp_server.clients)
 
2269
                                  for c in
 
2270
                                  tcp_server.clients.itervalues())
2026
2271
            
2027
2272
            @dbus.service.method(_interface,
2028
2273
                                 out_signature="a{oa{sv}}")
2030
2275
                "D-Bus method"
2031
2276
                return dbus.Dictionary(
2032
2277
                    ((c.dbus_object_path, c.GetAll(""))
2033
 
                     for c in tcp_server.clients),
 
2278
                     for c in tcp_server.clients.itervalues()),
2034
2279
                    signature="oa{sv}")
2035
2280
            
2036
2281
            @dbus.service.method(_interface, in_signature="o")
2037
2282
            def RemoveClient(self, object_path):
2038
2283
                "D-Bus method"
2039
 
                for c in tcp_server.clients:
 
2284
                for c in tcp_server.clients.itervalues():
2040
2285
                    if c.dbus_object_path == object_path:
2041
 
                        tcp_server.clients.remove(c)
 
2286
                        del tcp_server.clients[c.name]
2042
2287
                        c.remove_from_connection()
2043
2288
                        # Don't signal anything except ClientRemoved
2044
2289
                        c.disable(quiet=True)
2050
2295
            del _interface
2051
2296
        
2052
2297
        class MandosDBusServiceTransitional(MandosDBusService):
2053
 
            __metaclass__ = transitional_dbus_metaclass
 
2298
            __metaclass__ = AlternateDBusNamesMetaclass
2054
2299
        mandos_dbus_service = MandosDBusServiceTransitional()
2055
2300
    
2056
2301
    def cleanup():
2057
2302
        "Cleanup function; run on exit"
2058
2303
        service.cleanup()
2059
2304
        
 
2305
        multiprocessing.active_children()
 
2306
        if not (tcp_server.clients or client_settings):
 
2307
            return
 
2308
 
 
2309
        # Store client before exiting. Secrets are encrypted with key based
 
2310
        # on what config file has. If config file is removed/edited, old
 
2311
        # secret will thus be unrecovable.
 
2312
        clients = []
 
2313
        for client in tcp_server.clients.itervalues():
 
2314
            client.encrypt_secret(client_settings[client.name]["secret"])
 
2315
 
 
2316
            client_dict = {}
 
2317
 
 
2318
            # A list of attributes that will not be stored when shuting down.
 
2319
            exclude = set(("bus", "changedstate", "secret"))            
 
2320
            for name, typ in inspect.getmembers(dbus.service.Object):
 
2321
                exclude.add(name)
 
2322
                
 
2323
            client_dict["encrypted_secret"] = client.encrypted_secret
 
2324
            for attr in client.client_structure:
 
2325
                if attr not in exclude:
 
2326
                    client_dict[attr] = getattr(client, attr)
 
2327
 
 
2328
            clients.append(client_dict) 
 
2329
            del client_settings[client.name]["secret"]
 
2330
            
 
2331
        try:
 
2332
            with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
 
2333
                pickle.dump((clients, client_settings), stored_state)
 
2334
        except IOError as e:
 
2335
            logger.warning("Could not save persistant state: {0}".format(e))
 
2336
            if e.errno != errno.ENOENT:
 
2337
                raise
 
2338
 
 
2339
        # Delete all clients, and settings from config
2060
2340
        while tcp_server.clients:
2061
 
            client = tcp_server.clients.pop()
 
2341
            name, client = tcp_server.clients.popitem()
2062
2342
            if use_dbus:
2063
2343
                client.remove_from_connection()
2064
 
            client.disable_hook = None
2065
2344
            # Don't signal anything except ClientRemoved
2066
2345
            client.disable(quiet=True)
2067
2346
            if use_dbus:
2068
2347
                # Emit D-Bus signal
2069
 
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
2348
                mandos_dbus_service.ClientRemoved(client
 
2349
                                                  .dbus_object_path,
2070
2350
                                                  client.name)
 
2351
        client_settings.clear()
2071
2352
    
2072
2353
    atexit.register(cleanup)
2073
2354
    
2074
 
    for client in tcp_server.clients:
 
2355
    for client in tcp_server.clients.itervalues():
2075
2356
        if use_dbus:
2076
2357
            # Emit D-Bus signal
2077
2358
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
2078
 
        client.enable()
 
2359
        # Need to initiate checking of clients
 
2360
        if client.enabled:
 
2361
            client.init_checker()
 
2362
 
2079
2363
    
2080
2364
    tcp_server.enable()
2081
2365
    tcp_server.server_activate()