/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

Change "fukt.bsnet.se" to "recompile.se" throughout.

* README: - '' -
* debian/control: - '' -
* debian/copyright: - '' -
* debian/mandos-client.README.Debian: - '' - and some rewriting.
* debian/mandos.README.Debian: - '' -
* debian/watch: Change "fukt.bsnet.se" to "recompile.se".
* init.d-mandos: - '' -
* intro.xml: - '' -
* mandos: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-ctl: - '' -
* mandos-ctl.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos-monitor: - '' -
* mandos-monitor.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.lsm: - '' -
* mandos.xml: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/plymouth.c: - '' -
* plugins.d/plymouth.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
# along with this program.  If not, see
29
29
# <http://www.gnu.org/licenses/>.
30
30
31
 
# Contact the authors at <mandos@fukt.bsnet.se>.
 
31
# Contact the authors at <mandos@recompile.se>.
32
32
33
33
 
34
34
from __future__ import (division, absolute_import, print_function,
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.
612
627
    def _get_all_dbus_properties(self):
613
628
        """Returns a generator of (name, attribute) pairs
614
629
        """
615
 
        return ((prop._dbus_name, prop)
616
 
                for name, prop in
617
 
                inspect.getmembers(self, self._is_dbus_property))
 
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))
618
633
    
619
634
    def _get_dbus_property(self, interface_name, property_name):
620
635
        """Returns a bound method if one exists which is a D-Bus
621
636
        property with the specified name and interface.
622
637
        """
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
 
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
        
633
643
        # No such property
634
644
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
635
645
                                   + interface_name + "."
669
679
    def GetAll(self, interface_name):
670
680
        """Standard D-Bus property GetAll() method, see D-Bus
671
681
        standard.
672
 
 
 
682
        
673
683
        Note: Will not include properties with access="write".
674
684
        """
675
685
        all = {}
737
747
        return xmlstring
738
748
 
739
749
 
 
750
def datetime_to_dbus (dt, variant_level=0):
 
751
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
752
    if dt is None:
 
753
        return dbus.String("", variant_level = variant_level)
 
754
    return dbus.String(dt.isoformat(),
 
755
                       variant_level=variant_level)
 
756
 
 
757
class AlternateDBusNamesMetaclass(DBusObjectWithProperties.__metaclass__):
 
758
    """Applied to an empty subclass of a D-Bus object, this metaclass
 
759
    will add additional D-Bus attributes matching a certain pattern.
 
760
    """
 
761
    def __new__(mcs, name, bases, attr):
 
762
        # Go through all the base classes which could have D-Bus
 
763
        # methods, signals, or properties in them
 
764
        for base in (b for b in bases
 
765
                     if issubclass(b, dbus.service.Object)):
 
766
            # Go though all attributes of the base class
 
767
            for attrname, attribute in inspect.getmembers(base):
 
768
                # Ignore non-D-Bus attributes, and D-Bus attributes
 
769
                # with the wrong interface name
 
770
                if (not hasattr(attribute, "_dbus_interface")
 
771
                    or not attribute._dbus_interface
 
772
                    .startswith("se.recompile.Mandos")):
 
773
                    continue
 
774
                # Create an alternate D-Bus interface name based on
 
775
                # the current name
 
776
                alt_interface = (attribute._dbus_interface
 
777
                                 .replace("se.recompile.Mandos",
 
778
                                          "se.bsnet.fukt.Mandos"))
 
779
                # Is this a D-Bus signal?
 
780
                if getattr(attribute, "_dbus_is_signal", False):
 
781
                    # Extract the original non-method function by
 
782
                    # black magic
 
783
                    nonmethod_func = (dict(
 
784
                            zip(attribute.func_code.co_freevars,
 
785
                                attribute.__closure__))["func"]
 
786
                                      .cell_contents)
 
787
                    # Create a new, but exactly alike, function
 
788
                    # object, and decorate it to be a new D-Bus signal
 
789
                    # with the alternate D-Bus interface name
 
790
                    new_function = (dbus.service.signal
 
791
                                    (alt_interface,
 
792
                                     attribute._dbus_signature)
 
793
                                    (types.FunctionType(
 
794
                                nonmethod_func.func_code,
 
795
                                nonmethod_func.func_globals,
 
796
                                nonmethod_func.func_name,
 
797
                                nonmethod_func.func_defaults,
 
798
                                nonmethod_func.func_closure)))
 
799
                    # Define a creator of a function to call both the
 
800
                    # old and new functions, so both the old and new
 
801
                    # signals gets sent when the function is called
 
802
                    def fixscope(func1, func2):
 
803
                        """This function is a scope container to pass
 
804
                        func1 and func2 to the "call_both" function
 
805
                        outside of its arguments"""
 
806
                        def call_both(*args, **kwargs):
 
807
                            """This function will emit two D-Bus
 
808
                            signals by calling func1 and func2"""
 
809
                            func1(*args, **kwargs)
 
810
                            func2(*args, **kwargs)
 
811
                        return call_both
 
812
                    # Create the "call_both" function and add it to
 
813
                    # the class
 
814
                    attr[attrname] = fixscope(attribute,
 
815
                                              new_function)
 
816
                # Is this a D-Bus method?
 
817
                elif getattr(attribute, "_dbus_is_method", False):
 
818
                    # Create a new, but exactly alike, function
 
819
                    # object.  Decorate it to be a new D-Bus method
 
820
                    # with the alternate D-Bus interface name.  Add it
 
821
                    # to the class.
 
822
                    attr[attrname] = (dbus.service.method
 
823
                                      (alt_interface,
 
824
                                       attribute._dbus_in_signature,
 
825
                                       attribute._dbus_out_signature)
 
826
                                      (types.FunctionType
 
827
                                       (attribute.func_code,
 
828
                                        attribute.func_globals,
 
829
                                        attribute.func_name,
 
830
                                        attribute.func_defaults,
 
831
                                        attribute.func_closure)))
 
832
                # Is this a D-Bus property?
 
833
                elif getattr(attribute, "_dbus_is_property", False):
 
834
                    # Create a new, but exactly alike, function
 
835
                    # object, and decorate it to be a new D-Bus
 
836
                    # property with the alternate D-Bus interface
 
837
                    # name.  Add it to the class.
 
838
                    attr[attrname] = (dbus_service_property
 
839
                                      (alt_interface,
 
840
                                       attribute._dbus_signature,
 
841
                                       attribute._dbus_access,
 
842
                                       attribute
 
843
                                       ._dbus_get_args_options
 
844
                                       ["byte_arrays"])
 
845
                                      (types.FunctionType
 
846
                                       (attribute.func_code,
 
847
                                        attribute.func_globals,
 
848
                                        attribute.func_name,
 
849
                                        attribute.func_defaults,
 
850
                                        attribute.func_closure)))
 
851
        return type.__new__(mcs, name, bases, attr)
 
852
 
740
853
class ClientDBus(Client, DBusObjectWithProperties):
741
854
    """A Client class using D-Bus
742
855
    
764
877
        DBusObjectWithProperties.__init__(self, self.bus,
765
878
                                          self.dbus_object_path)
766
879
        
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)
 
880
    def notifychangeproperty(transform_func,
 
881
                             dbus_name, type_func=lambda x: x,
 
882
                             variant_level=1):
 
883
        """ Modify a variable so that it's a property which announces
 
884
        its changes to DBus.
778
885
 
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
 
886
        transform_fun: Function that takes a value and transforms it
 
887
                       to a D-Bus type.
 
888
        dbus_name: D-Bus name of the variable
 
889
        type_func: Function that transform the value before sending it
 
890
                   to the D-Bus.  Default: no transform
 
891
        variant_level: D-Bus variant level.  Default: 1
 
892
        """
 
893
        real_value = [None,]
 
894
        def setter(self, value):
 
895
            old_value = real_value[0]
 
896
            real_value[0] = value
 
897
            if hasattr(self, "dbus_object_path"):
 
898
                if type_func(old_value) != type_func(real_value[0]):
 
899
                    dbus_value = transform_func(type_func(real_value[0]),
 
900
                                                variant_level)
 
901
                    self.PropertyChanged(dbus.String(dbus_name),
 
902
                                         dbus_value)
 
903
        
 
904
        return property(lambda self: real_value[0], setter)
 
905
    
 
906
    
 
907
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
 
908
    approvals_pending = notifychangeproperty(dbus.Boolean,
 
909
                                             "ApprovalPending",
 
910
                                             type_func = bool)
 
911
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
 
912
    last_enabled = notifychangeproperty(datetime_to_dbus,
 
913
                                        "LastEnabled")
 
914
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
 
915
                                   type_func = lambda checker: checker is not None)
 
916
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
 
917
                                           "LastCheckedOK")
 
918
    last_approval_request = notifychangeproperty(datetime_to_dbus,
 
919
                                                 "LastApprovalRequest")
 
920
    approved_by_default = notifychangeproperty(dbus.Boolean,
 
921
                                               "ApprovedByDefault")
 
922
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
 
923
                                          type_func = _timedelta_to_milliseconds)
 
924
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
 
925
                                             type_func = _timedelta_to_milliseconds)
 
926
    host = notifychangeproperty(dbus.String, "Host")
 
927
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
 
928
                                   type_func = _timedelta_to_milliseconds)
 
929
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
 
930
                                            type_func = _timedelta_to_milliseconds)
 
931
    interval = notifychangeproperty(dbus.UInt16, "Interval",
 
932
                                    type_func = _timedelta_to_milliseconds)
 
933
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
934
    
 
935
    del notifychangeproperty
810
936
    
811
937
    def __del__(self, *args, **kwargs):
812
938
        try:
821
947
                         *args, **kwargs):
822
948
        self.checker_callback_tag = None
823
949
        self.checker = None
824
 
        # Emit D-Bus signal
825
 
        self.PropertyChanged(dbus.String("CheckerRunning"),
826
 
                             dbus.Boolean(False, variant_level=1))
827
950
        if os.WIFEXITED(condition):
828
951
            exitstatus = os.WEXITSTATUS(condition)
829
952
            # Emit D-Bus signal
839
962
        return Client.checker_callback(self, pid, condition, command,
840
963
                                       *args, **kwargs)
841
964
    
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
965
    def start_checker(self, *args, **kwargs):
860
966
        old_checker = self.checker
861
967
        if self.checker is not None:
868
974
            and old_checker_pid != self.checker.pid):
869
975
            # Emit D-Bus signal
870
976
            self.CheckerStarted(self.current_checker_command)
871
 
            self.PropertyChanged(
872
 
                dbus.String("CheckerRunning"),
873
 
                dbus.Boolean(True, variant_level=1))
874
977
        return r
875
978
    
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
979
    def _reset_approved(self):
886
980
        self._approved = None
887
981
        return False
889
983
    def approve(self, value=True):
890
984
        self.send_changedstate()
891
985
        self._approved = value
892
 
        gobject.timeout_add(self._timedelta_to_milliseconds
 
986
        gobject.timeout_add(_timedelta_to_milliseconds
893
987
                            (self.approval_duration),
894
988
                            self._reset_approved)
895
989
    
896
990
    
897
991
    ## D-Bus methods, signals & properties
898
 
    _interface = "se.bsnet.fukt.Mandos.Client"
 
992
    _interface = "se.recompile.Mandos.Client"
899
993
    
900
994
    ## Signals
901
995
    
987
1081
        if value is None:       # get
988
1082
            return dbus.Boolean(self.approved_by_default)
989
1083
        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
1084
    
994
1085
    # ApprovalDelay - property
995
1086
    @dbus_service_property(_interface, signature="t",
998
1089
        if value is None:       # get
999
1090
            return dbus.UInt64(self.approval_delay_milliseconds())
1000
1091
        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
1092
    
1005
1093
    # ApprovalDuration - property
1006
1094
    @dbus_service_property(_interface, signature="t",
1007
1095
                           access="readwrite")
1008
1096
    def ApprovalDuration_dbus_property(self, value=None):
1009
1097
        if value is None:       # get
1010
 
            return dbus.UInt64(self._timedelta_to_milliseconds(
 
1098
            return dbus.UInt64(_timedelta_to_milliseconds(
1011
1099
                    self.approval_duration))
1012
1100
        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
1101
    
1017
1102
    # Name - property
1018
1103
    @dbus_service_property(_interface, signature="s", access="read")
1031
1116
        if value is None:       # get
1032
1117
            return dbus.String(self.host)
1033
1118
        self.host = value
1034
 
        # Emit D-Bus signal
1035
 
        self.PropertyChanged(dbus.String("Host"),
1036
 
                             dbus.String(value, variant_level=1))
1037
1119
    
1038
1120
    # Created - property
1039
1121
    @dbus_service_property(_interface, signature="s", access="read")
1040
1122
    def Created_dbus_property(self):
1041
 
        return dbus.String(self._datetime_to_dbus(self.created))
 
1123
        return dbus.String(datetime_to_dbus(self.created))
1042
1124
    
1043
1125
    # LastEnabled - property
1044
1126
    @dbus_service_property(_interface, signature="s", access="read")
1045
1127
    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))
 
1128
        return datetime_to_dbus(self.last_enabled)
1049
1129
    
1050
1130
    # Enabled - property
1051
1131
    @dbus_service_property(_interface, signature="b",
1065
1145
        if value is not None:
1066
1146
            self.checked_ok()
1067
1147
            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))
 
1148
        return datetime_to_dbus(self.last_checked_ok)
 
1149
    
 
1150
    # Expires - property
 
1151
    @dbus_service_property(_interface, signature="s", access="read")
 
1152
    def Expires_dbus_property(self):
 
1153
        return datetime_to_dbus(self.expires)
1072
1154
    
1073
1155
    # LastApprovalRequest - property
1074
1156
    @dbus_service_property(_interface, signature="s", access="read")
1075
1157
    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))
 
1158
        return datetime_to_dbus(self.last_approval_request)
1081
1159
    
1082
1160
    # Timeout - property
1083
1161
    @dbus_service_property(_interface, signature="t",
1086
1164
        if value is None:       # get
1087
1165
            return dbus.UInt64(self.timeout_milliseconds())
1088
1166
        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
1167
        if getattr(self, "disable_initiator_tag", None) is None:
1093
1168
            return
1094
1169
        # Reschedule timeout
1095
1170
        gobject.source_remove(self.disable_initiator_tag)
1096
1171
        self.disable_initiator_tag = None
 
1172
        self.expires = None
1097
1173
        time_to_die = (self.
1098
1174
                       _timedelta_to_milliseconds((self
1099
1175
                                                   .last_checked_ok
1104
1180
            # The timeout has passed
1105
1181
            self.disable()
1106
1182
        else:
 
1183
            self.expires = (datetime.datetime.utcnow()
 
1184
                            + datetime.timedelta(milliseconds = time_to_die))
1107
1185
            self.disable_initiator_tag = (gobject.timeout_add
1108
1186
                                          (time_to_die, self.disable))
1109
1187
    
 
1188
    # ExtendedTimeout - property
 
1189
    @dbus_service_property(_interface, signature="t",
 
1190
                           access="readwrite")
 
1191
    def ExtendedTimeout_dbus_property(self, value=None):
 
1192
        if value is None:       # get
 
1193
            return dbus.UInt64(self.extended_timeout_milliseconds())
 
1194
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
 
1195
    
1110
1196
    # Interval - property
1111
1197
    @dbus_service_property(_interface, signature="t",
1112
1198
                           access="readwrite")
1114
1200
        if value is None:       # get
1115
1201
            return dbus.UInt64(self.interval_milliseconds())
1116
1202
        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
1203
        if getattr(self, "checker_initiator_tag", None) is None:
1121
1204
            return
1122
1205
        # Reschedule checker run
1124
1207
        self.checker_initiator_tag = (gobject.timeout_add
1125
1208
                                      (value, self.start_checker))
1126
1209
        self.start_checker()    # Start one now, too
1127
 
 
 
1210
    
1128
1211
    # Checker - property
1129
1212
    @dbus_service_property(_interface, signature="s",
1130
1213
                           access="readwrite")
1132
1215
        if value is None:       # get
1133
1216
            return dbus.String(self.checker_command)
1134
1217
        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
1218
    
1140
1219
    # CheckerRunning - property
1141
1220
    @dbus_service_property(_interface, signature="b",
1168
1247
        self._pipe.send(('init', fpr, address))
1169
1248
        if not self._pipe.recv():
1170
1249
            raise KeyError()
1171
 
 
 
1250
    
1172
1251
    def __getattribute__(self, name):
1173
1252
        if(name == '_pipe'):
1174
1253
            return super(ProxyClient, self).__getattribute__(name)
1181
1260
                self._pipe.send(('funcall', name, args, kwargs))
1182
1261
                return self._pipe.recv()[1]
1183
1262
            return func
1184
 
 
 
1263
    
1185
1264
    def __setattr__(self, name, value):
1186
1265
        if(name == '_pipe'):
1187
1266
            return super(ProxyClient, self).__setattr__(name, value)
1188
1267
        self._pipe.send(('setattr', name, value))
1189
1268
 
 
1269
class ClientDBusTransitional(ClientDBus):
 
1270
    __metaclass__ = AlternateDBusNamesMetaclass
1190
1271
 
1191
1272
class ClientHandler(socketserver.BaseRequestHandler, object):
1192
1273
    """A class to handle client connections.
1200
1281
                        unicode(self.client_address))
1201
1282
            logger.debug("Pipe FD: %d",
1202
1283
                         self.server.child_pipe.fileno())
1203
 
 
 
1284
            
1204
1285
            session = (gnutls.connection
1205
1286
                       .ClientSession(self.request,
1206
1287
                                      gnutls.connection
1207
1288
                                      .X509Credentials()))
1208
 
 
 
1289
            
1209
1290
            # Note: gnutls.connection.X509Credentials is really a
1210
1291
            # generic GnuTLS certificate credentials object so long as
1211
1292
            # no X.509 keys are added to it.  Therefore, we can use it
1212
1293
            # here despite using OpenPGP certificates.
1213
 
 
 
1294
            
1214
1295
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1215
1296
            #                      "+AES-256-CBC", "+SHA1",
1216
1297
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1222
1303
            (gnutls.library.functions
1223
1304
             .gnutls_priority_set_direct(session._c_object,
1224
1305
                                         priority, None))
1225
 
 
 
1306
            
1226
1307
            # Start communication using the Mandos protocol
1227
1308
            # Get protocol number
1228
1309
            line = self.request.makefile().readline()
1233
1314
            except (ValueError, IndexError, RuntimeError) as error:
1234
1315
                logger.error("Unknown protocol version: %s", error)
1235
1316
                return
1236
 
 
 
1317
            
1237
1318
            # Start GnuTLS connection
1238
1319
            try:
1239
1320
                session.handshake()
1243
1324
                # established.  Just abandon the request.
1244
1325
                return
1245
1326
            logger.debug("Handshake succeeded")
1246
 
 
 
1327
            
1247
1328
            approval_required = False
1248
1329
            try:
1249
1330
                try:
1254
1335
                    logger.warning("Bad certificate: %s", error)
1255
1336
                    return
1256
1337
                logger.debug("Fingerprint: %s", fpr)
1257
 
 
 
1338
                
1258
1339
                try:
1259
1340
                    client = ProxyClient(child_pipe, fpr,
1260
1341
                                         self.client_address)
1326
1407
                                 sent, len(client.secret)
1327
1408
                                 - (sent_size + sent))
1328
1409
                    sent_size += sent
1329
 
 
 
1410
                
1330
1411
                logger.info("Sending secret to %s", client.name)
1331
1412
                # bump the timeout as if seen
1332
 
                client.checked_ok()
 
1413
                client.checked_ok(client.extended_timeout)
1333
1414
                if self.server.use_dbus:
1334
1415
                    # Emit D-Bus signal
1335
1416
                    client.GotSecret()
1420
1501
        multiprocessing.Process(target = self.sub_process_main,
1421
1502
                                args = (request, address)).start()
1422
1503
 
 
1504
 
1423
1505
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1424
1506
    """ adds a pipe to the MixIn """
1425
1507
    def process_request(self, request, client_address):
1428
1510
        This function creates a new pipe in self.pipe
1429
1511
        """
1430
1512
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1431
 
 
 
1513
        
1432
1514
        super(MultiprocessingMixInWithPipe,
1433
1515
              self).process_request(request, client_address)
1434
1516
        self.child_pipe.close()
1435
1517
        self.add_pipe(parent_pipe)
1436
 
 
 
1518
    
1437
1519
    def add_pipe(self, parent_pipe):
1438
1520
        """Dummy function; override as necessary"""
1439
1521
        raise NotImplementedError
1440
1522
 
 
1523
 
1441
1524
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1442
1525
                     socketserver.TCPServer, object):
1443
1526
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1591
1674
            kwargs = request[3]
1592
1675
            
1593
1676
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1594
 
 
 
1677
        
1595
1678
        if command == 'getattr':
1596
1679
            attrname = request[1]
1597
1680
            if callable(client_object.__getattribute__(attrname)):
1603
1686
            attrname = request[1]
1604
1687
            value = request[2]
1605
1688
            setattr(client_object, attrname, value)
1606
 
 
 
1689
        
1607
1690
        return True
1608
1691
 
1609
1692
 
1788
1871
    debuglevel = server_settings["debuglevel"]
1789
1872
    use_dbus = server_settings["use_dbus"]
1790
1873
    use_ipv6 = server_settings["use_ipv6"]
1791
 
 
 
1874
    
1792
1875
    if server_settings["servicename"] != "Mandos":
1793
1876
        syslogger.setFormatter(logging.Formatter
1794
1877
                               ('Mandos (%s) [%%(process)d]:'
1796
1879
                                % server_settings["servicename"]))
1797
1880
    
1798
1881
    # Parse config file with clients
1799
 
    client_defaults = { "timeout": "1h",
1800
 
                        "interval": "5m",
 
1882
    client_defaults = { "timeout": "5m",
 
1883
                        "extended_timeout": "15m",
 
1884
                        "interval": "2m",
1801
1885
                        "checker": "fping -q -- %%(host)s",
1802
1886
                        "host": "",
1803
1887
                        "approval_delay": "0s",
1854
1938
        level = getattr(logging, debuglevel.upper())
1855
1939
        syslogger.setLevel(level)
1856
1940
        console.setLevel(level)
1857
 
 
 
1941
    
1858
1942
    if debug:
1859
1943
        # Enable all possible GnuTLS debugging
1860
1944
        
1891
1975
    # End of Avahi example code
1892
1976
    if use_dbus:
1893
1977
        try:
1894
 
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
 
1978
            bus_name = dbus.service.BusName("se.recompile.Mandos",
1895
1979
                                            bus, do_not_queue=True)
 
1980
            old_bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
 
1981
                                                bus, do_not_queue=True)
1896
1982
        except dbus.exceptions.NameExistsException as e:
1897
1983
            logger.error(unicode(e) + ", disabling D-Bus")
1898
1984
            use_dbus = False
1911
1997
    
1912
1998
    client_class = Client
1913
1999
    if use_dbus:
1914
 
        client_class = functools.partial(ClientDBus, bus = bus)
 
2000
        client_class = functools.partial(ClientDBusTransitional, bus = bus)        
1915
2001
    def client_config_items(config, section):
1916
2002
        special_settings = {
1917
2003
            "approved_by_default":
1947
2033
        del pidfilename
1948
2034
        
1949
2035
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1950
 
 
 
2036
    
1951
2037
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1952
2038
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1953
2039
    
1956
2042
            """A D-Bus proxy object"""
1957
2043
            def __init__(self):
1958
2044
                dbus.service.Object.__init__(self, bus, "/")
1959
 
            _interface = "se.bsnet.fukt.Mandos"
 
2045
            _interface = "se.recompile.Mandos"
1960
2046
            
1961
2047
            @dbus.service.signal(_interface, signature="o")
1962
2048
            def ClientAdded(self, objpath):
2004
2090
            
2005
2091
            del _interface
2006
2092
        
2007
 
        mandos_dbus_service = MandosDBusService()
 
2093
        class MandosDBusServiceTransitional(MandosDBusService):
 
2094
            __metaclass__ = AlternateDBusNamesMetaclass
 
2095
        mandos_dbus_service = MandosDBusServiceTransitional()
2008
2096
    
2009
2097
    def cleanup():
2010
2098
        "Cleanup function; run on exit"
2074
2162
    # Must run before the D-Bus bus name gets deregistered
2075
2163
    cleanup()
2076
2164
 
 
2165
 
2077
2166
if __name__ == '__main__':
2078
2167
    main()