/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-ctl

  • Committer: Teddy Hogeborn
  • Date: 2024-09-09 01:36:41 UTC
  • Revision ID: teddy@recompile.se-20240909013641-6zu6kx2f7meu134k
Make all required directories when installing

When installing into a normal system, one can assume that target
directories, such as /usr/bin, already exists.  But when installing
into a subdirectory for the purpose of creating a package, one cannot
assume that all directories already exist.  Therefore, when
installing, we must not check if any directories exist, and must
instead always create any directories we want to install into.

* Makefile (confdir/mandos.conf, confdir/clients.conf, install-html):
  Use the "-D" option to "install" instead of creating the directory
  separately.
  (install-server): Move creation of $(CONFDIR) down to before it is
  needed.  Don't check if the $(TMPFILES) or $(SYSUSERS) directories
  exist; instead create them by using the "-D" option to "install".
  Create the $(PREFIX)/sbin directory.  Always use
  "--target-directory" if possible; i.e. if the file name is the same.
  Create the $(DBUSPOLICYDIR) and $(DESTDIR)/etc/init.d directories by
  using the "-D" option to "install".  Don't check if the $(SYSTEMD)
  directory exists; instead create it by using the "-D" option to
  "install".  Create the $(DESTDIR)/etc/default and $(MANDIR)/man8
  directories by using the "-D" option to "install".  Create the
  $(MANDIR)/man5 directories explicitly.
  (install-client-nokey): Remove unnecessary creation of the
  $(CONFDIR) directory.  Don't check if the $(SYSUSERS) directory
  exists; instead create it by using the "-D" option to "install".
  Move the "--directory" argument to be the first argument, for
  clarity.  Create the $(PREFIX)/sbin directory.  Use the "-D"
  argument to "install" when installing
  $(INITRAMFSTOOLS)/hooks/mandos,
  $(INITRAMFSTOOLS)/conf.d/mandos-conf,
  $(INITRAMFSTOOLS)/conf-hooks.d/zz-mandos,
  $(INITRAMFSTOOLS)/scripts/init-premount/mandos,
  $(INITRAMFSTOOLS)/scripts/local-premount/mandos,
  $(DRACUTMODULE)/ask-password-mandos.path, and
  $(DRACUTMODULE)/dracut-module/ask-password-mandos.service.  Create
  the $(MANDIR)/man8 directory.

Reported-By: Erich Eckner <erich@eckner.net>
Thanks: Erich Eckner <erich@eckner.net> for analysis

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
    str = unicode
51
51
    input = raw_input
52
52
 
 
53
 
53
54
class gi:
54
55
    """Dummy gi module, for the tests"""
55
56
    class repository:
56
57
        class GLib:
57
58
            class Error(Exception):
58
59
                pass
 
60
 
 
61
 
59
62
dbussy = None
60
63
ravel = None
61
64
dbus_python = None
78
81
    warnings.simplefilter("default")
79
82
 
80
83
log = logging.getLogger(os.path.basename(sys.argv[0]))
81
 
logging.basicConfig(level="INFO", # Show info level messages
 
84
logging.basicConfig(level="INFO",         # Show info level messages
82
85
                    format="%(message)s") # Show basic log messages
83
86
 
84
87
logging.captureWarnings(True)   # Show warnings via the logging system
89
92
 
90
93
locale.setlocale(locale.LC_ALL, "")
91
94
 
92
 
version = "1.8.15"
 
95
version = "1.8.16"
93
96
 
94
97
 
95
98
def main():
392
395
 
393
396
 
394
397
def parse_pre_1_6_1_interval(interval):
395
 
    """Parse an interval string as documented by Mandos before 1.6.1,
 
398
    r"""Parse an interval string as documented by Mandos before 1.6.1,
396
399
    and return a datetime.timedelta
397
400
 
398
401
    >>> parse_pre_1_6_1_interval("7d") == datetime.timedelta(days=7)
410
413
    >>> parse_pre_1_6_1_interval("") == datetime.timedelta(0)
411
414
    True
412
415
    >>> # Ignore unknown characters, allow any order and repetitions
413
 
    >>> parse_pre_1_6_1_interval("2dxy7zz11y3m5m") == datetime.timedelta(2, 480, 18000)
 
416
    >>> parse_pre_1_6_1_interval("2dxy7zz11y3m5m") \
 
417
    ... == datetime.timedelta(2, 480, 18000)
414
418
    True
415
419
 
416
420
    """
485
489
    class SystemBus:
486
490
 
487
491
        object_manager_iface = "org.freedesktop.DBus.ObjectManager"
 
492
 
488
493
        def get_managed_objects(self, busname, objectpath):
489
494
            return self.call_method("GetManagedObjects", busname,
490
495
                                    objectpath,
491
496
                                    self.object_manager_iface)
492
497
 
493
498
        properties_iface = "org.freedesktop.DBus.Properties"
 
499
 
494
500
        def set_property(self, busname, objectpath, interface, key,
495
501
                         value):
496
502
            self.call_method("Set", busname, objectpath,
501
507
                        interface, *args):
502
508
            raise NotImplementedError()
503
509
 
504
 
 
505
510
    class MandosBus(SystemBus):
506
511
        busname_domain = "se.recompile"
507
512
        busname = busname_domain + ".Mandos"
600
605
 
601
606
    class SilenceLogger:
602
607
        "Simple context manager to silence a particular logger"
 
608
 
603
609
        def __init__(self, loggername):
604
610
            self.logger = logging.getLogger(loggername)
605
611
 
615
621
        def __exit__(self, exc_type, exc_val, exc_tb):
616
622
            self.logger.removeFilter(self.nullfilter)
617
623
 
618
 
 
619
624
    class CachingBus(SystemBus):
620
625
        """A caching layer for dbus_python_adapter.SystemBus"""
 
626
 
621
627
        def __init__(self, *args, **kwargs):
622
628
            self.object_cache = {}
623
629
            super(dbus_python_adapter.CachingBus,
624
630
                  self).__init__(*args, **kwargs)
 
631
 
625
632
        def get_object(self, busname, objectpath):
626
633
            try:
627
634
                return self.object_cache[(busname, objectpath)]
629
636
                new_object = super(
630
637
                    dbus_python_adapter.CachingBus,
631
638
                    self).get_object(busname, objectpath)
632
 
                self.object_cache[(busname, objectpath)]  = new_object
 
639
                self.object_cache[(busname, objectpath)] = new_object
633
640
                return new_object
634
641
 
635
642
 
682
689
 
683
690
    class CachingBus(SystemBus):
684
691
        """A caching layer for pydbus_adapter.SystemBus"""
 
692
 
685
693
        def __init__(self, *args, **kwargs):
686
694
            self.object_cache = {}
687
695
            super(pydbus_adapter.CachingBus,
688
696
                  self).__init__(*args, **kwargs)
 
697
 
689
698
        def get(self, busname, objectpath):
690
699
            try:
691
700
                return self.object_cache[(busname, objectpath)]
692
701
            except KeyError:
693
702
                new_object = (super(pydbus_adapter.CachingBus, self)
694
703
                              .get(busname, objectpath))
695
 
                self.object_cache[(busname, objectpath)]  = new_object
 
704
                self.object_cache[(busname, objectpath)] = new_object
696
705
                return new_object
697
706
 
698
707
 
724
733
            iface = proxy_object.get_interface(interface)
725
734
            method = getattr(iface, methodname)
726
735
            with self.convert_exception(dbus.Error):
727
 
                value =  method(*args)
 
736
                value = method(*args)
728
737
            # DBussy returns values either as an empty list or as a
729
738
            # list of one element with the return value
730
739
            if value:
771
780
 
772
781
    class CachingBus(MandosBus):
773
782
        """A caching layer for dbussy_adapter.MandosBus"""
 
783
 
774
784
        def __init__(self, *args, **kwargs):
775
785
            self.object_cache = {}
776
786
            super(dbussy_adapter.CachingBus, self).__init__(*args,
777
787
                                                            **kwargs)
 
788
 
778
789
        def get_object(self, busname, objectpath):
779
790
            try:
780
791
                return self.object_cache[(busname, objectpath)]
782
793
                new_object = super(
783
794
                    dbussy_adapter.CachingBus,
784
795
                    self).get_object(busname, objectpath)
785
 
                self.object_cache[(busname, objectpath)]  = new_object
 
796
                self.object_cache[(busname, objectpath)] = new_object
786
797
                return new_object
787
798
 
788
799
 
824
835
 
825
836
    class Base:
826
837
        """Abstract base class for commands"""
 
838
 
827
839
        def run(self, clients, bus=None):
828
840
            """Normal commands should implement run_on_one_client(),
829
841
but commands which want to operate on all clients at the same time can
833
845
            for client, properties in clients.items():
834
846
                self.run_on_one_client(client, properties)
835
847
 
836
 
 
837
848
    class IsEnabled(Base):
838
849
        def run(self, clients, bus=None):
839
850
            properties = next(iter(clients.values()))
841
852
                sys.exit(0)
842
853
            sys.exit(1)
843
854
 
844
 
 
845
855
    class Approve(Base):
846
856
        def run_on_one_client(self, client, properties):
847
857
            self.bus.call_client_method(client, "Approve", True)
848
858
 
849
 
 
850
859
    class Deny(Base):
851
860
        def run_on_one_client(self, client, properties):
852
861
            self.bus.call_client_method(client, "Approve", False)
853
862
 
854
 
 
855
863
    class Remove(Base):
856
864
        def run(self, clients, bus):
857
865
            for clientpath in frozenset(clients.keys()):
858
866
                bus.call_server_method("RemoveClient", clientpath)
859
867
 
860
 
 
861
868
    class Output(Base):
862
869
        """Abstract class for commands outputting client details"""
863
870
        all_keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
869
876
                        "Checker", "ExtendedTimeout", "Expires",
870
877
                        "LastCheckerStatus")
871
878
 
872
 
 
873
879
    class DumpJSON(Output):
874
880
        def run(self, clients, bus=None):
875
881
            data = {properties["Name"]:
878
884
                    for properties in clients.values()}
879
885
            print(json.dumps(data, indent=4, separators=(",", ": ")))
880
886
 
881
 
 
882
887
    class PrintTable(Output):
883
888
        def __init__(self, verbose=False):
884
889
            self.verbose = verbose
924
929
 
925
930
            if sys.version_info.major == 2:
926
931
                __unicode__ = __str__
 
932
 
927
933
                def __str__(self):
928
934
                    return str(self).encode(
929
935
                        locale.getpreferredencoding())
975
981
                                minutes=(td.seconds % 3600) // 60,
976
982
                                seconds=td.seconds % 60))
977
983
 
978
 
 
979
984
    class PropertySetter(Base):
980
985
        "Abstract class for Actions for setting one client property"
981
986
 
988
993
        def propname(self):
989
994
            raise NotImplementedError()
990
995
 
991
 
 
992
996
    class Enable(PropertySetter):
993
997
        propname = "Enabled"
994
998
        value_to_set = True
995
999
 
996
 
 
997
1000
    class Disable(PropertySetter):
998
1001
        propname = "Enabled"
999
1002
        value_to_set = False
1000
1003
 
1001
 
 
1002
1004
    class BumpTimeout(PropertySetter):
1003
1005
        propname = "LastCheckedOK"
1004
1006
        value_to_set = ""
1005
1007
 
1006
 
 
1007
1008
    class StartChecker(PropertySetter):
1008
1009
        propname = "CheckerRunning"
1009
1010
        value_to_set = True
1010
1011
 
1011
 
 
1012
1012
    class StopChecker(PropertySetter):
1013
1013
        propname = "CheckerRunning"
1014
1014
        value_to_set = False
1015
1015
 
1016
 
 
1017
1016
    class ApproveByDefault(PropertySetter):
1018
1017
        propname = "ApprovedByDefault"
1019
1018
        value_to_set = True
1020
1019
 
1021
 
 
1022
1020
    class DenyByDefault(PropertySetter):
1023
1021
        propname = "ApprovedByDefault"
1024
1022
        value_to_set = False
1025
1023
 
1026
 
 
1027
1024
    class PropertySetterValue(PropertySetter):
1028
1025
        """Abstract class for PropertySetter recieving a value as
1029
1026
constructor argument instead of a class attribute."""
 
1027
 
1030
1028
        def __init__(self, value):
1031
1029
            self.value_to_set = value
1032
1030
 
1039
1037
    class SetChecker(PropertySetterValue):
1040
1038
        propname = "Checker"
1041
1039
 
1042
 
 
1043
1040
    class SetHost(PropertySetterValue):
1044
1041
        propname = "Host"
1045
1042
 
1046
 
 
1047
1043
    class SetSecret(PropertySetterValue):
1048
1044
        propname = "Secret"
1049
1045
 
1057
1053
            self._vts = value.read()
1058
1054
            value.close()
1059
1055
 
1060
 
 
1061
1056
    class PropertySetterValueMilliseconds(PropertySetterValue):
1062
1057
        """Abstract class for PropertySetterValue taking a value
1063
1058
argument as a datetime.timedelta() but should store it as
1072
1067
            "When setting, convert value from a datetime.timedelta"
1073
1068
            self._vts = int(round(value.total_seconds() * 1000))
1074
1069
 
1075
 
 
1076
1070
    class SetTimeout(PropertySetterValueMilliseconds):
1077
1071
        propname = "Timeout"
1078
1072
 
1079
 
 
1080
1073
    class SetExtendedTimeout(PropertySetterValueMilliseconds):
1081
1074
        propname = "ExtendedTimeout"
1082
1075
 
1083
 
 
1084
1076
    class SetInterval(PropertySetterValueMilliseconds):
1085
1077
        propname = "Interval"
1086
1078
 
1087
 
 
1088
1079
    class SetApprovalDelay(PropertySetterValueMilliseconds):
1089
1080
        propname = "ApprovalDelay"
1090
1081
 
1091
 
 
1092
1082
    class SetApprovalDuration(PropertySetterValueMilliseconds):
1093
1083
        propname = "ApprovalDuration"
1094
1084
 
1765
1755
        self.assertIs(ret, expected_method_return)
1766
1756
 
1767
1757
    def test_call_method_handles_exception(self):
1768
 
        dbus_logger = logging.getLogger("dbus.proxies")
1769
 
 
1770
1758
        def func():
1771
1759
            raise gi.repository.GLib.Error()
1772
1760