/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

* initramfs-tools-hook: Set DEVICE for network hooks.

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