/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-07-27 17:58:27 UTC
  • mto: (237.4.29 release)
  • mto: This revision was merged to the branch mainline in revision 490.
  • Revision ID: teddy@fukt.bsnet.se-20110727175827-nrd1ysjl4jrh6qw1
Tags: version-1.3.1-1
* Makefile (version): Changed to "1.3.1".
* NEWS (Version 1.3.1): New entry.
* debian/changelog (1.3.1-1): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
265
264
        self.server_state_changed(self.server.GetState())
266
265
 
267
266
 
268
 
def _timedelta_to_milliseconds(td):
269
 
    "Convert a datetime.timedelta() to milliseconds"
270
 
    return ((td.days * 24 * 60 * 60 * 1000)
271
 
            + (td.seconds * 1000)
272
 
            + (td.microseconds // 1000))
273
 
        
274
267
class Client(object):
275
268
    """A representation of a client host served by this server.
276
269
    
304
297
    secret:     bytestring; sent verbatim (over TLS) to client
305
298
    timeout:    datetime.timedelta(); How long from last_checked_ok
306
299
                                      until this client is disabled
307
 
    extended_timeout:   extra long timeout when password has been sent
308
300
    runtime_expansions: Allowed attributes for runtime expansion.
309
 
    expires:    datetime.datetime(); time (UTC) when a client will be
310
 
                disabled, or None
311
301
    """
312
302
    
313
303
    runtime_expansions = ("approval_delay", "approval_duration",
315
305
                          "host", "interval", "last_checked_ok",
316
306
                          "last_enabled", "name", "timeout")
317
307
    
 
308
    @staticmethod
 
309
    def _timedelta_to_milliseconds(td):
 
310
        "Convert a datetime.timedelta() to milliseconds"
 
311
        return ((td.days * 24 * 60 * 60 * 1000)
 
312
                + (td.seconds * 1000)
 
313
                + (td.microseconds // 1000))
 
314
    
318
315
    def timeout_milliseconds(self):
319
316
        "Return the 'timeout' attribute in milliseconds"
320
 
        return _timedelta_to_milliseconds(self.timeout)
321
 
    
322
 
    def extended_timeout_milliseconds(self):
323
 
        "Return the 'extended_timeout' attribute in milliseconds"
324
 
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
317
        return self._timedelta_to_milliseconds(self.timeout)
325
318
    
326
319
    def interval_milliseconds(self):
327
320
        "Return the 'interval' attribute in milliseconds"
328
 
        return _timedelta_to_milliseconds(self.interval)
329
 
    
 
321
        return self._timedelta_to_milliseconds(self.interval)
 
322
 
330
323
    def approval_delay_milliseconds(self):
331
 
        return _timedelta_to_milliseconds(self.approval_delay)
 
324
        return self._timedelta_to_milliseconds(self.approval_delay)
332
325
    
333
326
    def __init__(self, name = None, disable_hook=None, config=None):
334
327
        """Note: the 'checker' key in 'config' sets the
361
354
        self.last_enabled = None
362
355
        self.last_checked_ok = None
363
356
        self.timeout = string_to_delta(config["timeout"])
364
 
        self.extended_timeout = string_to_delta(config["extended_timeout"])
365
357
        self.interval = string_to_delta(config["interval"])
366
358
        self.disable_hook = disable_hook
367
359
        self.checker = None
368
360
        self.checker_initiator_tag = None
369
361
        self.disable_initiator_tag = None
370
 
        self.expires = None
371
362
        self.checker_callback_tag = None
372
363
        self.checker_command = config["checker"]
373
364
        self.current_checker_command = None
393
384
            # Already enabled
394
385
            return
395
386
        self.send_changedstate()
 
387
        self.last_enabled = datetime.datetime.utcnow()
396
388
        # Schedule a new checker to be started an 'interval' from now,
397
389
        # and every interval from then on.
398
390
        self.checker_initiator_tag = (gobject.timeout_add
399
391
                                      (self.interval_milliseconds(),
400
392
                                       self.start_checker))
401
393
        # Schedule a disable() when 'timeout' has passed
402
 
        self.expires = datetime.datetime.utcnow() + self.timeout
403
394
        self.disable_initiator_tag = (gobject.timeout_add
404
395
                                   (self.timeout_milliseconds(),
405
396
                                    self.disable))
406
397
        self.enabled = True
407
 
        self.last_enabled = datetime.datetime.utcnow()
408
398
        # Also start a new checker *right now*.
409
399
        self.start_checker()
410
400
    
419
409
        if getattr(self, "disable_initiator_tag", False):
420
410
            gobject.source_remove(self.disable_initiator_tag)
421
411
            self.disable_initiator_tag = None
422
 
        self.expires = None
423
412
        if getattr(self, "checker_initiator_tag", False):
424
413
            gobject.source_remove(self.checker_initiator_tag)
425
414
            self.checker_initiator_tag = None
451
440
            logger.warning("Checker for %(name)s crashed?",
452
441
                           vars(self))
453
442
    
454
 
    def checked_ok(self, timeout=None):
 
443
    def checked_ok(self):
455
444
        """Bump up the timeout for this client.
456
445
        
457
446
        This should only be called when the client has been seen,
458
447
        alive and well.
459
448
        """
460
 
        if timeout is None:
461
 
            timeout = self.timeout
462
449
        self.last_checked_ok = datetime.datetime.utcnow()
463
450
        gobject.source_remove(self.disable_initiator_tag)
464
 
        self.expires = datetime.datetime.utcnow() + timeout
465
451
        self.disable_initiator_tag = (gobject.timeout_add
466
 
                                      (_timedelta_to_milliseconds(timeout),
 
452
                                      (self.timeout_milliseconds(),
467
453
                                       self.disable))
468
454
    
469
455
    def need_approval(self):
510
496
                                       'replace')))
511
497
                    for attr in
512
498
                    self.runtime_expansions)
513
 
                
 
499
 
514
500
                try:
515
501
                    command = self.checker_command % escaped_attrs
516
502
                except TypeError as error:
562
548
                raise
563
549
        self.checker = None
564
550
 
565
 
 
566
551
def dbus_service_property(dbus_interface, signature="v",
567
552
                          access="readwrite", byte_arrays=False):
568
553
    """Decorators for marking methods of a DBusObjectWithProperties to
614
599
 
615
600
class DBusObjectWithProperties(dbus.service.Object):
616
601
    """A D-Bus object with properties.
617
 
    
 
602
 
618
603
    Classes inheriting from this can use the dbus_service_property
619
604
    decorator to expose methods as D-Bus properties.  It exposes the
620
605
    standard Get(), Set(), and GetAll() methods on the D-Bus.
627
612
    def _get_all_dbus_properties(self):
628
613
        """Returns a generator of (name, attribute) pairs
629
614
        """
630
 
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
 
                for cls in self.__class__.__mro__
632
 
                for name, prop in inspect.getmembers(cls, self._is_dbus_property))
 
615
        return ((prop._dbus_name, prop)
 
616
                for name, prop in
 
617
                inspect.getmembers(self, self._is_dbus_property))
633
618
    
634
619
    def _get_dbus_property(self, interface_name, property_name):
635
620
        """Returns a bound method if one exists which is a D-Bus
636
621
        property with the specified name and interface.
637
622
        """
638
 
        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:
641
 
                    return value.__get__(self)
642
 
        
 
623
        for name in (property_name,
 
624
                     property_name + "_dbus_property"):
 
625
            prop = getattr(self, name, None)
 
626
            if (prop is None
 
627
                or not self._is_dbus_property(prop)
 
628
                or prop._dbus_name != property_name
 
629
                or (interface_name and prop._dbus_interface
 
630
                    and interface_name != prop._dbus_interface)):
 
631
                continue
 
632
            return prop
643
633
        # No such property
644
634
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
645
635
                                   + interface_name + "."
646
636
                                   + property_name)
647
 
 
648
637
    
649
638
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
650
639
                         out_signature="v")
680
669
    def GetAll(self, interface_name):
681
670
        """Standard D-Bus property GetAll() method, see D-Bus
682
671
        standard.
683
 
        
 
672
 
684
673
        Note: Will not include properties with access="write".
685
674
        """
686
675
        all = {}
748
737
        return xmlstring
749
738
 
750
739
 
751
 
def datetime_to_dbus (dt, variant_level=0):
752
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
753
 
    if dt is None:
754
 
        return dbus.String("", variant_level = variant_level)
755
 
    return dbus.String(dt.isoformat(),
756
 
                       variant_level=variant_level)
757
 
 
758
 
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
759
 
    def __new__(mcs, name, bases, attr):
760
 
        for attrname, old_dbusobj in inspect.getmembers(bases[0]):
761
 
            new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
762
 
            if (getattr(old_dbusobj, "_dbus_is_signal", False)
763
 
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
764
 
                unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
765
 
                                    old_dbusobj.__closure__))["func"].cell_contents
766
 
                newfunc = types.FunctionType(unwrappedfunc.func_code,
767
 
                                             unwrappedfunc.func_globals,
768
 
                                             unwrappedfunc.func_name,
769
 
                                             unwrappedfunc.func_defaults,
770
 
                                             unwrappedfunc.func_closure)
771
 
                new_dbusfunc = dbus.service.signal(
772
 
                    new_interface, old_dbusobj._dbus_signature)(newfunc)            
773
 
                attr["_transitional_" + attrname] = new_dbusfunc
774
 
 
775
 
                def fixscope(func1, func2):
776
 
                    def newcall(*args, **kwargs):
777
 
                        func1(*args, **kwargs)
778
 
                        func2(*args, **kwargs)
779
 
                    return newcall
780
 
 
781
 
                attr[attrname] = fixscope(old_dbusobj, new_dbusfunc)
782
 
            
783
 
            elif (getattr(old_dbusobj, "_dbus_is_method", False)
784
 
                and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
785
 
                new_dbusfunc = (dbus.service.method
786
 
                                (new_interface,
787
 
                                 old_dbusobj._dbus_in_signature,
788
 
                                 old_dbusobj._dbus_out_signature)
789
 
                                (types.FunctionType
790
 
                                 (old_dbusobj.func_code,
791
 
                                  old_dbusobj.func_globals,
792
 
                                  old_dbusobj.func_name,
793
 
                                  old_dbusobj.func_defaults,
794
 
                                  old_dbusobj.func_closure)))
795
 
 
796
 
                attr[attrname] = new_dbusfunc
797
 
            elif (getattr(old_dbusobj, "_dbus_is_property", False)
798
 
                  and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
799
 
                new_dbusfunc = (dbus_service_property
800
 
                                (new_interface,
801
 
                                 old_dbusobj._dbus_signature,
802
 
                                 old_dbusobj._dbus_access,
803
 
                                 old_dbusobj._dbus_get_args_options["byte_arrays"])
804
 
                                (types.FunctionType
805
 
                                 (old_dbusobj.func_code,
806
 
                                  old_dbusobj.func_globals,
807
 
                                  old_dbusobj.func_name,
808
 
                                  old_dbusobj.func_defaults,
809
 
                                  old_dbusobj.func_closure)))
810
 
 
811
 
                attr[attrname] = new_dbusfunc
812
 
        return type.__new__(mcs, name, bases, attr)
813
 
 
814
740
class ClientDBus(Client, DBusObjectWithProperties):
815
741
    """A Client class using D-Bus
816
742
    
838
764
        DBusObjectWithProperties.__init__(self, self.bus,
839
765
                                          self.dbus_object_path)
840
766
        
841
 
    def notifychangeproperty(transform_func,
842
 
                             dbus_name, type_func=lambda x: x,
843
 
                             variant_level=1):
844
 
        """ Modify a variable so that its a property that announce its
845
 
        changes to DBus.
846
 
        transform_fun: Function that takes a value and transform it to
847
 
                       DBus type.
848
 
        dbus_name: DBus name of the variable
849
 
        type_func: Function that transform the value before sending it
850
 
                   to DBus
851
 
        variant_level: DBus variant level. default: 1
852
 
        """
853
 
        real_value = [None,]
854
 
        def setter(self, value):
855
 
            old_value = real_value[0]
856
 
            real_value[0] = value
857
 
            if hasattr(self, "dbus_object_path"):
858
 
                if type_func(old_value) != type_func(real_value[0]):
859
 
                    dbus_value = transform_func(type_func(real_value[0]),
860
 
                                                variant_level)
861
 
                    self.PropertyChanged(dbus.String(dbus_name),
862
 
                                         dbus_value)
863
 
        
864
 
        return property(lambda self: real_value[0], setter)
865
 
    
866
 
    
867
 
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
868
 
    approvals_pending = notifychangeproperty(dbus.Boolean,
869
 
                                             "ApprovalPending",
870
 
                                             type_func = bool)
871
 
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
872
 
    last_enabled = notifychangeproperty(datetime_to_dbus,
873
 
                                        "LastEnabled")
874
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
875
 
                                   type_func = lambda checker: checker is not None)
876
 
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
877
 
                                           "LastCheckedOK")
878
 
    last_approval_request = notifychangeproperty(datetime_to_dbus,
879
 
                                                 "LastApprovalRequest")
880
 
    approved_by_default = notifychangeproperty(dbus.Boolean,
881
 
                                               "ApprovedByDefault")
882
 
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
883
 
                                          type_func = _timedelta_to_milliseconds)
884
 
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
885
 
                                             type_func = _timedelta_to_milliseconds)
886
 
    host = notifychangeproperty(dbus.String, "Host")
887
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
888
 
                                   type_func = _timedelta_to_milliseconds)
889
 
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
890
 
                                            type_func = _timedelta_to_milliseconds)
891
 
    interval = notifychangeproperty(dbus.UInt16, "Interval",
892
 
                                    type_func = _timedelta_to_milliseconds)
893
 
    checker_command = notifychangeproperty(dbus.String, "Checker")
894
 
    
895
 
    del notifychangeproperty
 
767
    def _get_approvals_pending(self):
 
768
        return self._approvals_pending
 
769
    def _set_approvals_pending(self, value):
 
770
        old_value = self._approvals_pending
 
771
        self._approvals_pending = value
 
772
        bval = bool(value)
 
773
        if (hasattr(self, "dbus_object_path")
 
774
            and bval is not bool(old_value)):
 
775
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
776
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
777
                                 dbus_bool)
 
778
 
 
779
    approvals_pending = property(_get_approvals_pending,
 
780
                                 _set_approvals_pending)
 
781
    del _get_approvals_pending, _set_approvals_pending
 
782
    
 
783
    @staticmethod
 
784
    def _datetime_to_dbus(dt, variant_level=0):
 
785
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
786
        return dbus.String(dt.isoformat(),
 
787
                           variant_level=variant_level)
 
788
    
 
789
    def enable(self):
 
790
        oldstate = getattr(self, "enabled", False)
 
791
        r = Client.enable(self)
 
792
        if oldstate != self.enabled:
 
793
            # Emit D-Bus signals
 
794
            self.PropertyChanged(dbus.String("Enabled"),
 
795
                                 dbus.Boolean(True, variant_level=1))
 
796
            self.PropertyChanged(
 
797
                dbus.String("LastEnabled"),
 
798
                self._datetime_to_dbus(self.last_enabled,
 
799
                                       variant_level=1))
 
800
        return r
 
801
    
 
802
    def disable(self, quiet = False):
 
803
        oldstate = getattr(self, "enabled", False)
 
804
        r = Client.disable(self, quiet=quiet)
 
805
        if not quiet and oldstate != self.enabled:
 
806
            # Emit D-Bus signal
 
807
            self.PropertyChanged(dbus.String("Enabled"),
 
808
                                 dbus.Boolean(False, variant_level=1))
 
809
        return r
896
810
    
897
811
    def __del__(self, *args, **kwargs):
898
812
        try:
907
821
                         *args, **kwargs):
908
822
        self.checker_callback_tag = None
909
823
        self.checker = None
 
824
        # Emit D-Bus signal
 
825
        self.PropertyChanged(dbus.String("CheckerRunning"),
 
826
                             dbus.Boolean(False, variant_level=1))
910
827
        if os.WIFEXITED(condition):
911
828
            exitstatus = os.WEXITSTATUS(condition)
912
829
            # Emit D-Bus signal
922
839
        return Client.checker_callback(self, pid, condition, command,
923
840
                                       *args, **kwargs)
924
841
    
 
842
    def checked_ok(self, *args, **kwargs):
 
843
        Client.checked_ok(self, *args, **kwargs)
 
844
        # Emit D-Bus signal
 
845
        self.PropertyChanged(
 
846
            dbus.String("LastCheckedOK"),
 
847
            (self._datetime_to_dbus(self.last_checked_ok,
 
848
                                    variant_level=1)))
 
849
    
 
850
    def need_approval(self, *args, **kwargs):
 
851
        r = Client.need_approval(self, *args, **kwargs)
 
852
        # Emit D-Bus signal
 
853
        self.PropertyChanged(
 
854
            dbus.String("LastApprovalRequest"),
 
855
            (self._datetime_to_dbus(self.last_approval_request,
 
856
                                    variant_level=1)))
 
857
        return r
 
858
    
925
859
    def start_checker(self, *args, **kwargs):
926
860
        old_checker = self.checker
927
861
        if self.checker is not None:
934
868
            and old_checker_pid != self.checker.pid):
935
869
            # Emit D-Bus signal
936
870
            self.CheckerStarted(self.current_checker_command)
 
871
            self.PropertyChanged(
 
872
                dbus.String("CheckerRunning"),
 
873
                dbus.Boolean(True, variant_level=1))
937
874
        return r
938
875
    
 
876
    def stop_checker(self, *args, **kwargs):
 
877
        old_checker = getattr(self, "checker", None)
 
878
        r = Client.stop_checker(self, *args, **kwargs)
 
879
        if (old_checker is not None
 
880
            and getattr(self, "checker", None) is None):
 
881
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
882
                                 dbus.Boolean(False, variant_level=1))
 
883
        return r
 
884
 
939
885
    def _reset_approved(self):
940
886
        self._approved = None
941
887
        return False
943
889
    def approve(self, value=True):
944
890
        self.send_changedstate()
945
891
        self._approved = value
946
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
892
        gobject.timeout_add(self._timedelta_to_milliseconds
947
893
                            (self.approval_duration),
948
894
                            self._reset_approved)
949
895
    
950
896
    
951
897
    ## D-Bus methods, signals & properties
952
898
    _interface = "se.bsnet.fukt.Mandos.Client"
953
 
 
 
899
    
954
900
    ## Signals
955
901
    
956
902
    # CheckerCompleted - signal
1041
987
        if value is None:       # get
1042
988
            return dbus.Boolean(self.approved_by_default)
1043
989
        self.approved_by_default = bool(value)
 
990
        # Emit D-Bus signal
 
991
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
 
992
                             dbus.Boolean(value, variant_level=1))
1044
993
    
1045
994
    # ApprovalDelay - property
1046
995
    @dbus_service_property(_interface, signature="t",
1049
998
        if value is None:       # get
1050
999
            return dbus.UInt64(self.approval_delay_milliseconds())
1051
1000
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
1001
        # Emit D-Bus signal
 
1002
        self.PropertyChanged(dbus.String("ApprovalDelay"),
 
1003
                             dbus.UInt64(value, variant_level=1))
1052
1004
    
1053
1005
    # ApprovalDuration - property
1054
1006
    @dbus_service_property(_interface, signature="t",
1055
1007
                           access="readwrite")
1056
1008
    def ApprovalDuration_dbus_property(self, value=None):
1057
1009
        if value is None:       # get
1058
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
1010
            return dbus.UInt64(self._timedelta_to_milliseconds(
1059
1011
                    self.approval_duration))
1060
1012
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
1013
        # Emit D-Bus signal
 
1014
        self.PropertyChanged(dbus.String("ApprovalDuration"),
 
1015
                             dbus.UInt64(value, variant_level=1))
1061
1016
    
1062
1017
    # Name - property
1063
1018
    @dbus_service_property(_interface, signature="s", access="read")
1076
1031
        if value is None:       # get
1077
1032
            return dbus.String(self.host)
1078
1033
        self.host = value
 
1034
        # Emit D-Bus signal
 
1035
        self.PropertyChanged(dbus.String("Host"),
 
1036
                             dbus.String(value, variant_level=1))
1079
1037
    
1080
1038
    # Created - property
1081
1039
    @dbus_service_property(_interface, signature="s", access="read")
1082
1040
    def Created_dbus_property(self):
1083
 
        return dbus.String(datetime_to_dbus(self.created))
 
1041
        return dbus.String(self._datetime_to_dbus(self.created))
1084
1042
    
1085
1043
    # LastEnabled - property
1086
1044
    @dbus_service_property(_interface, signature="s", access="read")
1087
1045
    def LastEnabled_dbus_property(self):
1088
 
        return datetime_to_dbus(self.last_enabled)
 
1046
        if self.last_enabled is None:
 
1047
            return dbus.String("")
 
1048
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
1089
1049
    
1090
1050
    # Enabled - property
1091
1051
    @dbus_service_property(_interface, signature="b",
1105
1065
        if value is not None:
1106
1066
            self.checked_ok()
1107
1067
            return
1108
 
        return datetime_to_dbus(self.last_checked_ok)
1109
 
    
1110
 
    # Expires - property
1111
 
    @dbus_service_property(_interface, signature="s", access="read")
1112
 
    def Expires_dbus_property(self):
1113
 
        return datetime_to_dbus(self.expires)
 
1068
        if self.last_checked_ok is None:
 
1069
            return dbus.String("")
 
1070
        return dbus.String(self._datetime_to_dbus(self
 
1071
                                                  .last_checked_ok))
1114
1072
    
1115
1073
    # LastApprovalRequest - property
1116
1074
    @dbus_service_property(_interface, signature="s", access="read")
1117
1075
    def LastApprovalRequest_dbus_property(self):
1118
 
        return datetime_to_dbus(self.last_approval_request)
 
1076
        if self.last_approval_request is None:
 
1077
            return dbus.String("")
 
1078
        return dbus.String(self.
 
1079
                           _datetime_to_dbus(self
 
1080
                                             .last_approval_request))
1119
1081
    
1120
1082
    # Timeout - property
1121
1083
    @dbus_service_property(_interface, signature="t",
1124
1086
        if value is None:       # get
1125
1087
            return dbus.UInt64(self.timeout_milliseconds())
1126
1088
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1089
        # Emit D-Bus signal
 
1090
        self.PropertyChanged(dbus.String("Timeout"),
 
1091
                             dbus.UInt64(value, variant_level=1))
1127
1092
        if getattr(self, "disable_initiator_tag", None) is None:
1128
1093
            return
1129
1094
        # Reschedule timeout
1130
1095
        gobject.source_remove(self.disable_initiator_tag)
1131
1096
        self.disable_initiator_tag = None
1132
 
        self.expires = None
1133
1097
        time_to_die = (self.
1134
1098
                       _timedelta_to_milliseconds((self
1135
1099
                                                   .last_checked_ok
1140
1104
            # The timeout has passed
1141
1105
            self.disable()
1142
1106
        else:
1143
 
            self.expires = (datetime.datetime.utcnow()
1144
 
                            + datetime.timedelta(milliseconds = time_to_die))
1145
1107
            self.disable_initiator_tag = (gobject.timeout_add
1146
1108
                                          (time_to_die, self.disable))
1147
1109
    
1148
 
    # ExtendedTimeout - property
1149
 
    @dbus_service_property(_interface, signature="t",
1150
 
                           access="readwrite")
1151
 
    def ExtendedTimeout_dbus_property(self, value=None):
1152
 
        if value is None:       # get
1153
 
            return dbus.UInt64(self.extended_timeout_milliseconds())
1154
 
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1155
 
    
1156
1110
    # Interval - property
1157
1111
    @dbus_service_property(_interface, signature="t",
1158
1112
                           access="readwrite")
1160
1114
        if value is None:       # get
1161
1115
            return dbus.UInt64(self.interval_milliseconds())
1162
1116
        self.interval = datetime.timedelta(0, 0, 0, value)
 
1117
        # Emit D-Bus signal
 
1118
        self.PropertyChanged(dbus.String("Interval"),
 
1119
                             dbus.UInt64(value, variant_level=1))
1163
1120
        if getattr(self, "checker_initiator_tag", None) is None:
1164
1121
            return
1165
1122
        # Reschedule checker run
1167
1124
        self.checker_initiator_tag = (gobject.timeout_add
1168
1125
                                      (value, self.start_checker))
1169
1126
        self.start_checker()    # Start one now, too
1170
 
    
 
1127
 
1171
1128
    # Checker - property
1172
1129
    @dbus_service_property(_interface, signature="s",
1173
1130
                           access="readwrite")
1175
1132
        if value is None:       # get
1176
1133
            return dbus.String(self.checker_command)
1177
1134
        self.checker_command = value
 
1135
        # Emit D-Bus signal
 
1136
        self.PropertyChanged(dbus.String("Checker"),
 
1137
                             dbus.String(self.checker_command,
 
1138
                                         variant_level=1))
1178
1139
    
1179
1140
    # CheckerRunning - property
1180
1141
    @dbus_service_property(_interface, signature="b",
1207
1168
        self._pipe.send(('init', fpr, address))
1208
1169
        if not self._pipe.recv():
1209
1170
            raise KeyError()
1210
 
    
 
1171
 
1211
1172
    def __getattribute__(self, name):
1212
1173
        if(name == '_pipe'):
1213
1174
            return super(ProxyClient, self).__getattribute__(name)
1220
1181
                self._pipe.send(('funcall', name, args, kwargs))
1221
1182
                return self._pipe.recv()[1]
1222
1183
            return func
1223
 
    
 
1184
 
1224
1185
    def __setattr__(self, name, value):
1225
1186
        if(name == '_pipe'):
1226
1187
            return super(ProxyClient, self).__setattr__(name, value)
1227
1188
        self._pipe.send(('setattr', name, value))
1228
1189
 
1229
 
class ClientDBusTransitional(ClientDBus):
1230
 
    __metaclass__ = transitional_dbus_metaclass
1231
1190
 
1232
1191
class ClientHandler(socketserver.BaseRequestHandler, object):
1233
1192
    """A class to handle client connections.
1241
1200
                        unicode(self.client_address))
1242
1201
            logger.debug("Pipe FD: %d",
1243
1202
                         self.server.child_pipe.fileno())
1244
 
            
 
1203
 
1245
1204
            session = (gnutls.connection
1246
1205
                       .ClientSession(self.request,
1247
1206
                                      gnutls.connection
1248
1207
                                      .X509Credentials()))
1249
 
            
 
1208
 
1250
1209
            # Note: gnutls.connection.X509Credentials is really a
1251
1210
            # generic GnuTLS certificate credentials object so long as
1252
1211
            # no X.509 keys are added to it.  Therefore, we can use it
1253
1212
            # here despite using OpenPGP certificates.
1254
 
            
 
1213
 
1255
1214
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1256
1215
            #                      "+AES-256-CBC", "+SHA1",
1257
1216
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1263
1222
            (gnutls.library.functions
1264
1223
             .gnutls_priority_set_direct(session._c_object,
1265
1224
                                         priority, None))
1266
 
            
 
1225
 
1267
1226
            # Start communication using the Mandos protocol
1268
1227
            # Get protocol number
1269
1228
            line = self.request.makefile().readline()
1274
1233
            except (ValueError, IndexError, RuntimeError) as error:
1275
1234
                logger.error("Unknown protocol version: %s", error)
1276
1235
                return
1277
 
            
 
1236
 
1278
1237
            # Start GnuTLS connection
1279
1238
            try:
1280
1239
                session.handshake()
1284
1243
                # established.  Just abandon the request.
1285
1244
                return
1286
1245
            logger.debug("Handshake succeeded")
1287
 
            
 
1246
 
1288
1247
            approval_required = False
1289
1248
            try:
1290
1249
                try:
1295
1254
                    logger.warning("Bad certificate: %s", error)
1296
1255
                    return
1297
1256
                logger.debug("Fingerprint: %s", fpr)
1298
 
                
 
1257
 
1299
1258
                try:
1300
1259
                    client = ProxyClient(child_pipe, fpr,
1301
1260
                                         self.client_address)
1367
1326
                                 sent, len(client.secret)
1368
1327
                                 - (sent_size + sent))
1369
1328
                    sent_size += sent
1370
 
                
 
1329
 
1371
1330
                logger.info("Sending secret to %s", client.name)
1372
1331
                # bump the timeout as if seen
1373
 
                client.checked_ok(client.extended_timeout)
 
1332
                client.checked_ok()
1374
1333
                if self.server.use_dbus:
1375
1334
                    # Emit D-Bus signal
1376
1335
                    client.GotSecret()
1461
1420
        multiprocessing.Process(target = self.sub_process_main,
1462
1421
                                args = (request, address)).start()
1463
1422
 
1464
 
 
1465
1423
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1466
1424
    """ adds a pipe to the MixIn """
1467
1425
    def process_request(self, request, client_address):
1470
1428
        This function creates a new pipe in self.pipe
1471
1429
        """
1472
1430
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1473
 
        
 
1431
 
1474
1432
        super(MultiprocessingMixInWithPipe,
1475
1433
              self).process_request(request, client_address)
1476
1434
        self.child_pipe.close()
1477
1435
        self.add_pipe(parent_pipe)
1478
 
    
 
1436
 
1479
1437
    def add_pipe(self, parent_pipe):
1480
1438
        """Dummy function; override as necessary"""
1481
1439
        raise NotImplementedError
1482
1440
 
1483
 
 
1484
1441
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1485
1442
                     socketserver.TCPServer, object):
1486
1443
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1634
1591
            kwargs = request[3]
1635
1592
            
1636
1593
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1637
 
        
 
1594
 
1638
1595
        if command == 'getattr':
1639
1596
            attrname = request[1]
1640
1597
            if callable(client_object.__getattribute__(attrname)):
1646
1603
            attrname = request[1]
1647
1604
            value = request[2]
1648
1605
            setattr(client_object, attrname, value)
1649
 
        
 
1606
 
1650
1607
        return True
1651
1608
 
1652
1609
 
1831
1788
    debuglevel = server_settings["debuglevel"]
1832
1789
    use_dbus = server_settings["use_dbus"]
1833
1790
    use_ipv6 = server_settings["use_ipv6"]
1834
 
    
 
1791
 
1835
1792
    if server_settings["servicename"] != "Mandos":
1836
1793
        syslogger.setFormatter(logging.Formatter
1837
1794
                               ('Mandos (%s) [%%(process)d]:'
1839
1796
                                % server_settings["servicename"]))
1840
1797
    
1841
1798
    # Parse config file with clients
1842
 
    client_defaults = { "timeout": "5m",
1843
 
                        "extended_timeout": "15m",
1844
 
                        "interval": "2m",
 
1799
    client_defaults = { "timeout": "1h",
 
1800
                        "interval": "5m",
1845
1801
                        "checker": "fping -q -- %%(host)s",
1846
1802
                        "host": "",
1847
1803
                        "approval_delay": "0s",
1898
1854
        level = getattr(logging, debuglevel.upper())
1899
1855
        syslogger.setLevel(level)
1900
1856
        console.setLevel(level)
1901
 
    
 
1857
 
1902
1858
    if debug:
1903
1859
        # Enable all possible GnuTLS debugging
1904
1860
        
1937
1893
        try:
1938
1894
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1939
1895
                                            bus, do_not_queue=True)
1940
 
            bus_name2 = dbus.service.BusName("se.recompile.Mandos",
1941
 
                                            bus, do_not_queue=True)
1942
1896
        except dbus.exceptions.NameExistsException as e:
1943
1897
            logger.error(unicode(e) + ", disabling D-Bus")
1944
1898
            use_dbus = False
1957
1911
    
1958
1912
    client_class = Client
1959
1913
    if use_dbus:
1960
 
        client_class = functools.partial(ClientDBusTransitional, bus = bus)        
 
1914
        client_class = functools.partial(ClientDBus, bus = bus)
1961
1915
    def client_config_items(config, section):
1962
1916
        special_settings = {
1963
1917
            "approved_by_default":
1993
1947
        del pidfilename
1994
1948
        
1995
1949
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1996
 
    
 
1950
 
1997
1951
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1998
1952
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1999
1953
    
2050
2004
            
2051
2005
            del _interface
2052
2006
        
2053
 
        class MandosDBusServiceTransitional(MandosDBusService):
2054
 
            __metaclass__ = transitional_dbus_metaclass
2055
 
        mandos_dbus_service = MandosDBusServiceTransitional()
 
2007
        mandos_dbus_service = MandosDBusService()
2056
2008
    
2057
2009
    def cleanup():
2058
2010
        "Cleanup function; run on exit"
2122
2074
    # Must run before the D-Bus bus name gets deregistered
2123
2075
    cleanup()
2124
2076
 
2125
 
 
2126
2077
if __name__ == '__main__':
2127
2078
    main()