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
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
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)
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
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
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
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