/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-10-02 13:45:45 UTC
  • mto: This revision was merged to the branch mainline in revision 505.
  • Revision ID: belorn@fukt.bsnet.se-20111002134545-oytmfbl15r8lsm6p
working transition code for going between se.bsnet.fukt to se.recompile

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
65
66
 
66
67
import dbus
67
68
import dbus.service
264
265
        self.server_state_changed(self.server.GetState())
265
266
 
266
267
 
 
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
        
267
274
class Client(object):
268
275
    """A representation of a client host served by this server.
269
276
    
297
304
    secret:     bytestring; sent verbatim (over TLS) to client
298
305
    timeout:    datetime.timedelta(); How long from last_checked_ok
299
306
                                      until this client is disabled
 
307
    extended_timeout:   extra long timeout when password has been sent
300
308
    runtime_expansions: Allowed attributes for runtime expansion.
 
309
    expires:    datetime.datetime(); time (UTC) when a client will be
 
310
                disabled, or None
301
311
    """
302
312
    
303
313
    runtime_expansions = ("approval_delay", "approval_duration",
305
315
                          "host", "interval", "last_checked_ok",
306
316
                          "last_enabled", "name", "timeout")
307
317
    
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
 
    
315
318
    def timeout_milliseconds(self):
316
319
        "Return the 'timeout' attribute in milliseconds"
317
 
        return self._timedelta_to_milliseconds(self.timeout)
 
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)    
318
325
    
319
326
    def interval_milliseconds(self):
320
327
        "Return the 'interval' attribute in milliseconds"
321
 
        return self._timedelta_to_milliseconds(self.interval)
322
 
 
 
328
        return _timedelta_to_milliseconds(self.interval)
 
329
    
323
330
    def approval_delay_milliseconds(self):
324
 
        return self._timedelta_to_milliseconds(self.approval_delay)
 
331
        return _timedelta_to_milliseconds(self.approval_delay)
325
332
    
326
333
    def __init__(self, name = None, disable_hook=None, config=None):
327
334
        """Note: the 'checker' key in 'config' sets the
354
361
        self.last_enabled = None
355
362
        self.last_checked_ok = None
356
363
        self.timeout = string_to_delta(config["timeout"])
 
364
        self.extended_timeout = string_to_delta(config["extended_timeout"])
357
365
        self.interval = string_to_delta(config["interval"])
358
366
        self.disable_hook = disable_hook
359
367
        self.checker = None
360
368
        self.checker_initiator_tag = None
361
369
        self.disable_initiator_tag = None
 
370
        self.expires = None
362
371
        self.checker_callback_tag = None
363
372
        self.checker_command = config["checker"]
364
373
        self.current_checker_command = None
384
393
            # Already enabled
385
394
            return
386
395
        self.send_changedstate()
387
 
        self.last_enabled = datetime.datetime.utcnow()
388
396
        # Schedule a new checker to be started an 'interval' from now,
389
397
        # and every interval from then on.
390
398
        self.checker_initiator_tag = (gobject.timeout_add
391
399
                                      (self.interval_milliseconds(),
392
400
                                       self.start_checker))
393
401
        # Schedule a disable() when 'timeout' has passed
 
402
        self.expires = datetime.datetime.utcnow() + self.timeout
394
403
        self.disable_initiator_tag = (gobject.timeout_add
395
404
                                   (self.timeout_milliseconds(),
396
405
                                    self.disable))
397
406
        self.enabled = True
 
407
        self.last_enabled = datetime.datetime.utcnow()
398
408
        # Also start a new checker *right now*.
399
409
        self.start_checker()
400
410
    
409
419
        if getattr(self, "disable_initiator_tag", False):
410
420
            gobject.source_remove(self.disable_initiator_tag)
411
421
            self.disable_initiator_tag = None
 
422
        self.expires = None
412
423
        if getattr(self, "checker_initiator_tag", False):
413
424
            gobject.source_remove(self.checker_initiator_tag)
414
425
            self.checker_initiator_tag = None
440
451
            logger.warning("Checker for %(name)s crashed?",
441
452
                           vars(self))
442
453
    
443
 
    def checked_ok(self):
 
454
    def checked_ok(self, timeout=None):
444
455
        """Bump up the timeout for this client.
445
456
        
446
457
        This should only be called when the client has been seen,
447
458
        alive and well.
448
459
        """
 
460
        if timeout is None:
 
461
            timeout = self.timeout
449
462
        self.last_checked_ok = datetime.datetime.utcnow()
450
463
        gobject.source_remove(self.disable_initiator_tag)
 
464
        self.expires = datetime.datetime.utcnow() + timeout
451
465
        self.disable_initiator_tag = (gobject.timeout_add
452
 
                                      (self.timeout_milliseconds(),
 
466
                                      (_timedelta_to_milliseconds(timeout),
453
467
                                       self.disable))
454
468
    
455
469
    def need_approval(self):
496
510
                                       'replace')))
497
511
                    for attr in
498
512
                    self.runtime_expansions)
499
 
 
 
513
                
500
514
                try:
501
515
                    command = self.checker_command % escaped_attrs
502
516
                except TypeError as error:
548
562
                raise
549
563
        self.checker = None
550
564
 
 
565
 
551
566
def dbus_service_property(dbus_interface, signature="v",
552
567
                          access="readwrite", byte_arrays=False):
553
568
    """Decorators for marking methods of a DBusObjectWithProperties to
599
614
 
600
615
class DBusObjectWithProperties(dbus.service.Object):
601
616
    """A D-Bus object with properties.
602
 
 
 
617
    
603
618
    Classes inheriting from this can use the dbus_service_property
604
619
    decorator to expose methods as D-Bus properties.  It exposes the
605
620
    standard Get(), Set(), and GetAll() methods on the D-Bus.
616
631
                for name, prop in
617
632
                inspect.getmembers(self, self._is_dbus_property))
618
633
    
 
634
#    def _get_dbus_property(self, interface_name, property_name):
 
635
#        """Returns a bound method if one exists which is a D-Bus
 
636
#        property with the specified name and interface.
 
637
#        """
 
638
#        print("get_property({0!r}, {1!r}".format(interface_name, property_name),file=sys.stderr)
 
639
#        print(dir(self), sys.stderr)
 
640
#        for name in (property_name,
 
641
#                     property_name + "_dbus_property"):
 
642
#            prop = getattr(self, name, None)
 
643
#            if (prop is None
 
644
#                or not self._is_dbus_property(prop)
 
645
#                or prop._dbus_name != property_name
 
646
#                or (interface_name and prop._dbus_interface
 
647
#                    and interface_name != prop._dbus_interface)):
 
648
#                continue
 
649
#            return prop
 
650
#        # No such property
 
651
#        raise DBusPropertyNotFound(self.dbus_object_path + ":"
 
652
#                                   + interface_name + "."
 
653
#                                   + property_name)
 
654
 
619
655
    def _get_dbus_property(self, interface_name, property_name):
620
656
        """Returns a bound method if one exists which is a D-Bus
621
657
        property with the specified name and interface.
622
658
        """
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
 
659
        for name, value in inspect.getmembers(self, self._is_dbus_property):
 
660
            if value._dbus_name == property_name and value._dbus_interface == interface_name:
 
661
                return value
 
662
        
633
663
        # No such property
634
664
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
635
665
                                   + interface_name + "."
636
666
                                   + property_name)
 
667
 
637
668
    
638
669
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
639
670
                         out_signature="v")
669
700
    def GetAll(self, interface_name):
670
701
        """Standard D-Bus property GetAll() method, see D-Bus
671
702
        standard.
672
 
 
 
703
        
673
704
        Note: Will not include properties with access="write".
674
705
        """
675
706
        all = {}
737
768
        return xmlstring
738
769
 
739
770
 
 
771
def datetime_to_dbus (dt, variant_level=0):
 
772
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
773
    if dt is None:
 
774
        return dbus.String("", variant_level = variant_level)
 
775
    return dbus.String(dt.isoformat(),
 
776
                       variant_level=variant_level)
 
777
 
 
778
class transitional_clientdbus(DBusObjectWithProperties.__metaclass__):
 
779
    def __new__(mcs, name, bases, attr):
 
780
        for key, old_dbusobj in attr.items():
 
781
            new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
 
782
            if getattr(old_dbusobj, "_dbus_is_signal", False):
 
783
                unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
 
784
                                    old_dbusobj.__closure__))["func"].cell_contents
 
785
                newfunc = types.FunctionType(unwrappedfunc.func_code,
 
786
                                             unwrappedfunc.func_globals,
 
787
                                             unwrappedfunc.func_name,
 
788
                                             unwrappedfunc.func_defaults,
 
789
                                             unwrappedfunc.func_closure)
 
790
                new_dbusfunc = dbus.service.signal(
 
791
                    new_interface, old_dbusobj._dbus_signature)(newfunc)            
 
792
                attr["_transitional_{0}_1".format(key)] = new_dbusfunc
 
793
                attr["_transitional_{0}_0".format(key)] = old_dbusobj                
 
794
                def fixscope(func1, func2):
 
795
                    def newcall(*args, **kwargs):
 
796
                        func1(*args, **kwargs)
 
797
                        func2(*args, **kwargs)
 
798
                    return newcall
 
799
 
 
800
                attr[key] = fixscope(
 
801
                    old_dbusobj, attr["_transitional_{0}_1".format(key)])
 
802
            
 
803
            if getattr(old_dbusobj, "_dbus_is_method", False):
 
804
                new_dbusfunc = (dbus.service.method
 
805
                                (new_interface,
 
806
                                 old_dbusobj._dbus_in_signature,
 
807
                                 old_dbusobj._dbus_out_signature)
 
808
                                (types.FunctionType
 
809
                                 (old_dbusobj.func_code,
 
810
                                  old_dbusobj.func_globals,
 
811
                                  old_dbusobj.func_name,
 
812
                                  old_dbusobj.func_defaults,
 
813
                                  old_dbusobj.func_closure)))
 
814
 
 
815
                attr["_transitional_{0}".format(key)] = new_dbusfunc
 
816
            if getattr(old_dbusobj, "_dbus_is_property", False):
 
817
                new_dbusfunc = (dbus_service_property
 
818
                                (new_interface,
 
819
                                 old_dbusobj._dbus_signature,
 
820
                                 old_dbusobj._dbus_access,
 
821
                                 old_dbusobj._dbus_get_args_options["byte_arrays"])
 
822
                                (types.FunctionType
 
823
                                 (old_dbusobj.func_code,
 
824
                                  old_dbusobj.func_globals,
 
825
                                  old_dbusobj.func_name,
 
826
                                  old_dbusobj.func_defaults,
 
827
                                  old_dbusobj.func_closure)))
 
828
 
 
829
                attr["_transitional_{0}".format(key)] = new_dbusfunc
 
830
        return type.__new__(mcs, name, bases, attr)
 
831
 
740
832
class ClientDBus(Client, DBusObjectWithProperties):
741
833
    """A Client class using D-Bus
742
834
    
747
839
    
748
840
    runtime_expansions = (Client.runtime_expansions
749
841
                          + ("dbus_object_path",))
 
842
 
 
843
    __metaclass__ = transitional_clientdbus
750
844
    
751
845
    # dbus.service.Object doesn't use super(), so we can't either.
752
846
    
764
858
        DBusObjectWithProperties.__init__(self, self.bus,
765
859
                                          self.dbus_object_path)
766
860
        
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
 
861
    def notifychangeproperty(transform_func,
 
862
                             dbus_name, type_func=lambda x: x,
 
863
                             variant_level=1):
 
864
        """ Modify a variable so that its a property that announce its
 
865
        changes to DBus.
 
866
        transform_fun: Function that takes a value and transform it to
 
867
                       DBus type.
 
868
        dbus_name: DBus name of the variable
 
869
        type_func: Function that transform the value before sending it
 
870
                   to DBus
 
871
        variant_level: DBus variant level. default: 1
 
872
        """
 
873
        real_value = [None,]
 
874
        def setter(self, value):
 
875
            old_value = real_value[0]
 
876
            real_value[0] = value
 
877
            if hasattr(self, "dbus_object_path"):
 
878
                if type_func(old_value) != type_func(real_value[0]):
 
879
                    dbus_value = transform_func(type_func(real_value[0]),
 
880
                                                variant_level)
 
881
                    self.PropertyChanged(dbus.String(dbus_name),
 
882
                                         dbus_value)
 
883
        
 
884
        return property(lambda self: real_value[0], setter)
 
885
    
 
886
    
 
887
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
 
888
    approvals_pending = notifychangeproperty(dbus.Boolean,
 
889
                                             "ApprovalPending",
 
890
                                             type_func = bool)
 
891
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
 
892
    last_enabled = notifychangeproperty(datetime_to_dbus,
 
893
                                        "LastEnabled")
 
894
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
 
895
                                   type_func = lambda checker: checker is not None)
 
896
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
 
897
                                           "LastCheckedOK")
 
898
    last_approval_request = notifychangeproperty(datetime_to_dbus,
 
899
                                                 "LastApprovalRequest")
 
900
    approved_by_default = notifychangeproperty(dbus.Boolean,
 
901
                                               "ApprovedByDefault")
 
902
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
 
903
                                          type_func = _timedelta_to_milliseconds)
 
904
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
 
905
                                             type_func = _timedelta_to_milliseconds)
 
906
    host = notifychangeproperty(dbus.String, "Host")
 
907
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
 
908
                                   type_func = _timedelta_to_milliseconds)
 
909
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
 
910
                                            type_func = _timedelta_to_milliseconds)
 
911
    interval = notifychangeproperty(dbus.UInt16, "Interval",
 
912
                                    type_func = _timedelta_to_milliseconds)
 
913
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
914
    
 
915
    del notifychangeproperty
810
916
    
811
917
    def __del__(self, *args, **kwargs):
812
918
        try:
821
927
                         *args, **kwargs):
822
928
        self.checker_callback_tag = None
823
929
        self.checker = None
824
 
        # Emit D-Bus signal
825
 
        self.PropertyChanged(dbus.String("CheckerRunning"),
826
 
                             dbus.Boolean(False, variant_level=1))
827
930
        if os.WIFEXITED(condition):
828
931
            exitstatus = os.WEXITSTATUS(condition)
829
932
            # Emit D-Bus signal
839
942
        return Client.checker_callback(self, pid, condition, command,
840
943
                                       *args, **kwargs)
841
944
    
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
 
    
859
945
    def start_checker(self, *args, **kwargs):
860
946
        old_checker = self.checker
861
947
        if self.checker is not None:
868
954
            and old_checker_pid != self.checker.pid):
869
955
            # Emit D-Bus signal
870
956
            self.CheckerStarted(self.current_checker_command)
871
 
            self.PropertyChanged(
872
 
                dbus.String("CheckerRunning"),
873
 
                dbus.Boolean(True, variant_level=1))
874
957
        return r
875
958
    
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
 
 
885
959
    def _reset_approved(self):
886
960
        self._approved = None
887
961
        return False
889
963
    def approve(self, value=True):
890
964
        self.send_changedstate()
891
965
        self._approved = value
892
 
        gobject.timeout_add(self._timedelta_to_milliseconds
 
966
        gobject.timeout_add(_timedelta_to_milliseconds
893
967
                            (self.approval_duration),
894
968
                            self._reset_approved)
895
969
    
896
970
    
897
971
    ## D-Bus methods, signals & properties
898
972
    _interface = "se.bsnet.fukt.Mandos.Client"
899
 
    
 
973
 
900
974
    ## Signals
901
975
    
902
976
    # CheckerCompleted - signal
987
1061
        if value is None:       # get
988
1062
            return dbus.Boolean(self.approved_by_default)
989
1063
        self.approved_by_default = bool(value)
990
 
        # Emit D-Bus signal
991
 
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
992
 
                             dbus.Boolean(value, variant_level=1))
993
1064
    
994
1065
    # ApprovalDelay - property
995
1066
    @dbus_service_property(_interface, signature="t",
998
1069
        if value is None:       # get
999
1070
            return dbus.UInt64(self.approval_delay_milliseconds())
1000
1071
        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))
1004
1072
    
1005
1073
    # ApprovalDuration - property
1006
1074
    @dbus_service_property(_interface, signature="t",
1007
1075
                           access="readwrite")
1008
1076
    def ApprovalDuration_dbus_property(self, value=None):
1009
1077
        if value is None:       # get
1010
 
            return dbus.UInt64(self._timedelta_to_milliseconds(
 
1078
            return dbus.UInt64(_timedelta_to_milliseconds(
1011
1079
                    self.approval_duration))
1012
1080
        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))
1016
1081
    
1017
1082
    # Name - property
1018
1083
    @dbus_service_property(_interface, signature="s", access="read")
1031
1096
        if value is None:       # get
1032
1097
            return dbus.String(self.host)
1033
1098
        self.host = value
1034
 
        # Emit D-Bus signal
1035
 
        self.PropertyChanged(dbus.String("Host"),
1036
 
                             dbus.String(value, variant_level=1))
1037
1099
    
1038
1100
    # Created - property
1039
1101
    @dbus_service_property(_interface, signature="s", access="read")
1040
1102
    def Created_dbus_property(self):
1041
 
        return dbus.String(self._datetime_to_dbus(self.created))
 
1103
        return dbus.String(datetime_to_dbus(self.created))
1042
1104
    
1043
1105
    # LastEnabled - property
1044
1106
    @dbus_service_property(_interface, signature="s", access="read")
1045
1107
    def LastEnabled_dbus_property(self):
1046
 
        if self.last_enabled is None:
1047
 
            return dbus.String("")
1048
 
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
1108
        return datetime_to_dbus(self.last_enabled)
1049
1109
    
1050
1110
    # Enabled - property
1051
1111
    @dbus_service_property(_interface, signature="b",
1065
1125
        if value is not None:
1066
1126
            self.checked_ok()
1067
1127
            return
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))
 
1128
        return datetime_to_dbus(self.last_checked_ok)
 
1129
    
 
1130
    # Expires - property
 
1131
    @dbus_service_property(_interface, signature="s", access="read")
 
1132
    def Expires_dbus_property(self):
 
1133
        return datetime_to_dbus(self.expires)
1072
1134
    
1073
1135
    # LastApprovalRequest - property
1074
1136
    @dbus_service_property(_interface, signature="s", access="read")
1075
1137
    def LastApprovalRequest_dbus_property(self):
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))
 
1138
        return datetime_to_dbus(self.last_approval_request)
1081
1139
    
1082
1140
    # Timeout - property
1083
1141
    @dbus_service_property(_interface, signature="t",
1086
1144
        if value is None:       # get
1087
1145
            return dbus.UInt64(self.timeout_milliseconds())
1088
1146
        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))
1092
1147
        if getattr(self, "disable_initiator_tag", None) is None:
1093
1148
            return
1094
1149
        # Reschedule timeout
1095
1150
        gobject.source_remove(self.disable_initiator_tag)
1096
1151
        self.disable_initiator_tag = None
 
1152
        self.expires = None
1097
1153
        time_to_die = (self.
1098
1154
                       _timedelta_to_milliseconds((self
1099
1155
                                                   .last_checked_ok
1104
1160
            # The timeout has passed
1105
1161
            self.disable()
1106
1162
        else:
 
1163
            self.expires = (datetime.datetime.utcnow()
 
1164
                            + datetime.timedelta(milliseconds = time_to_die))
1107
1165
            self.disable_initiator_tag = (gobject.timeout_add
1108
1166
                                          (time_to_die, self.disable))
1109
1167
    
 
1168
    # ExtendedTimeout - property
 
1169
    @dbus_service_property(_interface, signature="t",
 
1170
                           access="readwrite")
 
1171
    def ExtendedTimeout_dbus_property(self, value=None):
 
1172
        if value is None:       # get
 
1173
            return dbus.UInt64(self.extended_timeout_milliseconds())
 
1174
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
 
1175
    
1110
1176
    # Interval - property
1111
1177
    @dbus_service_property(_interface, signature="t",
1112
1178
                           access="readwrite")
1114
1180
        if value is None:       # get
1115
1181
            return dbus.UInt64(self.interval_milliseconds())
1116
1182
        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))
1120
1183
        if getattr(self, "checker_initiator_tag", None) is None:
1121
1184
            return
1122
1185
        # Reschedule checker run
1124
1187
        self.checker_initiator_tag = (gobject.timeout_add
1125
1188
                                      (value, self.start_checker))
1126
1189
        self.start_checker()    # Start one now, too
1127
 
 
 
1190
    
1128
1191
    # Checker - property
1129
1192
    @dbus_service_property(_interface, signature="s",
1130
1193
                           access="readwrite")
1132
1195
        if value is None:       # get
1133
1196
            return dbus.String(self.checker_command)
1134
1197
        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))
1139
1198
    
1140
1199
    # CheckerRunning - property
1141
1200
    @dbus_service_property(_interface, signature="b",
1168
1227
        self._pipe.send(('init', fpr, address))
1169
1228
        if not self._pipe.recv():
1170
1229
            raise KeyError()
1171
 
 
 
1230
    
1172
1231
    def __getattribute__(self, name):
1173
1232
        if(name == '_pipe'):
1174
1233
            return super(ProxyClient, self).__getattribute__(name)
1181
1240
                self._pipe.send(('funcall', name, args, kwargs))
1182
1241
                return self._pipe.recv()[1]
1183
1242
            return func
1184
 
 
 
1243
    
1185
1244
    def __setattr__(self, name, value):
1186
1245
        if(name == '_pipe'):
1187
1246
            return super(ProxyClient, self).__setattr__(name, value)
1200
1259
                        unicode(self.client_address))
1201
1260
            logger.debug("Pipe FD: %d",
1202
1261
                         self.server.child_pipe.fileno())
1203
 
 
 
1262
            
1204
1263
            session = (gnutls.connection
1205
1264
                       .ClientSession(self.request,
1206
1265
                                      gnutls.connection
1207
1266
                                      .X509Credentials()))
1208
 
 
 
1267
            
1209
1268
            # Note: gnutls.connection.X509Credentials is really a
1210
1269
            # generic GnuTLS certificate credentials object so long as
1211
1270
            # no X.509 keys are added to it.  Therefore, we can use it
1212
1271
            # here despite using OpenPGP certificates.
1213
 
 
 
1272
            
1214
1273
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1215
1274
            #                      "+AES-256-CBC", "+SHA1",
1216
1275
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1222
1281
            (gnutls.library.functions
1223
1282
             .gnutls_priority_set_direct(session._c_object,
1224
1283
                                         priority, None))
1225
 
 
 
1284
            
1226
1285
            # Start communication using the Mandos protocol
1227
1286
            # Get protocol number
1228
1287
            line = self.request.makefile().readline()
1233
1292
            except (ValueError, IndexError, RuntimeError) as error:
1234
1293
                logger.error("Unknown protocol version: %s", error)
1235
1294
                return
1236
 
 
 
1295
            
1237
1296
            # Start GnuTLS connection
1238
1297
            try:
1239
1298
                session.handshake()
1243
1302
                # established.  Just abandon the request.
1244
1303
                return
1245
1304
            logger.debug("Handshake succeeded")
1246
 
 
 
1305
            
1247
1306
            approval_required = False
1248
1307
            try:
1249
1308
                try:
1254
1313
                    logger.warning("Bad certificate: %s", error)
1255
1314
                    return
1256
1315
                logger.debug("Fingerprint: %s", fpr)
1257
 
 
 
1316
                
1258
1317
                try:
1259
1318
                    client = ProxyClient(child_pipe, fpr,
1260
1319
                                         self.client_address)
1326
1385
                                 sent, len(client.secret)
1327
1386
                                 - (sent_size + sent))
1328
1387
                    sent_size += sent
1329
 
 
 
1388
                
1330
1389
                logger.info("Sending secret to %s", client.name)
1331
1390
                # bump the timeout as if seen
1332
 
                client.checked_ok()
 
1391
                client.checked_ok(client.extended_timeout)
1333
1392
                if self.server.use_dbus:
1334
1393
                    # Emit D-Bus signal
1335
1394
                    client.GotSecret()
1420
1479
        multiprocessing.Process(target = self.sub_process_main,
1421
1480
                                args = (request, address)).start()
1422
1481
 
 
1482
 
1423
1483
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1424
1484
    """ adds a pipe to the MixIn """
1425
1485
    def process_request(self, request, client_address):
1428
1488
        This function creates a new pipe in self.pipe
1429
1489
        """
1430
1490
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1431
 
 
 
1491
        
1432
1492
        super(MultiprocessingMixInWithPipe,
1433
1493
              self).process_request(request, client_address)
1434
1494
        self.child_pipe.close()
1435
1495
        self.add_pipe(parent_pipe)
1436
 
 
 
1496
    
1437
1497
    def add_pipe(self, parent_pipe):
1438
1498
        """Dummy function; override as necessary"""
1439
1499
        raise NotImplementedError
1440
1500
 
 
1501
 
1441
1502
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1442
1503
                     socketserver.TCPServer, object):
1443
1504
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1591
1652
            kwargs = request[3]
1592
1653
            
1593
1654
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1594
 
 
 
1655
        
1595
1656
        if command == 'getattr':
1596
1657
            attrname = request[1]
1597
1658
            if callable(client_object.__getattribute__(attrname)):
1603
1664
            attrname = request[1]
1604
1665
            value = request[2]
1605
1666
            setattr(client_object, attrname, value)
1606
 
 
 
1667
        
1607
1668
        return True
1608
1669
 
1609
1670
 
1788
1849
    debuglevel = server_settings["debuglevel"]
1789
1850
    use_dbus = server_settings["use_dbus"]
1790
1851
    use_ipv6 = server_settings["use_ipv6"]
1791
 
 
 
1852
    
1792
1853
    if server_settings["servicename"] != "Mandos":
1793
1854
        syslogger.setFormatter(logging.Formatter
1794
1855
                               ('Mandos (%s) [%%(process)d]:'
1796
1857
                                % server_settings["servicename"]))
1797
1858
    
1798
1859
    # Parse config file with clients
1799
 
    client_defaults = { "timeout": "1h",
1800
 
                        "interval": "5m",
 
1860
    client_defaults = { "timeout": "5m",
 
1861
                        "extended_timeout": "15m",
 
1862
                        "interval": "2m",
1801
1863
                        "checker": "fping -q -- %%(host)s",
1802
1864
                        "host": "",
1803
1865
                        "approval_delay": "0s",
1854
1916
        level = getattr(logging, debuglevel.upper())
1855
1917
        syslogger.setLevel(level)
1856
1918
        console.setLevel(level)
1857
 
 
 
1919
    
1858
1920
    if debug:
1859
1921
        # Enable all possible GnuTLS debugging
1860
1922
        
1893
1955
        try:
1894
1956
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1895
1957
                                            bus, do_not_queue=True)
 
1958
            bus_name2 = dbus.service.BusName("se.recompile.Mandos",
 
1959
                                            bus, do_not_queue=True)
1896
1960
        except dbus.exceptions.NameExistsException as e:
1897
1961
            logger.error(unicode(e) + ", disabling D-Bus")
1898
1962
            use_dbus = False
1947
2011
        del pidfilename
1948
2012
        
1949
2013
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1950
 
 
 
2014
    
1951
2015
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1952
2016
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1953
2017
    
2074
2138
    # Must run before the D-Bus bus name gets deregistered
2075
2139
    cleanup()
2076
2140
 
 
2141
 
2077
2142
if __name__ == '__main__':
2078
2143
    main()