/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

* mandos-ctl: Made work again after D-Bus API changes.
  (datetime_to_milliseconds): Renamed to "timedelta_to_milliseconds".
                              All callers changed.
  (milliseconds_to_string): Use clearer mapping string format.
  (string_to_delta): Add some comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
67
67
from dbus.mainloop.glib import DBusGMainLoop
68
68
import ctypes
69
69
import ctypes.util
 
70
import xml.dom.minidom
 
71
import inspect
70
72
 
71
73
try:
72
74
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
77
79
        SO_BINDTODEVICE = None
78
80
 
79
81
 
80
 
version = "1.0.11"
 
82
version = "1.0.12"
81
83
 
82
84
logger = logging.Logger(u'mandos')
83
85
syslogger = (logging.handlers.SysLogHandler
174
176
                                    self.server.EntryGroupNew()),
175
177
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
178
            self.group.connect_to_signal('StateChanged',
177
 
                                         self.entry_group_state_changed)
 
179
                                         self
 
180
                                         .entry_group_state_changed)
178
181
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
179
182
                     self.name, self.type)
180
183
        self.group.AddService(
246
249
                                    to see if the client lives.
247
250
                                    'None' if no process is running.
248
251
    checker_initiator_tag: a gobject event source tag, or None
249
 
    disable_initiator_tag:    - '' -
 
252
    disable_initiator_tag: - '' -
250
253
    checker_callback_tag:  - '' -
251
254
    checker_command: string; External command which is run to check if
252
255
                     client lives.  %() expansions are done at
256
259
    """
257
260
    
258
261
    @staticmethod
259
 
    def _datetime_to_milliseconds(dt):
260
 
        "Convert a datetime.datetime() to milliseconds"
261
 
        return ((dt.days * 24 * 60 * 60 * 1000)
262
 
                + (dt.seconds * 1000)
263
 
                + (dt.microseconds // 1000))
 
262
    def _timedelta_to_milliseconds(td):
 
263
        "Convert a datetime.timedelta() to milliseconds"
 
264
        return ((td.days * 24 * 60 * 60 * 1000)
 
265
                + (td.seconds * 1000)
 
266
                + (td.microseconds // 1000))
264
267
    
265
268
    def timeout_milliseconds(self):
266
269
        "Return the 'timeout' attribute in milliseconds"
267
 
        return self._datetime_to_milliseconds(self.timeout)
 
270
        return self._timedelta_to_milliseconds(self.timeout)
268
271
    
269
272
    def interval_milliseconds(self):
270
273
        "Return the 'interval' attribute in milliseconds"
271
 
        return self._datetime_to_milliseconds(self.interval)
 
274
        return self._timedelta_to_milliseconds(self.interval)
272
275
    
273
276
    def __init__(self, name = None, disable_hook=None, config=None):
274
277
        """Note: the 'checker' key in 'config' sets the
477
480
            return now < (self.last_checked_ok + self.timeout)
478
481
 
479
482
 
480
 
class ClientDBus(Client, dbus.service.Object):
 
483
def dbus_service_property(dbus_interface, signature=u"v",
 
484
                          access=u"readwrite", byte_arrays=False):
 
485
    """Decorators for marking methods of a DBusObjectWithProperties to
 
486
    become properties on the D-Bus.
 
487
    
 
488
    The decorated method will be called with no arguments by "Get"
 
489
    and with one argument by "Set".
 
490
    
 
491
    The parameters, where they are supported, are the same as
 
492
    dbus.service.method, except there is only "signature", since the
 
493
    type from Get() and the type sent to Set() is the same.
 
494
    """
 
495
    def decorator(func):
 
496
        func._dbus_is_property = True
 
497
        func._dbus_interface = dbus_interface
 
498
        func._dbus_signature = signature
 
499
        func._dbus_access = access
 
500
        func._dbus_name = func.__name__
 
501
        if func._dbus_name.endswith(u"_dbus_property"):
 
502
            func._dbus_name = func._dbus_name[:-14]
 
503
        func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
 
504
        return func
 
505
    return decorator
 
506
 
 
507
 
 
508
class DBusPropertyException(dbus.exceptions.DBusException):
 
509
    """A base class for D-Bus property-related exceptions
 
510
    """
 
511
    def __unicode__(self):
 
512
        return unicode(str(self))
 
513
 
 
514
 
 
515
class DBusPropertyAccessException(DBusPropertyException):
 
516
    """A property's access permissions disallows an operation.
 
517
    """
 
518
    pass
 
519
 
 
520
 
 
521
class DBusPropertyNotFound(DBusPropertyException):
 
522
    """An attempt was made to access a non-existing property.
 
523
    """
 
524
    pass
 
525
 
 
526
 
 
527
class DBusObjectWithProperties(dbus.service.Object):
 
528
    """A D-Bus object with properties.
 
529
 
 
530
    Classes inheriting from this can use the dbus_service_property
 
531
    decorator to expose methods as D-Bus properties.  It exposes the
 
532
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
533
    """
 
534
    
 
535
    @staticmethod
 
536
    def _is_dbus_property(obj):
 
537
        return getattr(obj, u"_dbus_is_property", False)
 
538
    
 
539
    def _get_all_dbus_properties(self):
 
540
        """Returns a generator of (name, attribute) pairs
 
541
        """
 
542
        return ((prop._dbus_name, prop)
 
543
                for name, prop in
 
544
                inspect.getmembers(self, self._is_dbus_property))
 
545
    
 
546
    def _get_dbus_property(self, interface_name, property_name):
 
547
        """Returns a bound method if one exists which is a D-Bus
 
548
        property with the specified name and interface.
 
549
        """
 
550
        for name in (property_name,
 
551
                     property_name + u"_dbus_property"):
 
552
            prop = getattr(self, name, None)
 
553
            if (prop is None
 
554
                or not self._is_dbus_property(prop)
 
555
                or prop._dbus_name != property_name
 
556
                or (interface_name and prop._dbus_interface
 
557
                    and interface_name != prop._dbus_interface)):
 
558
                continue
 
559
            return prop
 
560
        # No such property
 
561
        raise DBusPropertyNotFound(self.dbus_object_path + u":"
 
562
                                   + interface_name + u"."
 
563
                                   + property_name)
 
564
    
 
565
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
 
566
                         out_signature=u"v")
 
567
    def Get(self, interface_name, property_name):
 
568
        """Standard D-Bus property Get() method, see D-Bus standard.
 
569
        """
 
570
        prop = self._get_dbus_property(interface_name, property_name)
 
571
        if prop._dbus_access == u"write":
 
572
            raise DBusPropertyAccessException(property_name)
 
573
        value = prop()
 
574
        if not hasattr(value, u"variant_level"):
 
575
            return value
 
576
        return type(value)(value, variant_level=value.variant_level+1)
 
577
    
 
578
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
 
579
    def Set(self, interface_name, property_name, value):
 
580
        """Standard D-Bus property Set() method, see D-Bus standard.
 
581
        """
 
582
        prop = self._get_dbus_property(interface_name, property_name)
 
583
        if prop._dbus_access == u"read":
 
584
            raise DBusPropertyAccessException(property_name)
 
585
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
586
            value = dbus.ByteArray(''.join(unichr(byte)
 
587
                                           for byte in value))
 
588
        prop(value)
 
589
    
 
590
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
 
591
                         out_signature=u"a{sv}")
 
592
    def GetAll(self, interface_name):
 
593
        """Standard D-Bus property GetAll() method, see D-Bus
 
594
        standard.
 
595
 
 
596
        Note: Will not include properties with access="write".
 
597
        """
 
598
        all = {}
 
599
        for name, prop in self._get_all_dbus_properties():
 
600
            if (interface_name
 
601
                and interface_name != prop._dbus_interface):
 
602
                # Interface non-empty but did not match
 
603
                continue
 
604
            # Ignore write-only properties
 
605
            if prop._dbus_access == u"write":
 
606
                continue
 
607
            value = prop()
 
608
            if not hasattr(value, u"variant_level"):
 
609
                all[name] = value
 
610
                continue
 
611
            all[name] = type(value)(value, variant_level=
 
612
                                    value.variant_level+1)
 
613
        return dbus.Dictionary(all, signature=u"sv")
 
614
    
 
615
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
616
                         out_signature=u"s",
 
617
                         path_keyword='object_path',
 
618
                         connection_keyword='connection')
 
619
    def Introspect(self, object_path, connection):
 
620
        """Standard D-Bus method, overloaded to insert property tags.
 
621
        """
 
622
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
623
                                           connection)
 
624
        document = xml.dom.minidom.parseString(xmlstring)
 
625
        del xmlstring
 
626
        def make_tag(document, name, prop):
 
627
            e = document.createElement(u"property")
 
628
            e.setAttribute(u"name", name)
 
629
            e.setAttribute(u"type", prop._dbus_signature)
 
630
            e.setAttribute(u"access", prop._dbus_access)
 
631
            return e
 
632
        for if_tag in document.getElementsByTagName(u"interface"):
 
633
            for tag in (make_tag(document, name, prop)
 
634
                        for name, prop
 
635
                        in self._get_all_dbus_properties()
 
636
                        if prop._dbus_interface
 
637
                        == if_tag.getAttribute(u"name")):
 
638
                if_tag.appendChild(tag)
 
639
        xmlstring = document.toxml(u"utf-8")
 
640
        document.unlink()
 
641
        return xmlstring
 
642
 
 
643
 
 
644
class ClientDBus(Client, DBusObjectWithProperties):
481
645
    """A Client class using D-Bus
482
646
    
483
647
    Attributes:
494
658
        self.dbus_object_path = (dbus.ObjectPath
495
659
                                 (u"/clients/"
496
660
                                  + self.name.replace(u".", u"_")))
497
 
        dbus.service.Object.__init__(self, self.bus,
498
 
                                     self.dbus_object_path)
 
661
        DBusObjectWithProperties.__init__(self, self.bus,
 
662
                                          self.dbus_object_path)
499
663
    
500
664
    @staticmethod
501
665
    def _datetime_to_dbus(dt, variant_level=0):
530
694
            self.remove_from_connection()
531
695
        except LookupError:
532
696
            pass
533
 
        if hasattr(dbus.service.Object, u"__del__"):
534
 
            dbus.service.Object.__del__(self, *args, **kwargs)
 
697
        if hasattr(DBusObjectWithProperties, u"__del__"):
 
698
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
535
699
        Client.__del__(self, *args, **kwargs)
536
700
    
537
701
    def checker_callback(self, pid, condition, command,
611
775
        "D-Bus signal"
612
776
        pass
613
777
    
614
 
    # GetAllProperties - method
615
 
    @dbus.service.method(_interface, out_signature=u"a{sv}")
616
 
    def GetAllProperties(self):
617
 
        "D-Bus method"
618
 
        return dbus.Dictionary({
619
 
                dbus.String(u"name"):
620
 
                    dbus.String(self.name, variant_level=1),
621
 
                dbus.String(u"fingerprint"):
622
 
                    dbus.String(self.fingerprint, variant_level=1),
623
 
                dbus.String(u"host"):
624
 
                    dbus.String(self.host, variant_level=1),
625
 
                dbus.String(u"created"):
626
 
                    self._datetime_to_dbus(self.created,
627
 
                                           variant_level=1),
628
 
                dbus.String(u"last_enabled"):
629
 
                    (self._datetime_to_dbus(self.last_enabled,
630
 
                                            variant_level=1)
631
 
                     if self.last_enabled is not None
632
 
                     else dbus.Boolean(False, variant_level=1)),
633
 
                dbus.String(u"enabled"):
634
 
                    dbus.Boolean(self.enabled, variant_level=1),
635
 
                dbus.String(u"last_checked_ok"):
636
 
                    (self._datetime_to_dbus(self.last_checked_ok,
637
 
                                            variant_level=1)
638
 
                     if self.last_checked_ok is not None
639
 
                     else dbus.Boolean (False, variant_level=1)),
640
 
                dbus.String(u"timeout"):
641
 
                    dbus.UInt64(self.timeout_milliseconds(),
642
 
                                variant_level=1),
643
 
                dbus.String(u"interval"):
644
 
                    dbus.UInt64(self.interval_milliseconds(),
645
 
                                variant_level=1),
646
 
                dbus.String(u"checker"):
647
 
                    dbus.String(self.checker_command,
648
 
                                variant_level=1),
649
 
                dbus.String(u"checker_running"):
650
 
                    dbus.Boolean(self.checker is not None,
651
 
                                 variant_level=1),
652
 
                dbus.String(u"object_path"):
653
 
                    dbus.ObjectPath(self.dbus_object_path,
654
 
                                    variant_level=1)
655
 
                }, signature=u"sv")
656
 
    
657
 
    # IsStillValid - method
658
 
    @dbus.service.method(_interface, out_signature=u"b")
659
 
    def IsStillValid(self):
660
 
        return self.still_valid()
661
 
    
662
778
    # PropertyChanged - signal
663
779
    @dbus.service.signal(_interface, signature=u"sv")
664
780
    def PropertyChanged(self, property, value):
677
793
        "D-Bus signal"
678
794
        pass
679
795
    
680
 
    # SetChecker - method
681
 
    @dbus.service.method(_interface, in_signature=u"s")
682
 
    def SetChecker(self, checker):
683
 
        "D-Bus setter method"
684
 
        self.checker_command = checker
685
 
        # Emit D-Bus signal
686
 
        self.PropertyChanged(dbus.String(u"checker"),
687
 
                             dbus.String(self.checker_command,
688
 
                                         variant_level=1))
689
 
    
690
 
    # SetHost - method
691
 
    @dbus.service.method(_interface, in_signature=u"s")
692
 
    def SetHost(self, host):
693
 
        "D-Bus setter method"
694
 
        self.host = host
695
 
        # Emit D-Bus signal
696
 
        self.PropertyChanged(dbus.String(u"host"),
697
 
                             dbus.String(self.host, variant_level=1))
698
 
    
699
 
    # SetInterval - method
700
 
    @dbus.service.method(_interface, in_signature=u"t")
701
 
    def SetInterval(self, milliseconds):
702
 
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
703
 
        # Emit D-Bus signal
704
 
        self.PropertyChanged(dbus.String(u"interval"),
705
 
                             (dbus.UInt64(self.interval_milliseconds(),
706
 
                                          variant_level=1)))
707
 
    
708
 
    # SetSecret - method
709
 
    @dbus.service.method(_interface, in_signature=u"ay",
710
 
                         byte_arrays=True)
711
 
    def SetSecret(self, secret):
712
 
        "D-Bus setter method"
713
 
        self.secret = str(secret)
714
 
    
715
 
    # SetTimeout - method
716
 
    @dbus.service.method(_interface, in_signature=u"t")
717
 
    def SetTimeout(self, milliseconds):
718
 
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
719
 
        # Emit D-Bus signal
720
 
        self.PropertyChanged(dbus.String(u"timeout"),
721
 
                             (dbus.UInt64(self.timeout_milliseconds(),
722
 
                                          variant_level=1)))
723
 
    
724
796
    # Enable - method
725
797
    @dbus.service.method(_interface)
726
798
    def Enable(self):
744
816
    def StopChecker(self):
745
817
        self.stop_checker()
746
818
    
 
819
    # name - property
 
820
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
821
    def name_dbus_property(self):
 
822
        return dbus.String(self.name)
 
823
    
 
824
    # fingerprint - property
 
825
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
826
    def fingerprint_dbus_property(self):
 
827
        return dbus.String(self.fingerprint)
 
828
    
 
829
    # host - property
 
830
    @dbus_service_property(_interface, signature=u"s",
 
831
                           access=u"readwrite")
 
832
    def host_dbus_property(self, value=None):
 
833
        if value is None:       # get
 
834
            return dbus.String(self.host)
 
835
        self.host = value
 
836
        # Emit D-Bus signal
 
837
        self.PropertyChanged(dbus.String(u"host"),
 
838
                             dbus.String(value, variant_level=1))
 
839
    
 
840
    # created - property
 
841
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
842
    def created_dbus_property(self):
 
843
        return dbus.String(self._datetime_to_dbus(self.created))
 
844
    
 
845
    # last_enabled - property
 
846
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
847
    def last_enabled_dbus_property(self):
 
848
        if self.last_enabled is None:
 
849
            return dbus.String(u"")
 
850
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
851
    
 
852
    # enabled - property
 
853
    @dbus_service_property(_interface, signature=u"b",
 
854
                           access=u"readwrite")
 
855
    def enabled_dbus_property(self, value=None):
 
856
        if value is None:       # get
 
857
            return dbus.Boolean(self.enabled)
 
858
        if value:
 
859
            self.enable()
 
860
        else:
 
861
            self.disable()
 
862
    
 
863
    # last_checked_ok - property
 
864
    @dbus_service_property(_interface, signature=u"s",
 
865
                           access=u"readwrite")
 
866
    def last_checked_ok_dbus_property(self, value=None):
 
867
        if value is not None:
 
868
            self.checked_ok()
 
869
            return
 
870
        if self.last_checked_ok is None:
 
871
            return dbus.String(u"")
 
872
        return dbus.String(self._datetime_to_dbus(self
 
873
                                                  .last_checked_ok))
 
874
    
 
875
    # timeout - property
 
876
    @dbus_service_property(_interface, signature=u"t",
 
877
                           access=u"readwrite")
 
878
    def timeout_dbus_property(self, value=None):
 
879
        if value is None:       # get
 
880
            return dbus.UInt64(self.timeout_milliseconds())
 
881
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
882
        # Emit D-Bus signal
 
883
        self.PropertyChanged(dbus.String(u"timeout"),
 
884
                             dbus.UInt64(value, variant_level=1))
 
885
        if getattr(self, u"disable_initiator_tag", None) is None:
 
886
            return
 
887
        # Reschedule timeout
 
888
        gobject.source_remove(self.disable_initiator_tag)
 
889
        self.disable_initiator_tag = None
 
890
        time_to_die = (self.
 
891
                       _timedelta_to_milliseconds((self
 
892
                                                   .last_checked_ok
 
893
                                                   + self.timeout)
 
894
                                                  - datetime.datetime
 
895
                                                  .utcnow()))
 
896
        if time_to_die <= 0:
 
897
            # The timeout has passed
 
898
            self.disable()
 
899
        else:
 
900
            self.disable_initiator_tag = (gobject.timeout_add
 
901
                                          (time_to_die, self.disable))
 
902
    
 
903
    # interval - property
 
904
    @dbus_service_property(_interface, signature=u"t",
 
905
                           access=u"readwrite")
 
906
    def interval_dbus_property(self, value=None):
 
907
        if value is None:       # get
 
908
            return dbus.UInt64(self.interval_milliseconds())
 
909
        self.interval = datetime.timedelta(0, 0, 0, value)
 
910
        # Emit D-Bus signal
 
911
        self.PropertyChanged(dbus.String(u"interval"),
 
912
                             dbus.UInt64(value, variant_level=1))
 
913
        if getattr(self, u"checker_initiator_tag", None) is None:
 
914
            return
 
915
        # Reschedule checker run
 
916
        gobject.source_remove(self.checker_initiator_tag)
 
917
        self.checker_initiator_tag = (gobject.timeout_add
 
918
                                      (value, self.start_checker))
 
919
        self.start_checker()    # Start one now, too
 
920
 
 
921
    # checker - property
 
922
    @dbus_service_property(_interface, signature=u"s",
 
923
                           access=u"readwrite")
 
924
    def checker_dbus_property(self, value=None):
 
925
        if value is None:       # get
 
926
            return dbus.String(self.checker_command)
 
927
        self.checker_command = value
 
928
        # Emit D-Bus signal
 
929
        self.PropertyChanged(dbus.String(u"checker"),
 
930
                             dbus.String(self.checker_command,
 
931
                                         variant_level=1))
 
932
    
 
933
    # checker_running - property
 
934
    @dbus_service_property(_interface, signature=u"b",
 
935
                           access=u"readwrite")
 
936
    def checker_running_dbus_property(self, value=None):
 
937
        if value is None:       # get
 
938
            return dbus.Boolean(self.checker is not None)
 
939
        if value:
 
940
            self.start_checker()
 
941
        else:
 
942
            self.stop_checker()
 
943
    
 
944
    # object_path - property
 
945
    @dbus_service_property(_interface, signature=u"o", access=u"read")
 
946
    def object_path_dbus_property(self):
 
947
        return self.dbus_object_path # is already a dbus.ObjectPath
 
948
    
 
949
    # secret = property
 
950
    @dbus_service_property(_interface, signature=u"ay",
 
951
                           access=u"write", byte_arrays=True)
 
952
    def secret_dbus_property(self, value):
 
953
        self.secret = str(value)
 
954
    
747
955
    del _interface
748
956
 
749
957
 
901
1109
    def process_request(self, request, client_address):
902
1110
        """Overrides and wraps the original process_request().
903
1111
        
904
 
        This function creates a new pipe in self.pipe 
 
1112
        This function creates a new pipe in self.pipe
905
1113
        """
906
1114
        self.pipe = os.pipe()
907
1115
        super(ForkingMixInWithPipe,
983
1191
        clients:        set of Client objects
984
1192
        gnutls_priority GnuTLS priority string
985
1193
        use_dbus:       Boolean; to emit D-Bus signals or not
986
 
        clients:        set of Client objects
987
 
        gnutls_priority GnuTLS priority string
988
 
        use_dbus:       Boolean; to emit D-Bus signals or not
989
1194
    
990
1195
    Assumes a gobject.MainLoop event loop.
991
1196
    """
1172
1377
 
1173
1378
def main():
1174
1379
    
1175
 
    ######################################################################
 
1380
    ##################################################################
1176
1381
    # Parsing of options, both command line and config file
1177
1382
    
1178
1383
    parser = optparse.OptionParser(version = "%%prog %s" % version)
1428
1633
            def GetAllClientsWithProperties(self):
1429
1634
                "D-Bus method"
1430
1635
                return dbus.Dictionary(
1431
 
                    ((c.dbus_object_path, c.GetAllProperties())
 
1636
                    ((c.dbus_object_path, c.GetAll(u""))
1432
1637
                     for c in tcp_server.clients),
1433
1638
                    signature=u"oa{sv}")
1434
1639
            
1454
1659
        if use_dbus:
1455
1660
            # Emit D-Bus signal
1456
1661
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1457
 
                                            client.GetAllProperties())
 
1662
                                            client.GetAll(u""))
1458
1663
        client.enable()
1459
1664
    
1460
1665
    tcp_server.enable()