/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 Hogeborn
  • Date: 2011-09-19 09:42:55 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110919094255-3lji3igz75uog8lh
* mandos-clients.conf.xml (OPTIONS): Moved up "extended_timeout" to
                                     order options alphabetically.
* clients.conf: Language change in comment.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
# along with this program.  If not, see
29
29
# <http://www.gnu.org/licenses/>.
30
30
31
 
# Contact the authors at <mandos@recompile.se>.
 
31
# Contact the authors at <mandos@fukt.bsnet.se>.
32
32
33
33
 
34
34
from __future__ import (division, absolute_import, print_function,
62
62
import functools
63
63
import cPickle as pickle
64
64
import multiprocessing
65
 
import types
66
65
 
67
66
import dbus
68
67
import dbus.service
83
82
        SO_BINDTODEVICE = None
84
83
 
85
84
 
86
 
version = "1.4.1"
 
85
version = "1.3.1"
87
86
 
88
87
#logger = logging.getLogger('mandos')
89
88
logger = logging.Logger('mandos')
160
159
                            " after %i retries, exiting.",
161
160
                            self.rename_count)
162
161
            raise AvahiServiceError("Too many renames")
163
 
        self.name = unicode(self.server
164
 
                            .GetAlternativeServiceName(self.name))
 
162
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
165
163
        logger.info("Changing Zeroconf service name to %r ...",
166
164
                    self.name)
167
165
        syslogger.setFormatter(logging.Formatter
266
264
        self.server_state_changed(self.server.GetState())
267
265
 
268
266
 
269
 
def _timedelta_to_milliseconds(td):
270
 
    "Convert a datetime.timedelta() to milliseconds"
271
 
    return ((td.days * 24 * 60 * 60 * 1000)
272
 
            + (td.seconds * 1000)
273
 
            + (td.microseconds // 1000))
274
 
        
275
267
class Client(object):
276
268
    """A representation of a client host served by this server.
277
269
    
316
308
                          "host", "interval", "last_checked_ok",
317
309
                          "last_enabled", "name", "timeout")
318
310
    
 
311
    @staticmethod
 
312
    def _timedelta_to_milliseconds(td):
 
313
        "Convert a datetime.timedelta() to milliseconds"
 
314
        return ((td.days * 24 * 60 * 60 * 1000)
 
315
                + (td.seconds * 1000)
 
316
                + (td.microseconds // 1000))
 
317
    
319
318
    def timeout_milliseconds(self):
320
319
        "Return the 'timeout' attribute in milliseconds"
321
 
        return _timedelta_to_milliseconds(self.timeout)
322
 
    
 
320
        return self._timedelta_to_milliseconds(self.timeout)
 
321
 
323
322
    def extended_timeout_milliseconds(self):
324
323
        "Return the 'extended_timeout' attribute in milliseconds"
325
 
        return _timedelta_to_milliseconds(self.extended_timeout)
 
324
        return self._timedelta_to_milliseconds(self.extended_timeout)    
326
325
    
327
326
    def interval_milliseconds(self):
328
327
        "Return the 'interval' attribute in milliseconds"
329
 
        return _timedelta_to_milliseconds(self.interval)
330
 
    
 
328
        return self._timedelta_to_milliseconds(self.interval)
 
329
 
331
330
    def approval_delay_milliseconds(self):
332
 
        return _timedelta_to_milliseconds(self.approval_delay)
 
331
        return self._timedelta_to_milliseconds(self.approval_delay)
333
332
    
334
333
    def __init__(self, name = None, disable_hook=None, config=None):
335
334
        """Note: the 'checker' key in 'config' sets the
362
361
        self.last_enabled = None
363
362
        self.last_checked_ok = None
364
363
        self.timeout = string_to_delta(config["timeout"])
365
 
        self.extended_timeout = string_to_delta(config
366
 
                                                ["extended_timeout"])
 
364
        self.extended_timeout = string_to_delta(config["extended_timeout"])
367
365
        self.interval = string_to_delta(config["interval"])
368
366
        self.disable_hook = disable_hook
369
367
        self.checker = None
382
380
            config["approval_delay"])
383
381
        self.approval_duration = string_to_delta(
384
382
            config["approval_duration"])
385
 
        self.changedstate = (multiprocessing_manager
386
 
                             .Condition(multiprocessing_manager
387
 
                                        .Lock()))
 
383
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
388
384
    
389
385
    def send_changedstate(self):
390
386
        self.changedstate.acquire()
391
387
        self.changedstate.notify_all()
392
388
        self.changedstate.release()
393
 
    
 
389
        
394
390
    def enable(self):
395
391
        """Start this client's checker and timeout hooks"""
396
392
        if getattr(self, "enabled", False):
397
393
            # Already enabled
398
394
            return
399
395
        self.send_changedstate()
 
396
        self.last_enabled = datetime.datetime.utcnow()
400
397
        # Schedule a new checker to be started an 'interval' from now,
401
398
        # and every interval from then on.
402
399
        self.checker_initiator_tag = (gobject.timeout_add
408
405
                                   (self.timeout_milliseconds(),
409
406
                                    self.disable))
410
407
        self.enabled = True
411
 
        self.last_enabled = datetime.datetime.utcnow()
412
408
        # Also start a new checker *right now*.
413
409
        self.start_checker()
414
410
    
464
460
        if timeout is None:
465
461
            timeout = self.timeout
466
462
        self.last_checked_ok = datetime.datetime.utcnow()
467
 
        if self.disable_initiator_tag is not None:
468
 
            gobject.source_remove(self.disable_initiator_tag)
469
 
        if getattr(self, "enabled", False):
470
 
            self.disable_initiator_tag = (gobject.timeout_add
471
 
                                          (_timedelta_to_milliseconds
472
 
                                           (timeout), self.disable))
473
 
            self.expires = datetime.datetime.utcnow() + timeout
 
463
        gobject.source_remove(self.disable_initiator_tag)
 
464
        self.expires = datetime.datetime.utcnow() + timeout
 
465
        self.disable_initiator_tag = (gobject.timeout_add
 
466
                                      (self._timedelta_to_milliseconds(timeout),
 
467
                                       self.disable))
474
468
    
475
469
    def need_approval(self):
476
470
        self.last_approval_request = datetime.datetime.utcnow()
516
510
                                       'replace')))
517
511
                    for attr in
518
512
                    self.runtime_expansions)
519
 
                
 
513
 
520
514
                try:
521
515
                    command = self.checker_command % escaped_attrs
522
516
                except TypeError as error:
568
562
                raise
569
563
        self.checker = None
570
564
 
571
 
 
572
565
def dbus_service_property(dbus_interface, signature="v",
573
566
                          access="readwrite", byte_arrays=False):
574
567
    """Decorators for marking methods of a DBusObjectWithProperties to
620
613
 
621
614
class DBusObjectWithProperties(dbus.service.Object):
622
615
    """A D-Bus object with properties.
623
 
    
 
616
 
624
617
    Classes inheriting from this can use the dbus_service_property
625
618
    decorator to expose methods as D-Bus properties.  It exposes the
626
619
    standard Get(), Set(), and GetAll() methods on the D-Bus.
633
626
    def _get_all_dbus_properties(self):
634
627
        """Returns a generator of (name, attribute) pairs
635
628
        """
636
 
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
637
 
                for cls in self.__class__.__mro__
 
629
        return ((prop._dbus_name, prop)
638
630
                for name, prop in
639
 
                inspect.getmembers(cls, self._is_dbus_property))
 
631
                inspect.getmembers(self, self._is_dbus_property))
640
632
    
641
633
    def _get_dbus_property(self, interface_name, property_name):
642
634
        """Returns a bound method if one exists which is a D-Bus
643
635
        property with the specified name and interface.
644
636
        """
645
 
        for cls in  self.__class__.__mro__:
646
 
            for name, value in (inspect.getmembers
647
 
                                (cls, self._is_dbus_property)):
648
 
                if (value._dbus_name == property_name
649
 
                    and value._dbus_interface == interface_name):
650
 
                    return value.__get__(self)
651
 
        
 
637
        for name in (property_name,
 
638
                     property_name + "_dbus_property"):
 
639
            prop = getattr(self, name, None)
 
640
            if (prop is None
 
641
                or not self._is_dbus_property(prop)
 
642
                or prop._dbus_name != property_name
 
643
                or (interface_name and prop._dbus_interface
 
644
                    and interface_name != prop._dbus_interface)):
 
645
                continue
 
646
            return prop
652
647
        # No such property
653
648
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
654
649
                                   + interface_name + "."
688
683
    def GetAll(self, interface_name):
689
684
        """Standard D-Bus property GetAll() method, see D-Bus
690
685
        standard.
691
 
        
 
686
 
692
687
        Note: Will not include properties with access="write".
693
688
        """
694
689
        all = {}
756
751
        return xmlstring
757
752
 
758
753
 
759
 
def datetime_to_dbus (dt, variant_level=0):
760
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
761
 
    if dt is None:
762
 
        return dbus.String("", variant_level = variant_level)
763
 
    return dbus.String(dt.isoformat(),
764
 
                       variant_level=variant_level)
765
 
 
766
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
767
 
                                  .__metaclass__):
768
 
    """Applied to an empty subclass of a D-Bus object, this metaclass
769
 
    will add additional D-Bus attributes matching a certain pattern.
770
 
    """
771
 
    def __new__(mcs, name, bases, attr):
772
 
        # Go through all the base classes which could have D-Bus
773
 
        # methods, signals, or properties in them
774
 
        for base in (b for b in bases
775
 
                     if issubclass(b, dbus.service.Object)):
776
 
            # Go though all attributes of the base class
777
 
            for attrname, attribute in inspect.getmembers(base):
778
 
                # Ignore non-D-Bus attributes, and D-Bus attributes
779
 
                # with the wrong interface name
780
 
                if (not hasattr(attribute, "_dbus_interface")
781
 
                    or not attribute._dbus_interface
782
 
                    .startswith("se.recompile.Mandos")):
783
 
                    continue
784
 
                # Create an alternate D-Bus interface name based on
785
 
                # the current name
786
 
                alt_interface = (attribute._dbus_interface
787
 
                                 .replace("se.recompile.Mandos",
788
 
                                          "se.bsnet.fukt.Mandos"))
789
 
                # Is this a D-Bus signal?
790
 
                if getattr(attribute, "_dbus_is_signal", False):
791
 
                    # Extract the original non-method function by
792
 
                    # black magic
793
 
                    nonmethod_func = (dict(
794
 
                            zip(attribute.func_code.co_freevars,
795
 
                                attribute.__closure__))["func"]
796
 
                                      .cell_contents)
797
 
                    # Create a new, but exactly alike, function
798
 
                    # object, and decorate it to be a new D-Bus signal
799
 
                    # with the alternate D-Bus interface name
800
 
                    new_function = (dbus.service.signal
801
 
                                    (alt_interface,
802
 
                                     attribute._dbus_signature)
803
 
                                    (types.FunctionType(
804
 
                                nonmethod_func.func_code,
805
 
                                nonmethod_func.func_globals,
806
 
                                nonmethod_func.func_name,
807
 
                                nonmethod_func.func_defaults,
808
 
                                nonmethod_func.func_closure)))
809
 
                    # Define a creator of a function to call both the
810
 
                    # old and new functions, so both the old and new
811
 
                    # signals gets sent when the function is called
812
 
                    def fixscope(func1, func2):
813
 
                        """This function is a scope container to pass
814
 
                        func1 and func2 to the "call_both" function
815
 
                        outside of its arguments"""
816
 
                        def call_both(*args, **kwargs):
817
 
                            """This function will emit two D-Bus
818
 
                            signals by calling func1 and func2"""
819
 
                            func1(*args, **kwargs)
820
 
                            func2(*args, **kwargs)
821
 
                        return call_both
822
 
                    # Create the "call_both" function and add it to
823
 
                    # the class
824
 
                    attr[attrname] = fixscope(attribute,
825
 
                                              new_function)
826
 
                # Is this a D-Bus method?
827
 
                elif getattr(attribute, "_dbus_is_method", False):
828
 
                    # Create a new, but exactly alike, function
829
 
                    # object.  Decorate it to be a new D-Bus method
830
 
                    # with the alternate D-Bus interface name.  Add it
831
 
                    # to the class.
832
 
                    attr[attrname] = (dbus.service.method
833
 
                                      (alt_interface,
834
 
                                       attribute._dbus_in_signature,
835
 
                                       attribute._dbus_out_signature)
836
 
                                      (types.FunctionType
837
 
                                       (attribute.func_code,
838
 
                                        attribute.func_globals,
839
 
                                        attribute.func_name,
840
 
                                        attribute.func_defaults,
841
 
                                        attribute.func_closure)))
842
 
                # Is this a D-Bus property?
843
 
                elif getattr(attribute, "_dbus_is_property", False):
844
 
                    # Create a new, but exactly alike, function
845
 
                    # object, and decorate it to be a new D-Bus
846
 
                    # property with the alternate D-Bus interface
847
 
                    # name.  Add it to the class.
848
 
                    attr[attrname] = (dbus_service_property
849
 
                                      (alt_interface,
850
 
                                       attribute._dbus_signature,
851
 
                                       attribute._dbus_access,
852
 
                                       attribute
853
 
                                       ._dbus_get_args_options
854
 
                                       ["byte_arrays"])
855
 
                                      (types.FunctionType
856
 
                                       (attribute.func_code,
857
 
                                        attribute.func_globals,
858
 
                                        attribute.func_name,
859
 
                                        attribute.func_defaults,
860
 
                                        attribute.func_closure)))
861
 
        return type.__new__(mcs, name, bases, attr)
862
 
 
863
754
class ClientDBus(Client, DBusObjectWithProperties):
864
755
    """A Client class using D-Bus
865
756
    
886
777
                                 ("/clients/" + client_object_name))
887
778
        DBusObjectWithProperties.__init__(self, self.bus,
888
779
                                          self.dbus_object_path)
 
780
    def _set_expires(self, value):
 
781
        old_value = getattr(self, "_expires", None)
 
782
        self._expires = value
 
783
        if hasattr(self, "dbus_object_path") and old_value != value:
 
784
            dbus_time = (self._datetime_to_dbus(self._expires,
 
785
                                                variant_level=1))
 
786
            self.PropertyChanged(dbus.String("Expires"),
 
787
                                 dbus_time)
 
788
    expires = property(lambda self: self._expires, _set_expires)
 
789
    del _set_expires
889
790
        
890
 
    def notifychangeproperty(transform_func,
891
 
                             dbus_name, type_func=lambda x: x,
892
 
                             variant_level=1):
893
 
        """ Modify a variable so that it's a property which announces
894
 
        its changes to DBus.
 
791
    def _get_approvals_pending(self):
 
792
        return self._approvals_pending
 
793
    def _set_approvals_pending(self, value):
 
794
        old_value = self._approvals_pending
 
795
        self._approvals_pending = value
 
796
        bval = bool(value)
 
797
        if (hasattr(self, "dbus_object_path")
 
798
            and bval is not bool(old_value)):
 
799
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
800
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
801
                                 dbus_bool)
895
802
 
896
 
        transform_fun: Function that takes a value and a variant_level
897
 
                       and transforms it to a D-Bus type.
898
 
        dbus_name: D-Bus name of the variable
899
 
        type_func: Function that transform the value before sending it
900
 
                   to the D-Bus.  Default: no transform
901
 
        variant_level: D-Bus variant level.  Default: 1
902
 
        """
903
 
        attrname = "_{0}".format(dbus_name)
904
 
        def setter(self, value):
905
 
            if hasattr(self, "dbus_object_path"):
906
 
                if (not hasattr(self, attrname) or
907
 
                    type_func(getattr(self, attrname, None))
908
 
                    != type_func(value)):
909
 
                    dbus_value = transform_func(type_func(value),
910
 
                                                variant_level
911
 
                                                =variant_level)
912
 
                    self.PropertyChanged(dbus.String(dbus_name),
913
 
                                         dbus_value)
914
 
            setattr(self, attrname, value)
915
 
        
916
 
        return property(lambda self: getattr(self, attrname), setter)
917
 
    
918
 
    
919
 
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
920
 
    approvals_pending = notifychangeproperty(dbus.Boolean,
921
 
                                             "ApprovalPending",
922
 
                                             type_func = bool)
923
 
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
924
 
    last_enabled = notifychangeproperty(datetime_to_dbus,
925
 
                                        "LastEnabled")
926
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
927
 
                                   type_func = lambda checker:
928
 
                                       checker is not None)
929
 
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
930
 
                                           "LastCheckedOK")
931
 
    last_approval_request = notifychangeproperty(
932
 
        datetime_to_dbus, "LastApprovalRequest")
933
 
    approved_by_default = notifychangeproperty(dbus.Boolean,
934
 
                                               "ApprovedByDefault")
935
 
    approval_delay = notifychangeproperty(dbus.UInt16,
936
 
                                          "ApprovalDelay",
937
 
                                          type_func =
938
 
                                          _timedelta_to_milliseconds)
939
 
    approval_duration = notifychangeproperty(
940
 
        dbus.UInt16, "ApprovalDuration",
941
 
        type_func = _timedelta_to_milliseconds)
942
 
    host = notifychangeproperty(dbus.String, "Host")
943
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
944
 
                                   type_func =
945
 
                                   _timedelta_to_milliseconds)
946
 
    extended_timeout = notifychangeproperty(
947
 
        dbus.UInt16, "ExtendedTimeout",
948
 
        type_func = _timedelta_to_milliseconds)
949
 
    interval = notifychangeproperty(dbus.UInt16,
950
 
                                    "Interval",
951
 
                                    type_func =
952
 
                                    _timedelta_to_milliseconds)
953
 
    checker_command = notifychangeproperty(dbus.String, "Checker")
954
 
    
955
 
    del notifychangeproperty
 
803
    approvals_pending = property(_get_approvals_pending,
 
804
                                 _set_approvals_pending)
 
805
    del _get_approvals_pending, _set_approvals_pending
 
806
    
 
807
    @staticmethod
 
808
    def _datetime_to_dbus(dt, variant_level=0):
 
809
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
810
        if dt is None:
 
811
            return dbus.String("", variant_level = variant_level)
 
812
        return dbus.String(dt.isoformat(),
 
813
                           variant_level=variant_level)
 
814
    
 
815
    def enable(self):
 
816
        oldstate = getattr(self, "enabled", False)
 
817
        r = Client.enable(self)
 
818
        if oldstate != self.enabled:
 
819
            # Emit D-Bus signals
 
820
            self.PropertyChanged(dbus.String("Enabled"),
 
821
                                 dbus.Boolean(True, variant_level=1))
 
822
            self.PropertyChanged(
 
823
                dbus.String("LastEnabled"),
 
824
                self._datetime_to_dbus(self.last_enabled,
 
825
                                       variant_level=1))
 
826
        return r
 
827
    
 
828
    def disable(self, quiet = False):
 
829
        oldstate = getattr(self, "enabled", False)
 
830
        r = Client.disable(self, quiet=quiet)
 
831
        if not quiet and oldstate != self.enabled:
 
832
            # Emit D-Bus signal
 
833
            self.PropertyChanged(dbus.String("Enabled"),
 
834
                                 dbus.Boolean(False, variant_level=1))
 
835
        return r
956
836
    
957
837
    def __del__(self, *args, **kwargs):
958
838
        try:
967
847
                         *args, **kwargs):
968
848
        self.checker_callback_tag = None
969
849
        self.checker = None
 
850
        # Emit D-Bus signal
 
851
        self.PropertyChanged(dbus.String("CheckerRunning"),
 
852
                             dbus.Boolean(False, variant_level=1))
970
853
        if os.WIFEXITED(condition):
971
854
            exitstatus = os.WEXITSTATUS(condition)
972
855
            # Emit D-Bus signal
982
865
        return Client.checker_callback(self, pid, condition, command,
983
866
                                       *args, **kwargs)
984
867
    
 
868
    def checked_ok(self, *args, **kwargs):
 
869
        Client.checked_ok(self, *args, **kwargs)
 
870
        # Emit D-Bus signal
 
871
        self.PropertyChanged(
 
872
            dbus.String("LastCheckedOK"),
 
873
            (self._datetime_to_dbus(self.last_checked_ok,
 
874
                                    variant_level=1)))
 
875
    
 
876
    def need_approval(self, *args, **kwargs):
 
877
        r = Client.need_approval(self, *args, **kwargs)
 
878
        # Emit D-Bus signal
 
879
        self.PropertyChanged(
 
880
            dbus.String("LastApprovalRequest"),
 
881
            (self._datetime_to_dbus(self.last_approval_request,
 
882
                                    variant_level=1)))
 
883
        return r
 
884
    
985
885
    def start_checker(self, *args, **kwargs):
986
886
        old_checker = self.checker
987
887
        if self.checker is not None:
994
894
            and old_checker_pid != self.checker.pid):
995
895
            # Emit D-Bus signal
996
896
            self.CheckerStarted(self.current_checker_command)
 
897
            self.PropertyChanged(
 
898
                dbus.String("CheckerRunning"),
 
899
                dbus.Boolean(True, variant_level=1))
997
900
        return r
998
901
    
 
902
    def stop_checker(self, *args, **kwargs):
 
903
        old_checker = getattr(self, "checker", None)
 
904
        r = Client.stop_checker(self, *args, **kwargs)
 
905
        if (old_checker is not None
 
906
            and getattr(self, "checker", None) is None):
 
907
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
908
                                 dbus.Boolean(False, variant_level=1))
 
909
        return r
 
910
 
999
911
    def _reset_approved(self):
1000
912
        self._approved = None
1001
913
        return False
1003
915
    def approve(self, value=True):
1004
916
        self.send_changedstate()
1005
917
        self._approved = value
1006
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
918
        gobject.timeout_add(self._timedelta_to_milliseconds
1007
919
                            (self.approval_duration),
1008
920
                            self._reset_approved)
1009
921
    
1010
922
    
1011
923
    ## D-Bus methods, signals & properties
1012
 
    _interface = "se.recompile.Mandos.Client"
 
924
    _interface = "se.bsnet.fukt.Mandos.Client"
1013
925
    
1014
926
    ## Signals
1015
927
    
1100
1012
    def ApprovedByDefault_dbus_property(self, value=None):
1101
1013
        if value is None:       # get
1102
1014
            return dbus.Boolean(self.approved_by_default)
 
1015
        old_value = self.approved_by_default
1103
1016
        self.approved_by_default = bool(value)
 
1017
        # Emit D-Bus signal
 
1018
        if old_value != self.approved_by_default:
 
1019
            self.PropertyChanged(dbus.String("ApprovedByDefault"),
 
1020
                                 dbus.Boolean(value, variant_level=1))
1104
1021
    
1105
1022
    # ApprovalDelay - property
1106
1023
    @dbus_service_property(_interface, signature="t",
1108
1025
    def ApprovalDelay_dbus_property(self, value=None):
1109
1026
        if value is None:       # get
1110
1027
            return dbus.UInt64(self.approval_delay_milliseconds())
 
1028
        old_value = self.approval_delay
1111
1029
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
1030
        # Emit D-Bus signal
 
1031
        if old_value != self.approval_delay:
 
1032
            self.PropertyChanged(dbus.String("ApprovalDelay"),
 
1033
                                 dbus.UInt64(value, variant_level=1))
1112
1034
    
1113
1035
    # ApprovalDuration - property
1114
1036
    @dbus_service_property(_interface, signature="t",
1115
1037
                           access="readwrite")
1116
1038
    def ApprovalDuration_dbus_property(self, value=None):
1117
1039
        if value is None:       # get
1118
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
1040
            return dbus.UInt64(self._timedelta_to_milliseconds(
1119
1041
                    self.approval_duration))
 
1042
        old_value = self.approval_duration
1120
1043
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
1044
        # Emit D-Bus signal
 
1045
        if old_value != self.approval_duration:
 
1046
            self.PropertyChanged(dbus.String("ApprovalDuration"),
 
1047
                                 dbus.UInt64(value, variant_level=1))
1121
1048
    
1122
1049
    # Name - property
1123
1050
    @dbus_service_property(_interface, signature="s", access="read")
1135
1062
    def Host_dbus_property(self, value=None):
1136
1063
        if value is None:       # get
1137
1064
            return dbus.String(self.host)
 
1065
        old_value = self.host
1138
1066
        self.host = value
 
1067
        # Emit D-Bus signal
 
1068
        if old_value != self.host:
 
1069
            self.PropertyChanged(dbus.String("Host"),
 
1070
                                 dbus.String(value, variant_level=1))
1139
1071
    
1140
1072
    # Created - property
1141
1073
    @dbus_service_property(_interface, signature="s", access="read")
1142
1074
    def Created_dbus_property(self):
1143
 
        return dbus.String(datetime_to_dbus(self.created))
 
1075
        return dbus.String(self._datetime_to_dbus(self.created))
1144
1076
    
1145
1077
    # LastEnabled - property
1146
1078
    @dbus_service_property(_interface, signature="s", access="read")
1147
1079
    def LastEnabled_dbus_property(self):
1148
 
        return datetime_to_dbus(self.last_enabled)
 
1080
        return self._datetime_to_dbus(self.last_enabled)
1149
1081
    
1150
1082
    # Enabled - property
1151
1083
    @dbus_service_property(_interface, signature="b",
1165
1097
        if value is not None:
1166
1098
            self.checked_ok()
1167
1099
            return
1168
 
        return datetime_to_dbus(self.last_checked_ok)
 
1100
        return self._datetime_to_dbus(self.last_checked_ok)
1169
1101
    
1170
1102
    # Expires - property
1171
1103
    @dbus_service_property(_interface, signature="s", access="read")
1172
1104
    def Expires_dbus_property(self):
1173
 
        return datetime_to_dbus(self.expires)
 
1105
        return self._datetime_to_dbus(self.expires)
1174
1106
    
1175
1107
    # LastApprovalRequest - property
1176
1108
    @dbus_service_property(_interface, signature="s", access="read")
1177
1109
    def LastApprovalRequest_dbus_property(self):
1178
 
        return datetime_to_dbus(self.last_approval_request)
 
1110
        return self._datetime_to_dbus(self.last_approval_request)
1179
1111
    
1180
1112
    # Timeout - property
1181
1113
    @dbus_service_property(_interface, signature="t",
1183
1115
    def Timeout_dbus_property(self, value=None):
1184
1116
        if value is None:       # get
1185
1117
            return dbus.UInt64(self.timeout_milliseconds())
 
1118
        old_value = self.timeout
1186
1119
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1120
        # Emit D-Bus signal
 
1121
        if old_value != self.timeout:
 
1122
            self.PropertyChanged(dbus.String("Timeout"),
 
1123
                                 dbus.UInt64(value, variant_level=1))
1187
1124
        if getattr(self, "disable_initiator_tag", None) is None:
1188
1125
            return
1189
1126
        # Reschedule timeout
1190
1127
        gobject.source_remove(self.disable_initiator_tag)
1191
1128
        self.disable_initiator_tag = None
1192
1129
        self.expires = None
1193
 
        time_to_die = _timedelta_to_milliseconds((self
1194
 
                                                  .last_checked_ok
1195
 
                                                  + self.timeout)
1196
 
                                                 - datetime.datetime
1197
 
                                                 .utcnow())
 
1130
        time_to_die = (self.
 
1131
                       _timedelta_to_milliseconds((self
 
1132
                                                   .last_checked_ok
 
1133
                                                   + self.timeout)
 
1134
                                                  - datetime.datetime
 
1135
                                                  .utcnow()))
1198
1136
        if time_to_die <= 0:
1199
1137
            # The timeout has passed
1200
1138
            self.disable()
1201
1139
        else:
1202
1140
            self.expires = (datetime.datetime.utcnow()
1203
 
                            + datetime.timedelta(milliseconds =
1204
 
                                                 time_to_die))
 
1141
                            + datetime.timedelta(milliseconds = time_to_die))
1205
1142
            self.disable_initiator_tag = (gobject.timeout_add
1206
1143
                                          (time_to_die, self.disable))
1207
 
    
 
1144
 
1208
1145
    # ExtendedTimeout - property
1209
1146
    @dbus_service_property(_interface, signature="t",
1210
1147
                           access="readwrite")
1211
1148
    def ExtendedTimeout_dbus_property(self, value=None):
1212
1149
        if value is None:       # get
1213
1150
            return dbus.UInt64(self.extended_timeout_milliseconds())
 
1151
        old_value = self.extended_timeout
1214
1152
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1215
 
    
 
1153
        # Emit D-Bus signal
 
1154
        if old_value != self.extended_timeout:
 
1155
            self.PropertyChanged(dbus.String("ExtendedTimeout"),
 
1156
                                 dbus.UInt64(value, variant_level=1))
 
1157
 
1216
1158
    # Interval - property
1217
1159
    @dbus_service_property(_interface, signature="t",
1218
1160
                           access="readwrite")
1219
1161
    def Interval_dbus_property(self, value=None):
1220
1162
        if value is None:       # get
1221
1163
            return dbus.UInt64(self.interval_milliseconds())
 
1164
        old_value = self.interval
1222
1165
        self.interval = datetime.timedelta(0, 0, 0, value)
 
1166
        # Emit D-Bus signal
 
1167
        if old_value != self.interval:
 
1168
            self.PropertyChanged(dbus.String("Interval"),
 
1169
                                 dbus.UInt64(value, variant_level=1))
1223
1170
        if getattr(self, "checker_initiator_tag", None) is None:
1224
1171
            return
1225
1172
        # Reschedule checker run
1227
1174
        self.checker_initiator_tag = (gobject.timeout_add
1228
1175
                                      (value, self.start_checker))
1229
1176
        self.start_checker()    # Start one now, too
1230
 
    
 
1177
 
1231
1178
    # Checker - property
1232
1179
    @dbus_service_property(_interface, signature="s",
1233
1180
                           access="readwrite")
1234
1181
    def Checker_dbus_property(self, value=None):
1235
1182
        if value is None:       # get
1236
1183
            return dbus.String(self.checker_command)
 
1184
        old_value = self.checker_command
1237
1185
        self.checker_command = value
 
1186
        # Emit D-Bus signal
 
1187
        if old_value != self.checker_command:
 
1188
            self.PropertyChanged(dbus.String("Checker"),
 
1189
                                 dbus.String(self.checker_command,
 
1190
                                             variant_level=1))
1238
1191
    
1239
1192
    # CheckerRunning - property
1240
1193
    @dbus_service_property(_interface, signature="b",
1267
1220
        self._pipe.send(('init', fpr, address))
1268
1221
        if not self._pipe.recv():
1269
1222
            raise KeyError()
1270
 
    
 
1223
 
1271
1224
    def __getattribute__(self, name):
1272
1225
        if(name == '_pipe'):
1273
1226
            return super(ProxyClient, self).__getattribute__(name)
1280
1233
                self._pipe.send(('funcall', name, args, kwargs))
1281
1234
                return self._pipe.recv()[1]
1282
1235
            return func
1283
 
    
 
1236
 
1284
1237
    def __setattr__(self, name, value):
1285
1238
        if(name == '_pipe'):
1286
1239
            return super(ProxyClient, self).__setattr__(name, value)
1287
1240
        self._pipe.send(('setattr', name, value))
1288
1241
 
1289
 
class ClientDBusTransitional(ClientDBus):
1290
 
    __metaclass__ = AlternateDBusNamesMetaclass
1291
1242
 
1292
1243
class ClientHandler(socketserver.BaseRequestHandler, object):
1293
1244
    """A class to handle client connections.
1301
1252
                        unicode(self.client_address))
1302
1253
            logger.debug("Pipe FD: %d",
1303
1254
                         self.server.child_pipe.fileno())
1304
 
            
 
1255
 
1305
1256
            session = (gnutls.connection
1306
1257
                       .ClientSession(self.request,
1307
1258
                                      gnutls.connection
1308
1259
                                      .X509Credentials()))
1309
 
            
 
1260
 
1310
1261
            # Note: gnutls.connection.X509Credentials is really a
1311
1262
            # generic GnuTLS certificate credentials object so long as
1312
1263
            # no X.509 keys are added to it.  Therefore, we can use it
1313
1264
            # here despite using OpenPGP certificates.
1314
 
            
 
1265
 
1315
1266
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1316
1267
            #                      "+AES-256-CBC", "+SHA1",
1317
1268
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1323
1274
            (gnutls.library.functions
1324
1275
             .gnutls_priority_set_direct(session._c_object,
1325
1276
                                         priority, None))
1326
 
            
 
1277
 
1327
1278
            # Start communication using the Mandos protocol
1328
1279
            # Get protocol number
1329
1280
            line = self.request.makefile().readline()
1334
1285
            except (ValueError, IndexError, RuntimeError) as error:
1335
1286
                logger.error("Unknown protocol version: %s", error)
1336
1287
                return
1337
 
            
 
1288
 
1338
1289
            # Start GnuTLS connection
1339
1290
            try:
1340
1291
                session.handshake()
1344
1295
                # established.  Just abandon the request.
1345
1296
                return
1346
1297
            logger.debug("Handshake succeeded")
1347
 
            
 
1298
 
1348
1299
            approval_required = False
1349
1300
            try:
1350
1301
                try:
1355
1306
                    logger.warning("Bad certificate: %s", error)
1356
1307
                    return
1357
1308
                logger.debug("Fingerprint: %s", fpr)
1358
 
                
 
1309
 
1359
1310
                try:
1360
1311
                    client = ProxyClient(child_pipe, fpr,
1361
1312
                                         self.client_address)
1373
1324
                                       client.name)
1374
1325
                        if self.server.use_dbus:
1375
1326
                            # Emit D-Bus signal
1376
 
                            client.Rejected("Disabled")
 
1327
                            client.Rejected("Disabled")                    
1377
1328
                        return
1378
1329
                    
1379
1330
                    if client._approved or not client.approval_delay:
1396
1347
                        return
1397
1348
                    
1398
1349
                    #wait until timeout or approved
 
1350
                    #x = float(client._timedelta_to_milliseconds(delay))
1399
1351
                    time = datetime.datetime.now()
1400
1352
                    client.changedstate.acquire()
1401
 
                    (client.changedstate.wait
1402
 
                     (float(client._timedelta_to_milliseconds(delay)
1403
 
                            / 1000)))
 
1353
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1404
1354
                    client.changedstate.release()
1405
1355
                    time2 = datetime.datetime.now()
1406
1356
                    if (time2 - time) >= delay:
1428
1378
                                 sent, len(client.secret)
1429
1379
                                 - (sent_size + sent))
1430
1380
                    sent_size += sent
1431
 
                
 
1381
 
1432
1382
                logger.info("Sending secret to %s", client.name)
1433
 
                # bump the timeout using extended_timeout
 
1383
                # bump the timeout as if seen
1434
1384
                client.checked_ok(client.extended_timeout)
1435
1385
                if self.server.use_dbus:
1436
1386
                    # Emit D-Bus signal
1516
1466
        except:
1517
1467
            self.handle_error(request, address)
1518
1468
        self.close_request(request)
1519
 
    
 
1469
            
1520
1470
    def process_request(self, request, address):
1521
1471
        """Start a new process to process the request."""
1522
 
        proc = multiprocessing.Process(target = self.sub_process_main,
1523
 
                                       args = (request,
1524
 
                                               address))
1525
 
        proc.start()
1526
 
        return proc
1527
 
 
 
1472
        multiprocessing.Process(target = self.sub_process_main,
 
1473
                                args = (request, address)).start()
1528
1474
 
1529
1475
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1530
1476
    """ adds a pipe to the MixIn """
1534
1480
        This function creates a new pipe in self.pipe
1535
1481
        """
1536
1482
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1537
 
        
1538
 
        proc = MultiprocessingMixIn.process_request(self, request,
1539
 
                                                    client_address)
 
1483
 
 
1484
        super(MultiprocessingMixInWithPipe,
 
1485
              self).process_request(request, client_address)
1540
1486
        self.child_pipe.close()
1541
 
        self.add_pipe(parent_pipe, proc)
1542
 
    
1543
 
    def add_pipe(self, parent_pipe, proc):
 
1487
        self.add_pipe(parent_pipe)
 
1488
 
 
1489
    def add_pipe(self, parent_pipe):
1544
1490
        """Dummy function; override as necessary"""
1545
1491
        raise NotImplementedError
1546
1492
 
1547
 
 
1548
1493
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1549
1494
                     socketserver.TCPServer, object):
1550
1495
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1634
1579
    def server_activate(self):
1635
1580
        if self.enabled:
1636
1581
            return socketserver.TCPServer.server_activate(self)
1637
 
    
1638
1582
    def enable(self):
1639
1583
        self.enabled = True
1640
 
    
1641
 
    def add_pipe(self, parent_pipe, proc):
 
1584
    def add_pipe(self, parent_pipe):
1642
1585
        # Call "handle_ipc" for both data and EOF events
1643
1586
        gobject.io_add_watch(parent_pipe.fileno(),
1644
1587
                             gobject.IO_IN | gobject.IO_HUP,
1645
1588
                             functools.partial(self.handle_ipc,
1646
 
                                               parent_pipe =
1647
 
                                               parent_pipe,
1648
 
                                               proc = proc))
1649
 
    
 
1589
                                               parent_pipe = parent_pipe))
 
1590
        
1650
1591
    def handle_ipc(self, source, condition, parent_pipe=None,
1651
 
                   proc = None, client_object=None):
 
1592
                   client_object=None):
1652
1593
        condition_names = {
1653
1594
            gobject.IO_IN: "IN",   # There is data to read.
1654
1595
            gobject.IO_OUT: "OUT", # Data can be written (without
1663
1604
                                       for cond, name in
1664
1605
                                       condition_names.iteritems()
1665
1606
                                       if cond & condition)
1666
 
        # error, or the other end of multiprocessing.Pipe has closed
 
1607
        # error or the other end of multiprocessing.Pipe has closed
1667
1608
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1668
 
            # Wait for other process to exit
1669
 
            proc.join()
1670
1609
            return False
1671
1610
        
1672
1611
        # Read a request from the child
1686
1625
                            "dress: %s", fpr, address)
1687
1626
                if self.use_dbus:
1688
1627
                    # Emit D-Bus signal
1689
 
                    mandos_dbus_service.ClientNotFound(fpr,
1690
 
                                                       address[0])
 
1628
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
1691
1629
                parent_pipe.send(False)
1692
1630
                return False
1693
1631
            
1694
1632
            gobject.io_add_watch(parent_pipe.fileno(),
1695
1633
                                 gobject.IO_IN | gobject.IO_HUP,
1696
1634
                                 functools.partial(self.handle_ipc,
1697
 
                                                   parent_pipe =
1698
 
                                                   parent_pipe,
1699
 
                                                   proc = proc,
1700
 
                                                   client_object =
1701
 
                                                   client))
 
1635
                                                   parent_pipe = parent_pipe,
 
1636
                                                   client_object = client))
1702
1637
            parent_pipe.send(True)
1703
 
            # remove the old hook in favor of the new above hook on
1704
 
            # same fileno
 
1638
            # remove the old hook in favor of the new above hook on same fileno
1705
1639
            return False
1706
1640
        if command == 'funcall':
1707
1641
            funcname = request[1]
1708
1642
            args = request[2]
1709
1643
            kwargs = request[3]
1710
1644
            
1711
 
            parent_pipe.send(('data', getattr(client_object,
1712
 
                                              funcname)(*args,
1713
 
                                                         **kwargs)))
1714
 
        
 
1645
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1646
 
1715
1647
        if command == 'getattr':
1716
1648
            attrname = request[1]
1717
1649
            if callable(client_object.__getattribute__(attrname)):
1718
1650
                parent_pipe.send(('function',))
1719
1651
            else:
1720
 
                parent_pipe.send(('data', client_object
1721
 
                                  .__getattribute__(attrname)))
 
1652
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1722
1653
        
1723
1654
        if command == 'setattr':
1724
1655
            attrname = request[1]
1725
1656
            value = request[2]
1726
1657
            setattr(client_object, attrname, value)
1727
 
        
 
1658
 
1728
1659
        return True
1729
1660
 
1730
1661
 
1909
1840
    debuglevel = server_settings["debuglevel"]
1910
1841
    use_dbus = server_settings["use_dbus"]
1911
1842
    use_ipv6 = server_settings["use_ipv6"]
1912
 
    
 
1843
 
1913
1844
    if server_settings["servicename"] != "Mandos":
1914
1845
        syslogger.setFormatter(logging.Formatter
1915
1846
                               ('Mandos (%s) [%%(process)d]:'
1976
1907
        level = getattr(logging, debuglevel.upper())
1977
1908
        syslogger.setLevel(level)
1978
1909
        console.setLevel(level)
1979
 
    
 
1910
 
1980
1911
    if debug:
1981
1912
        # Enable all possible GnuTLS debugging
1982
1913
        
2013
1944
    # End of Avahi example code
2014
1945
    if use_dbus:
2015
1946
        try:
2016
 
            bus_name = dbus.service.BusName("se.recompile.Mandos",
 
1947
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
2017
1948
                                            bus, do_not_queue=True)
2018
 
            old_bus_name = (dbus.service.BusName
2019
 
                            ("se.bsnet.fukt.Mandos", bus,
2020
 
                             do_not_queue=True))
2021
1949
        except dbus.exceptions.NameExistsException as e:
2022
1950
            logger.error(unicode(e) + ", disabling D-Bus")
2023
1951
            use_dbus = False
2036
1964
    
2037
1965
    client_class = Client
2038
1966
    if use_dbus:
2039
 
        client_class = functools.partial(ClientDBusTransitional,
2040
 
                                         bus = bus)
 
1967
        client_class = functools.partial(ClientDBus, bus = bus)
2041
1968
    def client_config_items(config, section):
2042
1969
        special_settings = {
2043
1970
            "approved_by_default":
2073
2000
        del pidfilename
2074
2001
        
2075
2002
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2076
 
    
 
2003
 
2077
2004
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2078
2005
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2079
2006
    
2082
2009
            """A D-Bus proxy object"""
2083
2010
            def __init__(self):
2084
2011
                dbus.service.Object.__init__(self, bus, "/")
2085
 
            _interface = "se.recompile.Mandos"
 
2012
            _interface = "se.bsnet.fukt.Mandos"
2086
2013
            
2087
2014
            @dbus.service.signal(_interface, signature="o")
2088
2015
            def ClientAdded(self, objpath):
2130
2057
            
2131
2058
            del _interface
2132
2059
        
2133
 
        class MandosDBusServiceTransitional(MandosDBusService):
2134
 
            __metaclass__ = AlternateDBusNamesMetaclass
2135
 
        mandos_dbus_service = MandosDBusServiceTransitional()
 
2060
        mandos_dbus_service = MandosDBusService()
2136
2061
    
2137
2062
    def cleanup():
2138
2063
        "Cleanup function; run on exit"
2139
2064
        service.cleanup()
2140
2065
        
2141
 
        multiprocessing.active_children()
2142
2066
        while tcp_server.clients:
2143
2067
            client = tcp_server.clients.pop()
2144
2068
            if use_dbus:
2148
2072
            client.disable(quiet=True)
2149
2073
            if use_dbus:
2150
2074
                # Emit D-Bus signal
2151
 
                mandos_dbus_service.ClientRemoved(client
2152
 
                                                  .dbus_object_path,
 
2075
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2153
2076
                                                  client.name)
2154
2077
    
2155
2078
    atexit.register(cleanup)
2204
2127
    # Must run before the D-Bus bus name gets deregistered
2205
2128
    cleanup()
2206
2129
 
2207
 
 
2208
2130
if __name__ == '__main__':
2209
2131
    main()