Exemple #1
0
 def __init__(self, tool, config):
     SELinuxEntryHandler.__init__(self, tool, config)
     self.filetool = POSIXFile(config)
     try:
         self.setype = selinux.selinux_getpolicytype()[1]
     except IndexError:
         self.logger.error("Unable to determine SELinux policy type")
         self.setype = None
Exemple #2
0
 def __init__(self, tool, logger, setup, config):
     SELinuxEntryHandler.__init__(self, tool, logger, setup, config)
     self.filetool = POSIXFile(logger, setup, config)
     try:
         self.setype = selinux.selinux_getpolicytype()[1]
     except IndexError:
         self.logger.error("Unable to determine SELinux policy type")
         self.setype = None
Exemple #3
0
 def __init__(self, config):
     POSIXTool.__init__(self, config)
     self._augeas = dict()
     # file tool for setting initial values of files that don't
     # exist
     self.filetool = POSIXFile(config)
Exemple #4
0
class POSIXAugeas(POSIXTool):
    """ Handle <Path type='augeas'...> entries.  See
    :ref:`client-tools-augeas`. """
    __req__ = ['name', 'mode', 'owner', 'group']

    def __init__(self, config):
        POSIXTool.__init__(self, config)
        self._augeas = dict()
        # file tool for setting initial values of files that don't
        # exist
        self.filetool = POSIXFile(config)

    def get_augeas(self, entry):
        """ Get an augeas object for the given entry. """
        if entry.get("name") not in self._augeas:
            aug = Augeas()
            if entry.get("lens"):
                self.logger.debug("Augeas: Adding %s to include path for %s" %
                                  (entry.get("name"), entry.get("lens")))
                incl = "/augeas/load/%s/incl" % entry.get("lens")
                ilen = len(aug.match(incl))
                if ilen == 0:
                    self.logger.error("Augeas: Lens %s does not exist" %
                                      entry.get("lens"))
                else:
                    aug.set("%s[%s]" % (incl, ilen + 1), entry.get("name"))
                    aug.load()
            self._augeas[entry.get("name")] = aug
        return self._augeas[entry.get("name")]

    def fully_specified(self, entry):
        return len(entry.getchildren()) != 0

    def get_commands(self, entry):
        """ Get a list of commands to verify or install.

        @param entry: The entry to get commands from.
        @type entry: lxml.etree._Element
        @param unverified: Only get commands that failed verification.
        @type unverified: bool
        @returns: list of
                  :class:`Bcfg2.Client.Tools.POSIX.Augeas.AugeasCommand`
                  objects representing the commands.
        """
        rv = []
        for cmd in entry.iterchildren():
            if cmd.tag == "Initial":
                continue
            if cmd.tag in globals():
                rv.append(globals()[cmd.tag](cmd, self.get_augeas(entry),
                                             self.logger))
            else:
                err = "Augeas: Unknown command %s in %s" % (cmd.tag,
                                                            entry.get("name"))
                self.logger.error(err)
                entry.set('qtext', "\n".join([entry.get('qtext', ''), err]))
        return rv

    def verify(self, entry, modlist):
        rv = True
        for cmd in self.get_commands(entry):
            try:
                if not cmd.verify():
                    err = "Augeas: Command has not been applied to %s: %s" % \
                          (entry.get("name"), cmd)
                    self.logger.debug(err)
                    entry.set('qtext', "\n".join([entry.get('qtext', ''),
                                                  err]))
                    rv = False
                    cmd.command.set("verified", "false")
                else:
                    cmd.command.set("verified", "true")
            except:  # pylint: disable=W0702
                err = "Augeas: Unexpected error verifying %s: %s: %s" % \
                      (entry.get("name"), cmd, sys.exc_info()[1])
                self.logger.error(err)
                entry.set('qtext', "\n".join([entry.get('qtext', ''), err]))
                rv = False
                cmd.command.set("verified", "false")
        return POSIXTool.verify(self, entry, modlist) and rv

    def install(self, entry):
        rv = True
        if entry.get("current_exists", "true") == "false":
            initial = entry.find("Initial")
            if initial is not None:
                self.logger.debug("Augeas: Setting initial data for %s" %
                                  entry.get("name"))
                file_entry = Bcfg2.Client.XML.Element("Path",
                                                      **dict(entry.attrib))
                file_entry.text = initial.text
                self.filetool.install(file_entry)
                # re-parse the file
                self.get_augeas(entry).load()
        for cmd in self.get_commands(entry):
            try:
                cmd.install()
            except:  # pylint: disable=W0702
                self.logger.error(
                    "Failure running Augeas command on %s: %s: %s" %
                    (entry.get("name"), cmd, sys.exc_info()[1]))
                rv = False
        try:
            self.get_augeas(entry).save()
        except:  # pylint: disable=W0702
            self.logger.error("Failure saving Augeas changes to %s: %s" %
                              (entry.get("name"), sys.exc_info()[1]))
            rv = False
        return POSIXTool.install(self, entry) and rv
Exemple #5
0
class SELinuxSemoduleHandler(SELinuxEntryHandler):
    """ handle SELinux module entries """

    etype = "module"
    value_format = (None, "disabled")

    def __init__(self, tool, logger, setup, config):
        SELinuxEntryHandler.__init__(self, tool, logger, setup, config)
        self.filetool = POSIXFile(logger, setup, config)
        try:
            self.setype = selinux.selinux_getpolicytype()[1]
        except IndexError:
            self.logger.error("Unable to determine SELinux policy type")
            self.setype = None

    @property
    def all_records(self):
        if self._all is None:
            try:
                # we get a list of tuples back; coerce it into a dict
                self._all = dict([(m[0], (m[1], m[2]))
                                  for m in self.records.get_all()])
            except AttributeError:
                # early versions of seobject don't have moduleRecords,
                # so we parse the output of `semodule` >_<
                self._all = dict()
                self.logger.debug("SELinux: Getting modules from semodule")
                try:
                    rv = self.tool.cmd.run(['semodule', '-l'])
                except OSError:
                    # semanage failed; probably not in $PATH.  try to
                    # get the list of modules from the filesystem
                    err = sys.exc_info()[1]
                    self.logger.debug("SELinux: Failed to run semodule: %s" %
                                      err)
                    self._all.update(self._all_records_from_filesystem())
                else:
                    if rv.success:
                        # ran semodule successfully
                        for line in rv.stdout.splitlines():
                            mod, version = line.split()
                            self._all[mod] = (version, 1)

                        # get other (disabled) modules from the filesystem
                        for mod in self._all_records_from_filesystem().keys():
                            if mod not in self._all:
                                self._all[mod] = ('', 0)
                    else:
                        self.logger.error("SELinux: Failed to run semodule: %s"
                                          % rv.error)
                        self._all.update(self._all_records_from_filesystem())
        return self._all

    def _all_records_from_filesystem(self):
        """ the seobject API doesn't support modules and semodule is
        broken or missing, so just list modules on the filesystem.
        this is terrible. """
        self.logger.debug("SELinux: Getting modules from filesystem")
        rv = dict()
        for mod in glob.glob(os.path.join("/usr/share/selinux", self.setype,
                                          "*.pp")):
            rv[os.path.basename(mod)[:-3]] = ('', 1)
        return rv

    def _key(self, entry):
        name = entry.get("name").lstrip("/")
        if name.endswith(".pp"):
            return name[:-3]
        else:
            return name

    def _key2attrs(self, key):
        rv = SELinuxEntryHandler._key2attrs(self, key)
        status = self.all_records[key][1]
        if status:
            rv['disabled'] = "false"
        else:
            rv['disabled'] = "true"
        return rv

    def _filepath(self, entry):
        """ get the path to the .pp module file for this module entry
        """
        return os.path.join("/usr/share/selinux", self.setype,
                            entry.get("name") + '.pp')

    def _pathentry(self, entry):
        """ Get an XML Path entry based on this SELinux module entry,
        suitable for installing the module .pp file itself to the
        filesystem """
        pathentry = copy.deepcopy(entry)
        pathentry.set("name", self._filepath(pathentry))
        pathentry.set("mode", "0644")
        pathentry.set("owner", "root")
        pathentry.set("group", "root")
        pathentry.set("secontext", "__default__")
        return pathentry

    def Verify(self, entry):
        if not entry.get("disabled"):
            entry.set("disabled", "false")
        return (SELinuxEntryHandler.Verify(self, entry) and
                self.filetool.verify(self._pathentry(entry), []))

    def canInstall(self, entry):
        return (entry.text and self.setype and
                SELinuxEntryHandler.canInstall(self, entry))

    def Install(self, entry, _=None):
        if not self.filetool.install(self._pathentry(entry)):
            return False
        if hasattr(seobject, 'moduleRecords'):
            # if seobject has the moduleRecords attribute, install the
            # module using the seobject library
            return self._install_seobject(entry)
        else:
            # seobject doesn't have the moduleRecords attribute, so
            # install the module using `semodule`
            self.logger.debug("Installing %s using semodule" %
                              entry.get("name"))
            self._all = None
            return self._install_semodule(entry)

    def _install_seobject(self, entry):
        """ Install an SELinux module using the seobject library """
        try:
            if not SELinuxEntryHandler.Install(self, entry):
                return False
        except NameError:
            # some versions of selinux have a bug in seobject that
            # makes modify() calls fail.  add() seems to have the same
            # effect as modify, but without the bug
            if self.exists(entry):
                if not SELinuxEntryHandler.Install(self, entry, method="add"):
                    return False

        if entry.get("disabled", "false").lower() == "true":
            method = "disable"
        else:
            method = "enable"
        return SELinuxEntryHandler.Install(self, entry, method=method)

    def _install_semodule(self, entry, fromqueue=False):
        """ Install an SELinux module using the semodule command """
        if fromqueue:
            self.logger.debug("Installing SELinux module %s from "
                              "post-transaction queue" % entry.get("name"))
        elif self.tool.txn:
            # we've started a transaction, so if we run semodule -i
            # then it'll fail with lock errors.  so we add this
            # installation to a queue to be run after the transaction
            # is closed.
            self.logger.debug("Waiting to install SELinux module %s until "
                              "SELinux transaction is finished" %
                              entry.get('name'))
            self.tool.post_txn_queue.append((self._install_semodule,
                                             (entry,),
                                             dict(fromqueue=True)))
            return False
        self.logger.debug("Install SELinux module %s with semodule -i %s" %
                          (entry.get('name'), self._filepath(entry)))
        try:
            rv = self.tool.cmd.run(['semodule', '-i', self._filepath(entry)])
        except OSError:
            err = sys.exc_info()[1]
            self.logger.error("Failed to install SELinux module %s with "
                              "semodule: %s" % (entry.get("name"), err))
            return False
        if rv.success:
            if entry.get("disabled", "false").lower() == "true":
                self.logger.warning("SELinux: Cannot disable modules with "
                                    "semodule")
                return False
            else:
                return True
        else:
            self.logger.error("Failed to install SELinux module %s with "
                              "semodule: %s" % (entry.get("name"), rv.error))
            return False

    def _addargs(self, entry):
        """ argument list for adding entries """
        return (self._filepath(entry),)

    def _defaultargs(self, entry):
        """ argument list for modifying and deleting entries """
        return (entry.get("name"),)

    def FindExtra(self):
        specified = [self._key(e)
                     for e in self.tool.getSupportedEntries()]
        rv = []
        for module in self._all_records_from_filesystem().keys():
            if module not in specified:
                rv.append(self.key2entry(module))
        return rv
Exemple #6
0
class SELinuxSemoduleHandler(SELinuxEntryHandler):
    """ handle SELinux module entries """

    etype = "module"
    value_format = (None, "disabled")

    def __init__(self, tool, config):
        SELinuxEntryHandler.__init__(self, tool, config)
        self.filetool = POSIXFile(config)
        try:
            self.setype = selinux.selinux_getpolicytype()[1]
        except IndexError:
            self.logger.error("Unable to determine SELinux policy type")
            self.setype = None

    @property
    def all_records(self):
        if self._all is None:
            try:
                # we get a list of tuples back; coerce it into a dict
                self._all = dict([(m[0], (m[1], m[2]))
                                  for m in self.records.get_all()])
            except AttributeError:
                # early versions of seobject don't have moduleRecords,
                # so we parse the output of `semodule` >_<
                self._all = dict()
                self.logger.debug("SELinux: Getting modules from semodule")
                try:
                    rv = self.tool.cmd.run(['semodule', '-l'])
                except OSError:
                    # semanage failed; probably not in $PATH.  try to
                    # get the list of modules from the filesystem
                    err = sys.exc_info()[1]
                    self.logger.debug("SELinux: Failed to run semodule: %s" %
                                      err)
                    self._all.update(self._all_records_from_filesystem())
                else:
                    if rv.success:
                        # ran semodule successfully
                        for line in rv.stdout.splitlines():
                            mod, version = line.split()
                            self._all[mod] = (version, 1)

                        # get other (disabled) modules from the filesystem
                        for mod in self._all_records_from_filesystem().keys():
                            if mod not in self._all:
                                self._all[mod] = ('', 0)
                    else:
                        self.logger.error("SELinux: Failed to run semodule: %s"
                                          % rv.error)
                        self._all.update(self._all_records_from_filesystem())
        return self._all

    def _all_records_from_filesystem(self):
        """ the seobject API doesn't support modules and semodule is
        broken or missing, so just list modules on the filesystem.
        this is terrible. """
        self.logger.debug("SELinux: Getting modules from filesystem")
        rv = dict()
        for mod in glob.glob(os.path.join("/usr/share/selinux", self.setype,
                                          "*.pp")):
            rv[os.path.basename(mod)[:-3]] = ('', 1)
        return rv

    def _key(self, entry):
        name = entry.get("name").lstrip("/")
        if name.endswith(".pp"):
            return name[:-3]
        else:
            return name

    def _key2attrs(self, key):
        rv = SELinuxEntryHandler._key2attrs(self, key)
        status = self.all_records[key][1]
        if status:
            rv['disabled'] = "false"
        else:
            rv['disabled'] = "true"
        return rv

    def _filepath(self, entry):
        """ get the path to the .pp module file for this module entry
        """
        return os.path.join("/usr/share/selinux", self.setype,
                            entry.get("name") + '.pp')

    def _pathentry(self, entry):
        """ Get an XML Path entry based on this SELinux module entry,
        suitable for installing the module .pp file itself to the
        filesystem """
        pathentry = copy.deepcopy(entry)
        pathentry.set("name", self._filepath(pathentry))
        pathentry.set("mode", "0644")
        pathentry.set("owner", "root")
        pathentry.set("group", "root")
        pathentry.set("secontext", "__default__")
        return pathentry

    def Verify(self, entry):
        if not entry.get("disabled"):
            entry.set("disabled", "false")
        return (SELinuxEntryHandler.Verify(self, entry) and
                self.filetool.verify(self._pathentry(entry), []))

    def canInstall(self, entry):
        return (entry.text and self.setype and
                SELinuxEntryHandler.canInstall(self, entry))

    def Install(self, entry, _=None):
        if not self.filetool.install(self._pathentry(entry)):
            return False
        if hasattr(seobject, 'moduleRecords'):
            # if seobject has the moduleRecords attribute, install the
            # module using the seobject library
            return self._install_seobject(entry)
        else:
            # seobject doesn't have the moduleRecords attribute, so
            # install the module using `semodule`
            self.logger.debug("Installing %s using semodule" %
                              entry.get("name"))
            self._all = None
            return self._install_semodule(entry)

    def _install_seobject(self, entry):
        """ Install an SELinux module using the seobject library """
        try:
            if not SELinuxEntryHandler.Install(self, entry):
                return False
        except NameError:
            # some versions of selinux have a bug in seobject that
            # makes modify() calls fail.  add() seems to have the same
            # effect as modify, but without the bug
            if self.exists(entry):
                if not SELinuxEntryHandler.Install(self, entry, method="add"):
                    return False

        if entry.get("disabled", "false").lower() == "true":
            method = "disable"
        else:
            method = "enable"
        return SELinuxEntryHandler.Install(self, entry, method=method)

    def _install_semodule(self, entry, fromqueue=False):
        """ Install an SELinux module using the semodule command """
        if fromqueue:
            self.logger.debug("Installing SELinux module %s from "
                              "post-transaction queue" % entry.get("name"))
        elif self.tool.txn:
            # we've started a transaction, so if we run semodule -i
            # then it'll fail with lock errors.  so we add this
            # installation to a queue to be run after the transaction
            # is closed.
            self.logger.debug("Waiting to install SELinux module %s until "
                              "SELinux transaction is finished" %
                              entry.get('name'))
            self.tool.post_txn_queue.append((self._install_semodule,
                                             (entry,),
                                             dict(fromqueue=True)))
            return False
        self.logger.debug("Install SELinux module %s with semodule -i %s" %
                          (entry.get('name'), self._filepath(entry)))
        try:
            rv = self.tool.cmd.run(['semodule', '-i', self._filepath(entry)])
        except OSError:
            err = sys.exc_info()[1]
            self.logger.error("Failed to install SELinux module %s with "
                              "semodule: %s" % (entry.get("name"), err))
            return False
        if rv.success:
            if entry.get("disabled", "false").lower() == "true":
                self.logger.warning("SELinux: Cannot disable modules with "
                                    "semodule")
                return False
            else:
                return True
        else:
            self.logger.error("Failed to install SELinux module %s with "
                              "semodule: %s" % (entry.get("name"), rv.error))
            return False

    def _addargs(self, entry):
        """ argument list for adding entries """
        return (self._filepath(entry),)

    def _defaultargs(self, entry):
        """ argument list for modifying and deleting entries """
        return (entry.get("name"),)

    def FindExtra(self):
        specified = [self._key(e)
                     for e in self.tool.getSupportedEntries()]
        rv = []
        for module in self._all_records_from_filesystem().keys():
            if module not in specified:
                rv.append(self.key2entry(module))
        return rv
Exemple #7
0
 def __init__(self, config):
     POSIXTool.__init__(self, config)
     self._augeas = dict()
     # file tool for setting initial values of files that don't
     # exist
     self.filetool = POSIXFile(config)
Exemple #8
0
class POSIXAugeas(POSIXTool):
    """ Handle <Path type='augeas'...> entries.  See
    :ref:`client-tools-augeas`. """
    __req__ = ['name', 'mode', 'owner', 'group']

    def __init__(self, config):
        POSIXTool.__init__(self, config)
        self._augeas = dict()
        # file tool for setting initial values of files that don't
        # exist
        self.filetool = POSIXFile(config)

    def get_augeas(self, entry):
        """ Get an augeas object for the given entry. """
        if entry.get("name") not in self._augeas:
            aug = Augeas()
            if entry.get("lens"):
                self.logger.debug("Augeas: Adding %s to include path for %s" %
                                  (entry.get("name"), entry.get("lens")))
                incl = "/augeas/load/%s/incl" % entry.get("lens")
                ilen = len(aug.match(incl))
                if ilen == 0:
                    self.logger.error("Augeas: Lens %s does not exist" %
                                      entry.get("lens"))
                else:
                    aug.set("%s[%s]" % (incl, ilen + 1), entry.get("name"))
                    aug.load()
            self._augeas[entry.get("name")] = aug
        return self._augeas[entry.get("name")]

    def fully_specified(self, entry):
        return len(entry.getchildren()) != 0

    def get_commands(self, entry):
        """ Get a list of commands to verify or install.

        @param entry: The entry to get commands from.
        @type entry: lxml.etree._Element
        @param unverified: Only get commands that failed verification.
        @type unverified: bool
        @returns: list of
                  :class:`Bcfg2.Client.Tools.POSIX.Augeas.AugeasCommand`
                  objects representing the commands.
        """
        rv = []
        for cmd in entry:
            if cmd.tag == "Initial":
                continue
            if cmd.tag in globals():
                rv.append(globals()[cmd.tag](entry, cmd,
                                             self.get_augeas(entry),
                                             self.logger))
            else:
                err = "Augeas: Unknown command %s in %s" % (cmd.tag,
                                                            entry.get("name"))
                self.logger.error(err)
                entry.set('qtext', "\n".join([entry.get('qtext', ''), err]))
        return rv

    def verify(self, entry, modlist):
        rv = True
        for cmd in self.get_commands(entry):
            try:
                if not cmd.verify():
                    err = "Augeas: Command has not been applied to %s: %s" % \
                          (entry.get("name"), cmd)
                    self.logger.debug(err)
                    entry.set('qtext', "\n".join([entry.get('qtext', ''),
                                                  err]))
                    rv = False
                    cmd.command.set("verified", "false")
                else:
                    cmd.command.set("verified", "true")
            except:  # pylint: disable=W0702
                err = "Augeas: Unexpected error verifying %s: %s: %s" % \
                      (entry.get("name"), cmd, sys.exc_info()[1])
                self.logger.error(err)
                entry.set('qtext', "\n".join([entry.get('qtext', ''), err]))
                rv = False
                cmd.command.set("verified", "false")
        return POSIXTool.verify(self, entry, modlist) and rv

    def install(self, entry):
        rv = True
        if entry.get("current_exists", "true") == "false":
            initial = entry.find("Initial")
            if initial is not None:
                self.logger.debug("Augeas: Setting initial data for %s" %
                                  entry.get("name"))
                file_entry = Bcfg2.Client.XML.Element("Path",
                                                      **dict(entry.attrib))
                file_entry.text = initial.text
                self.filetool.install(file_entry)
                # re-parse the file
                self.get_augeas(entry).load()
        for cmd in self.get_commands(entry):
            try:
                cmd.install()
            except:  # pylint: disable=W0702
                self.logger.error(
                    "Failure running Augeas command on %s: %s: %s" %
                    (entry.get("name"), cmd, sys.exc_info()[1]))
                rv = False
        try:
            self.get_augeas(entry).save()
        except:  # pylint: disable=W0702
            self.logger.error("Failure saving Augeas changes to %s: %s" %
                              (entry.get("name"), sys.exc_info()[1]))
            rv = False
        return POSIXTool.install(self, entry) and rv