class InstalledSoftwareVerification(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        """Constructor"""
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 230
        self.rulename = 'InstalledSoftwareVerification'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.guidance = [
            'NSA 2.1.3.2', 'CCE 14931-0', 'CCE-RHEL7-CCE-TBD 2.1.3.2.1'
        ]
        self.applicable = {
            'type': 'white',
            'os': {
                'Red Hat Enterprise Linux': ['6.0', '+'],
                'CentOS Linux': ['7.0', '+']
            }
        }

        datatype = 'bool'
        key = 'FIXPERMISSIONS'
        instructions = 'If set to True, this rule will fix the permissions \
of the package for any file which has a permission deviation from the vendor \
default.'

        default = True
        self.fixPermsCi = self.initCi(datatype, key, instructions, default)
        self.sethelptext()

    def getInstalledPackages(self):
        """return a list of installed packages (as reported
        by rpm database)


        :returns: installedpackages

        :rtype: list
@author: Breen Malmberg

        """

        installedpackages = []

        listinstalledcmd = "/usr/bin/rpm -qa"

        self.ch.executeCommand(listinstalledcmd)
        outputlist = self.ch.getOutput()
        retcode = self.ch.getReturnCode()
        if retcode == 0:
            installedpackages = outputlist
        else:
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)

        return installedpackages

    def report(self):
        """Compile a list of files not conforming to rpm package database permissions (Mode)
        report non-compliant if any are found
        else report compliant


        :returns: self.compliant

        :rtype: bool
@author: Eric Ball
@author: Breen Malmberg
@change: Breen Malmberg - 07/30/2018 - complete re-write of method

        """

        self.detailedresults = ""
        self.compliant = True
        self.ch = CommandHelper(self.logger)
        reportcmd = "/usr/bin/rpm -V --nosignature --nolinkto --nofiledigest --nosize --nomtime --nordev --nocaps "
        self.badpermfiles = []
        self.badpermpkgs = {}
        self.badgroupfiles = []
        self.badownerfiles = []
        self.badhashfiles = []

        try:

            self.logger.log(
                LogPriority.DEBUG,
                "Searching for files with incorrect permissions...")

            installedpkgs = self.getInstalledPackages()

            for pkg in installedpkgs:
                self.ch.executeCommand(reportcmd + pkg)
                outputlist = self.ch.getOutput()
                self.badpermpkgs[pkg] = []
                for line in outputlist:
                    # search for bad permissions
                    if re.search("^.*(\.+M|M\.+)", line, re.IGNORECASE):
                        sline = line.split()
                        self.badpermpkgs[pkg].append(sline[len(sline) - 1])
                        self.badpermfiles.append(sline[len(sline) - 1])
                    # search for bad group ownership
                    if re.search("^.*(\.+G|G\.+)", line, re.IGNORECASE):
                        sline = line.split()
                        self.badgroupfiles.append(sline[len(sline) - 1])
                    # search for bad ownership (user)
                    if re.search("^.*(\.+U|U\.+)", line, re.IGNORECASE):
                        sline = line.split()
                        self.badownerfiles.append(sline[len(sline) - 1])
                    # search for bad md5 hash
                    if re.search("^.*(\.+5|5\.+)", line, re.IGNORECASE):
                        sline = line.split()
                        self.badhashfiles.append(sline[len(sline) - 1])

            if self.badpermfiles:
                self.compliant = False
                self.detailedresults += "\nThe following package files have incorrect permissions:\n" + "\n".join(
                    self.badpermfiles)
            if self.badgroupfiles:
                self.compliant = False
                self.detailedresults += "\n\nThe following package files have bad group ownership:\n" + "\n".join(
                    self.badgroupfiles)
            if self.badownerfiles:
                self.compliant = False
                self.detailedresults += "\n\nThe following package files have bad ownership:\n" + "\n".join(
                    self.badownerfiles)
            if self.badhashfiles:
                self.compliant = False
                self.detailedresults += "\n\nThe following package files have bad MD5 checksums:\n" + "\n".join(
                    self.badhashfiles)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.compliant = False
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def fix(self):
        """The fix method changes permissions to the package defaults.


        :returns: self.rulesuccess

        :rtype: bool
@author: Eric Ball
@author: Breen Malmberg
@change: Breen Malmberg - 07/30/2018 - re-write of entire method

        """

        self.detailedresults = ""
        self.rulesuccess = True
        fixpermscmd = "/usr/bin/rpm --setperms "

        try:

            if not self.fixPermsCi.getcurrvalue():
                return self.rulesuccess

            for pkg in self.badpermpkgs:
                if self.badpermpkgs[pkg]:
                    self.ch.executeCommand(fixpermscmd + pkg)
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        self.rulesuccess = False

            self.detailedresults += "\n\nPlease note that we will not attempt to fix ownership, group ownership, or bad md5 checksums. For suggestions on what to do if files are found with these issues, please see the rule's help text."
            self.detailedresults += "\nIt is expected that this rule will still be non-compliant after fix if files are found with incorrect ownership or group ownership."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess
Ejemplo n.º 2
0
class KVEditor(object):
    '''The main parent class for The Key Value Editor Class group.  Call the
    validate def to see if the specified values are either present or not based
    on the intent desired, "present" or "notpresent", where, "present" means
    the values you set are desired in the configuration file and "notpresent"
    means the values you set are not desired in the configuration file.  If you
    have a mixture of desired key-values and undesired key-values you must set
    intent for each time you change intents then setData with the new data.  Do
    not run commit until all'''

    def __init__(self, stchlgr, logger, kvtype, path, tmpPath, data, intent="",
                 configType="", output=""):
        '''
        KVEditor constructor
        @param stchlgr: StateChgLogger object
        @param logger: logger object
        @param kvtype: Type of key-value file.
                       Valid values: "tagconf", "conf", "defaults", "profiles"
        @param path: Path to key-value file
        @param tmpPath: Path to temp file for key-value list
        @param data: Dict of key-value data
        @param intent: "present" or "notpresent"
        @param configType: Specify how the config options are separated.
                           Valid values: "space", "openeq", "closedeq"
        @param output: Output of profiler command, used only by KVAProfiles
        '''
        self.kvtype = kvtype
        self.path = path
        self.tmpPath = tmpPath
        self.logger = logger
        self.configType = configType
        self.data = data
        self.output = output
        self.detailedresults = ""
        self.missing = []
        self.fixables = {}
        self.fixlist = []
        self.removeables = {}
        self.intent = intent
        self.container = {}
        self.iditerator = 0
        self.idcontainer = []
        if self.kvtype == "tagconf":
            if not self.getPath:
                return None
            self.editor = KVATaggedConf.KVATaggedConf(self.path, self.tmpPath,
                                                      self.intent,
                                                      self.configType,
                                                      self.logger)
        elif self.kvtype == "conf":
            if not self.getPath:
                return None
            self.editor = KVAConf.KVAConf(self.path, self.tmpPath, self.intent,
                                          self.configType, self.logger)
        elif self.kvtype == "defaults":
            self.editor = KVADefault.KVADefault(self.path, self.logger,
                                                self.data)
        elif self.kvtype == "profiles":
            self.editor = KVAProfiles.KVAProfiles(self.logger, self.path)
        else:
            self.detailedresults = "Not one of the supported kveditor types"
            self.logger.log(LogPriority.DEBUG,
                            ["KVEditor.__init__", self.detailedresults])
            return None

    def setData(self, data):
        if data is None:
            return False
        elif data == "":
            return False
        else:
            self.data = data
            return True

    def getData(self):
        return self.data

    def updateData(self, data):
        self.data = data

    def setIntent(self, intent):
        if intent == "present" or intent == "notpresent":
            self.intent = intent
            self.editor.setIntent(intent)
            return True
        else:
            return False

    def getIntent(self):
        return self.intent

    def setPath(self, path):
        if not os.path.exists(path):
            self.detailedresults = "File path does not exist"
            self.logger.log(LogPriority.INFO,
                            ["KVEditor", self.detailedresults])
            return False
        else:
            self.path = path
            self.editor.setPath(path)
            return True

    def getPath(self):
        if not os.path.exists(self.path):
            debug = "File path does not exist\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return False
        else:
            return self.path

    def setTmpPath(self, tmpPath):
        self.tmpPath = tmpPath
        self.editor.setTmpPath(tmpPath)
        return True

    def getTmpPath(self):
        return self.tmpPath

    def getType(self):
        return self.kvtype

    def setConfigType(self, configType):
        self.configType = configType
        self.editor.setConfigType(configType)
        return True

    def getConfigType(self):
        return self.configType

    def validate(self):
        try:
            status = False
            if self.kvtype == "defaults":
                status = self.validateDefaults()
            elif self.kvtype == "plist":
                status = self.validatePlist()
            elif self.kvtype == "conf":
                status = self.validateConf()
            elif self.kvtype == "tagconf":
                status = self.validateTag()
            elif self.kvtype == "profiles":
                status = self.validateProfiles()
            else:
                status = "invalid"
            debug = "KVEditor is returning " + str(status) + " back to " + \
                "KVEditorStonix.report()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return status
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            raise

    def update(self):
        try:
            status = False
            if self.kvtype == "defaults":
                status = self.updateDefaults()
            elif self.kvtype == "plist":
                status = self.updatePlist()
            elif self.kvtype == "conf":
                status = self.updateConf()
            elif self.kvtype == "tagconf":
                status = self.updateTag()
            elif self.kvtype == "profiles":
                status = self.updateProfiles()
            else:
                status = False
            debug = "KVEditor is returning " + str(status) + " back to " + \
                "KVEditorStonix.fix()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return status
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            raise

    def validateDefaults(self):
        if isinstance(self.data, dict):
            if not self.checkDefaults(self.data):
                return False
        else:
            return False
        if self.editor.validate():
            return True
        else:
            return False

    def updateDefaults(self):
        if self.editor.update():
            debug = "KVEditor.updateDefaults() is returning True to " + \
                "KVEditor.update()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return True
        else:
            debug = "KVEditor.updateDefaults() is returning False to " + \
                "KVEditor.update()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return False

    def checkDefaults(self, data):
        for k, v in data.iteritems():
            if isinstance(v, dict):
                retval = self.checkDefaults(v)
                return retval
            elif isinstance(v, list):
                if len(v) == 2 or len(v) == 3:
                    return True
                else:
                    return False
            else:
                return False

    def setCurrentHostbool(self, val):
        self.editor.currentHost = val

    def validateConf(self):
        validate = True
        if not self.checkConf():
            return False
        if self.intent == "present":
            for k, v in self.data.iteritems():
                retval = self.editor.validate(k, v)
                if retval == "invalid":
                    validate = "invalid"
                elif isinstance(retval, list):
                    self.fixables[k] = retval
                    validate = False
                elif not retval:
                    validate = False
                    self.fixables[k] = v
        if self.intent == "notpresent":
            for k, v in self.data.iteritems():
                retval = self.editor.validate(k, v)
                if retval == "invalid":
                    validate = "invalid"
                elif isinstance(retval, list):
                    self.removeables[k] = retval
                    validate = False
                elif retval is True:
                    validate = False
                    self.removeables[k] = v
        if validate == "invalid":
            debug = "KVEditor.validateConf() is returning invalid to " + \
                "KVEditor.validate()\n"
        elif validate:
            debug = "KVEditor.validateConf() is returning True to " + \
                "KVEditor.validate()\n"
        else:
            debug = "KVEditor.validateConf() is returning False to " + \
                "KVEditor.validate()\n"
        self.logger.log(LogPriority.DEBUG, debug)
        return validate

    def updateConf(self):
        if self.fixables or self.removeables:
            if self.editor.update(self.fixables, self.removeables):
                debug = "KVEditor.updateConf() is returning True to " + \
                    "KVEditor.update()\n"
                self.logger.log(LogPriority.DEBUG, debug)
                return True
            else:
                debug = "KVEditor.updateConf() is returning False to " + \
                    "KVEditor.update()\n"
                self.logger.log(LogPriority.DEBUG, debug)
                return False

    def checkConf(self):
        if isinstance(self.data, dict):
            return True
        else:
            return False

    def validateTag(self):
        validate = True
        keyvals = {}
        if not self.checkTag():
            return False
        if self.intent == "present":
            for tag in self.data:
                keyvals = self.editor.getValue(tag, self.data[tag])
                if keyvals == "invalid":
                    validate = "invalid"
                elif isinstance(keyvals, dict):
                    self.fixables[tag] = keyvals
                    validate = False
        if self.intent == "notpresent":
            for tag in self.data:
                keyvals = self.editor.getValue(tag, self.data[tag])
                if keyvals == "invalid":
                    validate = "invalid"
                elif isinstance(keyvals, dict):
                    self.removeables[tag] = keyvals
                    validate = False
        if validate == "invalid":
            debug = "KVEditor.validateTag() is returning invalid to " + \
                "KVEditor.validate()\n"
        elif validate:
            debug = "KVEditor.validateTag() is returning True to " + \
                "KVEditor.validate()\n"
        else:
            debug = "KVEditor.validateTag() is returning False to " + \
                "KVEditor.validate()\n"
        self.logger.log(LogPriority.DEBUG, debug)
        return validate

    def updateTag(self):
        if self.editor.setValue(self.fixables, self.removeables):
            debug = "KVEditor.updateTag() is returning True to " + \
                "KVEditor.update()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return True
        else:
            debug = "KVEditor.updateTag() is returning False to " + \
                "KVEditor.update()\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return False

    def checkTag(self):
        if isinstance(self.data, dict):
            for tag in self.data:
                if not isinstance(self.data[tag], dict):
                        return False
            return True
        else:
            return False

    def validateProfiles(self):
        '''
        @since: 3/10/2016
        @author: dwalker
        @var self.data: A dictionary in the form of {k: {v: ["numberValue",
                                                             "datatype",
                                                             "acceptableDeviation"(optional)],
                                                        v: ["", "", ""],
                                                        v: ["", "", ""],
                                                        .
                                                        .
                                                        .}}
        @var: k: The profile sub-identifier e.g.
            com.apple.mobiledevice.passwordpolicy
        @var v: The profile data key-value pairs in a dictionary e.g.
            allowSimple that will appear in the output of the system_profiler
            command within the first opening brace after the profile
            sub-identifier.  v also contains an associated list containing:
            [a,b,c]
            a) the value on the other side of the = sign
            b) whether that value is an integer(int) or a boolean(bool)
            c) (optional) whether the value present after the = sign(a),
                if an int, can be lower(less) or higher(more) in order to
                detect and represent stringency (see self.data description
                above).
        @return: Value returned from validate method in factory sub-class
        @rtype: bool
        '''
        cmd = ["/usr/sbin/system_profiler", "SPConfigurationProfileDataType"]
        self.ch = CommandHelper(self.logger)
        if self.ch.executeCommand(cmd):
            self.output = self.ch.getOutput()
            retval = True
            if self.output:
                for k, v in self.data.iteritems():
                    retval = self.editor.validate(self.output, k, v)
                    if not retval:
                        return False
            else:
                debug = "There are no profiles installed"
                self.logger.log(LogPriority.DEBUG, debug)
                return False
        return True

    def updateProfiles(self):
        retval = self.editor.update()
        return retval
    def commit(self):
        if self.kvtype == "defaults" or self.kvtype == "profiles":
            retval = self.editor.commit()
            return retval
        else:
            retval = self.editor.commit()
            self.fixables = {}
            self.removeables = {}
            return retval

    def removekey(self, d, key):
        r = dict(d)
        del r[key]
        return r
Ejemplo n.º 3
0
class Zypper(object):

    """The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    @author: Derek T Walker
    @change: 2012/08/08 dwalker - Original Implementation
    @change: 2014/09/10 dkennel - Added -n option to search command string
    @change: 2014/12/24 bemalmbe - fixed a typo in the old search string
    @change: 2014/12/24 bemalmbe - changed search strings to be match exact and
        search for installed or available separately
    @change: 2014/12/24 bemalmbe - fixed multiple pep8 violations
    @change: 2015/08/20 eball - Added getPackageFromFile and self.rpm var
    """

    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/bin/zypper --non-interactive install "
        self.remove = "/usr/bin/zypper --non-interactive remove "
        self.searchi = "/usr/bin/zypper --non-interactive search --match-exact -i "
        self.searchu = "/usr/bin/zypper --non-interactive search --match-exact -u "
        self.rpm = "/bin/rpm -q "

    ###############################################################################
    def installpackage(self, package):
        """Install a package. Return a bool indicating success or failure.
        @param string package : Name of the package to be installed, must be
            recognizable to the underlying package manager.
        @return: bool
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - fixed method doc string formatting
        """
        try:
            installed = False
            self.ch.executeCommand(self.install + package)
            output = self.ch.getOutputString()
            if self.ch.getReturnCode() == 0:
                if search("Abort, retry, ignore", output):
                    self.detailedresults += "There is an error contacting " + "one or more repos, aborting\n"
                    return False
                self.detailedresults += package + " pkg installed successfully\n"
                installed = True
            else:
                self.detailedresults += package + " pkg not able to install\n"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return installed
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

    ###############################################################################
    def removepackage(self, package):
        """Remove a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be removed, must be
            recognizable to the underlying package manager.
        @return: bool
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - fixed method doc string formatting
        @change: 12/24/2014 - bemalmbe - fixed an issue with var 'removed' not
            being initialized before it was called
        """
        removed = False
        try:
            self.ch.executeCommand(self.remove + package)
            output = self.ch.getOutputString()
            if self.ch.getReturnCode() == 0:
                if search("Abort, retry, ignore", output):
                    self.detailedresults += "There is an error contacting " + "one or more repos, aborting\n"
                    return False
                self.detailedresults += package + " pkg removed successfully\n"
                removed = True
            else:
                self.detailedresults += package + " pkg not able to be removed\n"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return removed
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

    ###############################################################################
    def checkInstall(self, package):
        """
        Check the installation status of a package. Return a bool; True if
        the package is installed.

        @param string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: bool
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - fixed method doc string formatting
        @change: 12/24/2014 - bemalmbe - changed var name 'found' to
            'installed'
        @change: 12/24/2014 - bemalmbe - now uses correct search syntax
        @change: 12/24/2014 - bemalmbe - removed detailedresults update on
            'found but not installed' as this no longer applies to this method
        """

        try:
            installed = False
            self.ch.executeCommand(self.searchi + package)
            if self.ch.getReturnCode() == 0:
                output = self.ch.getOutput()
                outputStr = self.ch.getOutputString()
                if search("Abort, retry, ignore", outputStr):
                    self.detailedresults += "There is an error contacting " + "one or more repos, aborting\n"
                    return False
                for line in output:
                    if search(package, line):
                        installed = True
                if installed:
                    self.detailedresults += package + " pkg is installed\n"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return True
            else:
                installed = False
                self.detailedresults += (
                    package
                    + " pkg not found or may be \
misspelled\n"
                )
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

    ###############################################################################
    def checkAvailable(self, package):
        """
        check if given package is available to install on the current system

        @param: package string name of package to search for
        @return: bool
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - added method documentation
        @change: 12/24/2014 - bemalmbe - changed var name 'found' to
            'available'
        @change: 12/24/2014 - bemalmbe - fixed search syntax and updated search
            variable name
        """
        try:
            available = False
            self.ch.executeCommand(self.searchu + package)
            if self.ch.getReturnCode() == 0:
                output = self.ch.getOutput()
                for line in output:
                    if search(package, line):
                        available = True
                if available:
                    self.detailedresults += package + " pkg is available\n"
                else:
                    self.detailedresults += package + " pkg is not available\n"
            else:
                self.detailedresults = (
                    package
                    + " pkg not found or may be \
misspelled\n"
                )
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return available
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

    ###############################################################################
    def getPackageFromFile(self, filename):
        """Returns the name of the package that provides the given
        filename/path.

        @param: string filename : The name or path of the file to resolve
        @return: string name of package if found, None otherwise
        @author: Eric Ball
        """
        try:
            self.ch.executeCommand(self.rpm + "-f " + filename)
            if self.ch.getReturnCode() == 0:
                return self.ch.getOutputString()
            else:
                return None
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)

    ###############################################################################
    def getInstall(self):
        """
        return the install command string for the zypper pkg manager

        @return: string
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - added method documentation
        """

        return self.install

    ###############################################################################
    def getRemove(self):
        """
        return the uninstall/remove command string for the zypper pkg manager

        @return: string
        @author: dwalker
        @change: 12/24/2014 - bemalmbe - added method documentation
        """

        return self.remove
Ejemplo n.º 4
0
class ConfigureMACPolicy(Rule):
    """The ConfigureMACPolicy class configures either selinux or apparmor
    depending on the os platform.
    @change: Derek Walker - created two config items, one for enable/disable, and
        another for whether the user wants to use permissive or enforcing

    """

    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 107
        self.rulename = 'ConfigureMACPolicy'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.guidance = ['NSA(2.1.1.6)(2.4.2)', 'CCE-3977-6', 'CCE-3999-0',
                         'CCE-3624-4', 'CIS 1.7']
        self.applicable = {'type': 'white',
                           'family': ['linux']}

        datatype = "bool"
        key = "CONFIGUREMAC"
        instructions = "To prevent the configuration of a mandatory " + \
            "access control policy, set the value of CONFIGUREMAC to " + \
            "False. Note: The 'mandatory access control' is either SELinux " + \
            "or AppArmor, depending on what is available to your current system."
        default = True
        self.ConfigureMAC = self.initCi(datatype, key, instructions, default)

        datatype2 = "string"
        key2 = "MODE"
        default2 = "permissive"
        instructions2 = "Valid modes for SELinux are: permissive or " + \
            "enforcing\nValid modes for AppArmor are: complain or enforce"
        self.modeci = self.initCi(datatype2, key2, instructions2, default2)

    def report(self):
        """

        :return: 
        """

        self.selinux = False
        self.apparmor = False
        self.ph = Pkghelper(self.logger, self.environ)
        self.ch = CommandHelper(self.logger)
        self.sh = ServiceHelper(self.environ, self.logger)
        selinux_packages = ["selinux", "libselinux", "selinux-basics"]
        apparmor_packages = ["apparmor"]
        self.mac_package = ""

        # discover whether this system can use - or is using - selinux
        # or if it should use apparmor (selinux takes precedence)
        for p in selinux_packages:
            if self.ph.check(p):
                self.selinux = True
                self.mac_package = p
                break
        if not self.selinux:
            for p in apparmor_packages:
                if self.ph.check(p):
                    self.apparmor = True
                    self.mac_package = p
                    break
        if not bool(self.selinux or self.apparmor):
            for p in selinux_packages:
                if self.ph.checkAvailable(p):
                    self.selinux = True
                    self.mac_package = p
                    break
            if not self.selinux:
                for p in apparmor_packages:
                    if self.ph.checkAvailable(p):
                        self.apparmor = True
                        self.mac_package = p
                        break

        self.compliant = True
        self.detailedresults = ""
        self.mode = str(self.modeci.getcurrvalue())

        try:

            if self.selinux:
                if not self.reportSelinux():
                    self.compliant = False
            elif self.apparmor:
                if not self.reportApparmor():
                    self.compliant = False
            else:
                self.detailedresults +=  "\nCould not find either selinux or apparmor installed and nother appears to be available to this system!"
                self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def set_selinux_conf_path(self):
        """

        :return:
        """

        # discover correct location of selinux config file
        self.selinux_config_file = "/etc/sysconfig/selinux"
        selinux_config_files = ["/etc/selinux/config", "/etc/sysconfig/selinux"]
        for p in selinux_config_files:
            if os.path.exists(p):
                self.selinux_config_file = p
                break

    def reportSelinux(self):
        """

        :return:
        """

        compliant = True
        conf_option_dict = {"selinux\s+status:\s+enabled": "status",
                            "current\s+mode:\s+" + self.mode: "mode"}

        # check if selinux is installed
        if not self.ph.check(self.mac_package):
            compliant = False
            self.detailedresults += "\nSELinux is not installed"

        # check sestatus for current configuration of selinux
        self.ch.executeCommand("/usr/sbin/sestatus")
        output = self.ch.getOutput()
        for co in conf_option_dict:
            rco = re.compile(co, re.I)
            if not list(filter(rco.match, output)):
                compliant = False
                self.detailedresults += "\nSELinux " + conf_option_dict[co] + " is not configured properly"

        self.set_selinux_conf_path()

        # check selinux config file for correct configuration so setting is
        # persistent after reboot
        selinux_tmp_file = self.selinux_config_file + ".stonixtmp"
        selinux_config_dict = {"SELINUX": self.mode}
        self.selinux_config_editor = KVEditorStonix(self.statechglogger, self.logger, "conf", self.selinux_config_file, selinux_tmp_file, selinux_config_dict, "present", "closedeq")
        self.selinux_config_editor.report()
        if self.selinux_config_editor.fixables:
            compliant = False
            self.detailedresults += "\nFollowing option(s) not configured correctly in " + self.selinux_config_file + " :\n" + "\n".join(self.selinux_config_editor.fixables)

        return compliant

    def reportApparmor(self):
        """

        :return:
        """

        compliant = True

        aa_enabled = "Yes"

        # check if apparmor is installed
        if not self.ph.check(self.mac_package):
            compliant = False
            self.detailedresults += "\nApparmor is not installed"

        # check if apparmor is enabled
        self.ch.executeCommand("/usr/bin/aa-enabled")
        output = self.ch.getOutputString()
        if not re.search(aa_enabled, output):
            compliant = False
            self.detailedresults += "\nApparmor is not enabled"

        # check if boot configuration for apparmor is correct
        f = open("/etc/default/grub", "r")
        contents = f.readlines()
        f.close()
        for line in contents:
            if re.search("GRUB_CMDLINE_LINUX_DEFAULT=", line):
                if not re.search("apparmor=1", line):
                    compliant = False
                    self.detailedresults += "\nApparmor not enabled in boot config"
                elif not re.search("security=apparmor", line):
                    compliant = False
                    self.detailedresults += "\nApparmor not enabled in boot config"
                break

        return compliant

    def fix(self):
        """

        :return: 
        """

        self.rulesuccess = True
        self.detailedresults = ""
        self.iditerator = 0

        try:

            if self.selinux:
                if not self.fixSelinux():
                    self.rulesuccess = False
            elif self.apparmor:
                if not self.fixApparmor():
                    self.rulesuccess = False
            else:
                self.rulesuccess = False
                self.detailedresults += "\nNeither SELinux nor Apparmor appears to be available to this system!"

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)

        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess

    def fixSelinux(self):
        """

        :return:
        """

        success = True
        self.iditerator = 0
        activate_utility = "/usr/sbin/selinux-activate"

        # install selinux package if it is not installed
        if not self.ph.check(self.mac_package):
            if not self.ph.install(self.mac_package):
                success = False
                self.detailedresults += "\nFailed to install selinux package"
            else:
                # if we just installed selinux, then we need to discover the correct conf path
                self.set_selinux_conf_path()

                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype":"pkghelper",
                         "pkgname":self.mac_package}
                self.statechglogger.recordchgevent(myid, event)

        if os.path.exists(activate_utility):
            self.ch.executeCommand(activate_utility)
            output = self.ch.getOutputString()
            if re.search("need to reboot", output, re.I):
                self.detailedresults += "\nSElinux has been configured, but you will need to reboot before selinux can become active. This rule will not report compliant until this is done."

        # set enforcement mode for selinux
        self.ch.executeCommand("/usr/sbin/setenforce " + self.mode)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            self.detailedresults += "\nFailed to set selinux mode to: " + self.mode

        self.iditerator += 1
        myid = iterate(self.iditerator, self.rulenumber)
        self.selinux_config_editor.setEventID(myid)
        # configure the selinux config file for persistence through reboot
        if not self.selinux_config_editor.fix():
            success = False
            self.detailedresults += "\nFailed to fix " + self.selinux_config_file
        elif not self.selinux_config_editor.commit():
            success = False
            self.detailedresults += "\nFailed to fix " + self.selinux_config_file

        return success

    def fixApparmor(self):
        """

        :return:
        """

        success = True
        profiles_dir = "/etc/apparmor.d/"
        valid_modes = ["complain", "enforce"]
        grub_file = "/etc/default/grub"
        tmp_grub_file = grub_file + ".stonixtmp"

        # install apparmor package if not installed
        if not self.ph.check(self.mac_package):
            if not self.ph.install(self.mac_package):
                success = False
                self.detailedresults += "\nFailed to install apparmor package"
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype":"pkghelper",
                         "pkgname":self.mac_package}
                self.statechglogger.recordchgevent(myid, event)
        if not self.ph.check("apparmor-profiles"):
            self.ph.install("apparmor-profiles")
        if not self.ph.check("apparmor-utils"):
            self.ph.install("apparmor-utils")

        # set apparmor enforcement mode
        if self.mode.lower() in ["complain", "permissive"]:
            self.ch.executeCommand("/usr/sbin/aa-complain " + profiles_dir + "*")
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                success = False
                self.detailedresults += "\nFailed to set apparmor profiles to complain mode"
        elif self.mode.lower() in ["enforce", "enforcing"]:
            if os.path.exists(profiles_dir):
                self.ch.executeCommand("/usr/sbin/aa-enforce " + profiles_dir + "*")
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    success = False
                    self.detailedresults += "\nFailed to set apparmor profiles to enforce mode"
            else:
                self.logger.log(LogPriority.DEBUG, "apparmor profiles directory does not exist")
                success = False
                self.detailedresults += "\nFailed to set apparmor mode to: " + self.mode
        else:
            success = False
            self.detailedresults += "\nPlease specify one of the following options in the MODE field:\n" + "\n".join(valid_modes)

        # correct apparmor boot config
        # (can't use kveditor because it can't handle appending to a config line)
        if os.path.exists(grub_file):
            f = open(grub_file, "r")
            contents = f.readlines()
            f.close()
            for n, i in enumerate(contents):
                if re.search("GRUB_CMDLINE_LINUX_DEFAULT=", i):
                    contents[n] = i.strip()[:-1]+' apparmor=1 security=apparmor"\n'
            tf = open(tmp_grub_file, "w")
            tf.writelines(contents)
            tf.close()

            self.iditerator += 1
            event = {"eventtype":"conf",
                     "filepath":grub_file}
            myid = iterate(self.iditerator, self.rulenumber)
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(grub_file, tmp_grub_file, myid)

            os.rename(tmp_grub_file, grub_file)

        # run update-grub to apply the new grub config
        self.ch.executeCommand("/usr/sbin/update-grub")

        return success
Ejemplo n.º 5
0
class AptGet(object):

    """Linux specific package manager for distributions that use the apt-get
    command to install packages.

    @author: Derek T Walker
    @change: 2012/08/06 dwalker - Original Implementation
    @change: 2015/08/20 eball - Added getPackageFromFile
    """

    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "sudo DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -y --force-yes install "
        self.remove = "/usr/bin/apt-get -y remove "

    ###############################################################################

    def installpackage(self, package):
        """Install a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be installed, must be
            recognizable to the underlying package manager.
        @return bool :
        @author dwalker"""
        try:
            self.ch.executeCommand(self.install + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg installed successfully"
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                return True
            else:
                # try to install for a second time
                self.ch.executeCommand(self.install + package)
                if self.ch.getReturnCode() == 0:
                    self.detailedresults = package + " pkg installed successfully"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return True
                else:
                    self.detailedresults = package + " pkg not able to install"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)

    ###############################################################################

    def removepackage(self, package):
        """Remove a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be removed, must be
            recognizable to the underlying package manager.
        @return bool :
        @author"""

        try:
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg removed successfully"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return True
            else:
                self.detailedresults = package + " pkg not able to be removed"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)

    ###############################################################################

    def checkInstall(self, package):
        """Check the installation status of a package. Return a bool; True if
        the package is installed.

        @param: string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: bool :
        @author: dwalker"""

        try:
            stringToMatch = "(.*)" + package + "(.*)"
            self.ch.executeCommand(["/usr/bin/dpkg", "-l", package])
            info = self.ch.getOutput()
            match = False
            for line in info:
                if search(stringToMatch, line):
                    parts = line.split()
                    if parts[0] == "ii":
                        match = True
                        break
                else:
                    continue
            if match:
                self.detailedresults = package + " pkg found and installed\n"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return True
            else:
                self.detailedresults = package + " pkg not installed\n"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)

    ###############################################################################

    def checkAvailable(self, package):
        try:
            found = False
            retval = call(["/usr/bin/apt-cache", "search", package], stdout=PIPE, stderr=PIPE, shell=False)
            if retval == 0:
                message = Popen(["/usr/bin/apt-cache", "search", package], stdout=PIPE, stderr=PIPE, shell=False)
                info = message.stdout.readlines()
                while message.poll() is None:
                    continue
                message.stdout.close()
                for line in info:
                    if search(package, line):
                        found = True
                if found:
                    self.detailedresults = package + " pkg is available"
                else:
                    self.detailedresults = package + " pkg is not available"
            else:
                self.detailedresults = (
                    package
                    + " pkg not found or may be \
misspelled"
                )
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise

    ###############################################################################

    def getPackageFromFile(self, filename):
        """Returns the name of the package that provides the given
        filename/path.

        @param: string filename : The name or path of the file to resolve
        @return: string name of package if found, None otherwise
        @author: Eric Ball
        """
        try:
            self.ch.executeCommand("dpkg -S " + filename)
            if self.ch.getReturnCode() == 0:
                output = self.ch.getOutputString()
                pkgname = output.split(":")[0]
                return pkgname
            else:
                return None
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)

    ###############################################################################

    def getInstall(self):
        return self.install

    ###############################################################################

    def getRemove(self):
        return self.remove
Ejemplo n.º 6
0
class AuditSSHKeys(Rule):
    """
    This class audits for password-less ssh keys on the system

    """

    def __init__(self, config, environ, logger, statechglogger):
        """
        private method to initialize module

        :param config: configuration object instance
        :param environ: environment object instance
        :param logger: logdispatcher object instance
        :param statechglogger: statechglogger object instance
        """

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.environ = environ
        self.rulenumber = 62
        self.rulename = 'AuditSSHKeys'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = ['LANL CAP', 'OpenSSH Security Best Practices']
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        datatype = 'bool'
        key = 'AUDITSSHKEYS'
        instructions = "To prevent this rule from modifying permissions on ssh keys, set the value of AUDITSSHKEYS to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.localize()

    def localize(self):
        """determine which OS the system is, and set
        certain variables accordingly

        """

        self.logger.log(LogPriority.DEBUG, "Running localize() ...")

        self.mac = False
        self.linux = False

        os = self.environ.getosfamily()
        if os == 'darwin':
            self.logger.log(LogPriority.DEBUG, "System OS type detected as: darwin")
            self.mac = True
        else:
            self.logger.log(LogPriority.DEBUG, "System OS type detected as: linux")
            self.linux = True

    def report(self):
        """check status of private ssh keys (whether they are encrypted with passwords or not)

        :returns: self.compliant - boolean; True if compliant, False if not compliant

        """

        searchterm = "Proc-Type:"
        self.searchdirs = []
        keylist = []
        self.keydict = {}
        self.compliant = True
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)

        try:

            self.logger.log(LogPriority.DEBUG, "Getting list of user home directories...")
            self.searchdirs = self.get_search_dirs()
            self.logger.log(LogPriority.DEBUG, "Getting list of ssh keys...")
            keylist = self.get_key_list(self.searchdirs)

            if keylist:
                self.logger.log(LogPriority.DEBUG, "Searching list of ssh keys...")
                for key in keylist:
                    self.keydict[key] = False
                    f = open(key, "r")
                    contentlines = f.readlines()
                    f.close()
                    for line in contentlines:
                        if re.search(searchterm, line):
                            self.keydict[key] = True

                for key in self.keydict:
                    if not self.keydict[key]:
                        self.compliant = False
                        self.detailedresults += "\nThe SSH key: " + str(key) + " was made without a password!"
                    if getOctalPerms(key) != 600:
                        self.compliant = False
                        self.detailedresults += "\nThe SSH key: " + str(key) + " has incorrect permissions"

                if self.compliant:
                    self.detailedresults += "\nAll SSH keys on this system are encrypted"

            else:
                self.detailedresults += "\nNo SSH keys were found on this system."

            if not self.compliant:
                self.detailedresults += "\n\nThis rule's fix only changes permissions on insecure keys. We cannot fix keys which were made without a password."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults = str(traceback.format_exc())
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        return self.compliant

    def get_key_list(self, searchdirs):
        """walk the ssh directory/ies and build and return a list of private keys (file names)

        :param searchdirs: list of directories to search for private ssh keys
        :returns: keylist - list; list of ssh key files

        """

        keylist = []

        if not searchdirs:
            self.logger.log(LogPriority.DEBUG, "Parameter searchdirs was empty! Returning empty keylist...")
            return keylist

        try:

            self.logger.log(LogPriority.DEBUG, "Building keylist...")
            for loc in searchdirs:
                files = glob(loc + "*")
                for f in files:
                    if os.path.isfile(f):
                        fh = open(f, "r")
                        contentlines = fh.readlines()
                        fh.close()
                        for line in contentlines:
                            if re.search("BEGIN\s+\w+\s+PRIVATE KEY", line):
                                keylist.append(f)
                                self.logger.log(LogPriority.DEBUG, "Adding SSH key file: " + str(f) + " to keylist...")

            self.logger.log(LogPriority.DEBUG, "Finished building keylist")

        except Exception:
            raise
        return keylist

    def get_search_dirs(self):
        """build and return a list of search directories to look for ssh keys

        :returns: searchdirs - list; directories to search for ssh keys in

        """

        searchdirs = []

        try:

            if self.mac:
                # the system is mac-based
                getuserscmd = "/usr/bin/dscl . -list /Users NFSHomeDirectory"
                self.ch.executeCommand(getuserscmd)
                retcode = self.ch.getReturnCode()
                if retcode == "0":
                    self.logger.log(LogPriority.DEBUG, "Command to get list of users' home directories ran successfully")
                    output = self.ch.getOutput()
                    self.logger.log(LogPriority.DEBUG, "Searching command output and building searchdirs list...")
                    for line in output:
                        sline = line.split()
                        if sline[1] not in ["/var/empty", "/dev/null"]:
                            if os.path.exists(sline[1] + "/.ssh/"):
                                searchdirs.append(sline[1] + "/.ssh/")
            else:
                # the system is linux-based
                # determine the start of the user id's on this system (500 or 1000)
                self.logger.log(LogPriority.DEBUG, "Setting default uidstart to 500...")
                uidstart = 500
                if os.path.exists('/etc/login.defs'):
                    self.logger.log(LogPriority.DEBUG, "login defs file exists. Getting actual uid start value...")
                    f = open('/etc/login.defs')
                    contentlines = f.readlines()
                    f.close()
                    for line in contentlines:
                        if re.search('^UID\_MIN\s+500', line, re.IGNORECASE):
                            uidstart = 500
                            self.logger.log(LogPriority.DEBUG, "Actual uid start value is 500")
                        if re.search('^UID\_MIN\s+1000', line, re.IGNORECASE):
                            uidstart = 1000
                            self.logger.log(LogPriority.DEBUG, "Actual uid start value is 1000")

                self.logger.log(LogPriority.DEBUG, "Building list of searchdirs...")
                # get list of user home directories from /etc/passwd
                f = open("/etc/passwd", "r")
                contentlines = f.readlines()
                f.close()
                for line in contentlines:
                    sline = line.split(":")
                    if len(sline) > 2:
                        if int(sline[2]) >= uidstart:
                            # build list of search directories based on home directories
                            if os.path.exists(sline[5] + "/.ssh/"):
                                searchdirs.append(sline[5] + "/.ssh/")
                                self.logger.log(LogPriority.DEBUG, "Adding directory: " + str(sline[5]) + "/.ssh/ to list of searchdirs...")

                # add the root ssh directory if it exists
                if os.path.exists("/root/.ssh/"):
                    searchdirs.append("/root/.ssh/")
                    self.logger.log(LogPriority.DEBUG, "Adding /root/.ssh/ to list of searchdirs...")

            self.logger.log(LogPriority.DEBUG, "Finished building searchdirs list")

        except Exception:
            raise
        return searchdirs

    def fix(self):
        """set permissions on all ssh keys to 0600 (384; -rw------)

        :returns: self.rulesuccess - boolean; True if fix operations succeeded, False if not

        """

        fixedkeys = []
        self.rulesuccess = True
        self.detailedresults = ""

        try:

            keylist = self.get_key_list(self.searchdirs)

            for key in keylist:
                self.logger.log(LogPriority.DEBUG, "Setting permissions on file: " + str(key) + " to 600...")
                os.chmod(key, 0o600)
                fixedkeys.append(key)

            if fixedkeys:
                self.detailedresults += "\nCorrected permissions on the following files:\n" + "\n".join(fixedkeys)
            else: self.detailedresults += "\nNo keys were modified."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults = str(traceback.format_exc())
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 7
0
class ConfigurePowerManagement(Rule):
    '''This Mac Only rule does the following:
    - Sets the Mac to stay awake if power is plugged-in.
    - Set the display to sleep after 30 minutes if inactivity (if plugged-in) or after 15 minutes of inactivity (if on laptop battery power)
    - Sets the hard drives to sleep after ten minutes of inactivity.
    - Sets the computer not to wake up if the computer is connected to a phone modem and the phone rings.
    - Sets the hard drives to sleep after ten minutes of inactivity.
    - Sets the computer not to wake up if the computer is connected to a phone modem and the phone rings.
    - Sets the Mac's wake-on-magic-ping option (Wake for Network Access or WakeOnLAN) off.
    - Disables the Start up automatically after a power failure feature in the Energy Saver System Pref. This feature is not available on all computers. This rule should be called disableAutoRestart, but it's not. Sorry.
    
    @author: ekkehard j. koch


    '''

###############################################################################

    def __init__(self, config, environ, logdispatcher, statechglogger):
        Rule.__init__(self, config, environ, logdispatcher,
                              statechglogger)
        self.rulenumber = 258
        self.rulename = 'ConfigurePowerManagement'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = []
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.psconfiguration = \
        {"ACDisableSystemSleep":
         {"HelpText": "Sets the Mac to stay awake if power is plugged-in. Default(AC Power, sleep, 0).",
          "PowerType": "AC Power",
          "PowerSetting": "sleep",
          "PowerSettingValue": 0,
          "PowerSettingMinimum": 0,
          "PowerSettingMaximum": 60},
         "ACDisplaySleep":
         {"HelpText": "Set Display Sleep  minutes on AC Power. Default(AC Power, displaysleep, 30).",
          "PowerType": "AC Power",
          "PowerSetting": "displaysleep",
          "PowerSettingValue": 30,
          "PowerSettingMinimum": 0,
          "PowerSettingMaximum": 60},
         "ACDiskSleep":
         {"HelpText": "Set Disk Sleep minutes on AC Power. Default(AC Power, disksleep, 10).",
          "PowerType": "AC Power",
          "PowerSetting": "disksleep",
          "PowerSettingValue": 10,
          "PowerSettingMinimum": 0,
          "PowerSettingMaximum": 60},
         "BatteryDisplaySleep":
         {"HelpText": "Set Display Sleep minutes on Battery Power. Default(Battery Power, displaysleep, 15).",
          "PowerType": "Battery Power",
          "PowerSetting": "displaysleep",
          "PowerSettingValue": 15,
          "PowerSettingMinimum": 0,
          "PowerSettingMaximum": 60},
         "BatteryDiskSleep":
         {"HelpText": "Set Disk Sleep minutes on Battery Power. Default(Battery Power, disksleep, 10).",
          "PowerType": "Battery Power",
          "PowerSetting": "disksleep",
          "PowerSettingValue": 10,
          "PowerSettingMinimum": 0,
          "PowerSettingMaximum": 60}
         }
        self.ci = {}
        index = -1
        for pslabel, psinfo in sorted(self.psconfiguration.items()):
            datatype = 'int'
            key = pslabel
            instructions = psinfo["HelpText"]
            defaultInteger = psinfo["PowerSettingValue"]
            index = index + 1
            self.ci[pslabel] = self.initCi(datatype, key, instructions, defaultInteger)
        self.pmset = "/usr/bin/pmset"
        self.psd = {}
        self.psACPowerAvailable = False
        self.psACBatteryPowerAvailable = False
        self.ch = CommandHelper(self.logdispatch)

    def report(self):
        '''Go through power settings dictionary and see if they match

        :param self: essential if you override this definition
        :returns: boolean - true if applicable false if not

        '''
        try:
            self.detailedresults = ""
            self.initializePowerSetting(True)
            self.compliant = True
            for pslabel, psinfo in sorted(self.psconfiguration.items()):
                powerType = psinfo["PowerType"]
                if (powerType == "AC Power" and self.psACPowerAvailable) or (powerType == "Battery Power" and self.psACBatteryPowerAvailable):
                    powerSetting = psinfo["PowerSetting"]
                    powerSettingValue = self.ci[pslabel].getcurrvalue()
                    powerSettingActual = self.getPowerSetting(powerType, powerSetting, False)
                    powerSettingActualValue = int(powerSettingActual)
                    powerSettingInfo = "(" + str(powerType) + ", " + str(powerSetting) + \
                    ", [desired, actual][" + str(powerSettingValue) + ", " + str(powerSettingActual) +"])"
                    if powerSettingValue == powerSettingActualValue:
                        self.resultAppend(pslabel + " is compliant. " + powerSettingInfo)
                    else:
                        self.resultAppend(pslabel + " is not compliant! " + powerSettingInfo)
                        self.compliant = False

            self.logdispatch.log(LogPriority.DEBUG, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
            " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

###############################################################################

    def fix(self):
        '''Go through power settings and fix the one we are supposed to

        :param self: essential if you override this definition
        :returns: boolean - true if applicable false if not

        '''
        try:
            self.detailedresults = ""
            success = True
            self.initializePowerSetting(True)
            for pslabel, psinfo in sorted(self.psconfiguration.items()):
                powerType = psinfo["PowerType"]
                if (powerType == "AC Power" and self.psACPowerAvailable) or (powerType == "Battery Power" and self.psACBatteryPowerAvailable):
                    powerSetting = psinfo["PowerSetting"]
                    powerSettingValue = self.ci[pslabel].getcurrvalue()
                    powerSettingActual = self.getPowerSetting(powerType, powerSetting, False)
                    powerSettingActualValue = int(powerSettingActual)
                    powerSettingInfo = "(" + str(powerType) + ", " + str(powerSetting) + \
                    ", [desired, actual][" + str(powerSettingValue) + ", " + str(powerSettingActual)+"])"
                    if powerSettingValue == powerSettingActualValue:
                        self.resultAppend(pslabel + " was correctly set. " + powerSettingInfo)
                    else:
                        newPowerSettingValue = self.setPowerSetting(powerType, powerSetting, powerSettingValue)
                        powerSettingActual = self.getPowerSetting(powerType, powerSetting, True)
                        powerSettingActualValue = int(powerSettingActual)
                        powerSettingInfo = "(" + str(powerType) + ", " + str(powerSetting) + \
                        ", [desired, actual][" + str(powerSettingValue) + ", " + str(powerSettingActual)+"])"
                        if powerSettingValue == powerSettingActualValue:
                            self.resultAppend(pslabel + " is now compliant! "  + powerSettingInfo)
                        else:
                            self.resultAppend(pslabel + " setting still not compliant! " + powerSettingInfo)
                            success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
            " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

###############################################################################

    def initializePowerSetting(self, forceUpdate=False):
        '''Initialize Power Setting Dictionary (psd) with output from pmset command

        :param self: essential if you override this definition
        :param forceUpdate: Force Dictionary Reset (Default value = False)
        :returns: boolean - true if applicable false if not

        '''
        success = True
        if forceUpdate:
            self.psd = {}
            self.psACPowerAvailable = False
            self.psACBatteryPowerAvailable = False
        if self.psd == {}:
            itemType = ""
            item = {}
            command = [self.pmset, "-g", "disk"]
            self.ch.executeCommand(command)
            output = self.ch.getOutput()
            for line in output:
                linestripped = line.strip()
                values = linestripped.split()
                if linestripped == "Battery Power:":
                    if not itemType == "":
                        self.psd[itemType] = item
                        item = {}
                    itemType = "Battery Power"
                    self.psACBatteryPowerAvailable = True
                elif linestripped == "AC Power:":
                    if not itemType == "":
                        self.psd[itemType] = item
                        item = {}
                    itemType = "AC Power"
                    self.psACPowerAvailable = True
                elif not itemType == "":
                    name = ""
                    for namepart in values[:-1]:
                        name = name + " " + str(namepart)
                    name = name.strip()
                    value = str(values[-1])
                    try:
                        item[name] = int(value)
                    except Exception:
                        item[name] = value
                    messagestring = "[" + str(itemType) + ", " + str(name) + "] = " + \
                    str(item[name])
                    self.logdispatch.log(LogPriority.DEBUG, messagestring)
            if not itemType == "":
                self.psd[itemType] = item
        return success

###############################################################################

    def getPowerSetting(self, powerType, powerSetting, forceUpdate=False):
        '''Get a power setting on a system
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param powerType: Like AC Power or Battery Power
        :param powerSetting: Like sleep or disksleep
        :param forceUpdate: Get new values from system if true (Default value = False)
        :returns: boolean - true
        @note: None

        '''
        try:
            self.initializePowerSetting(forceUpdate)
            powerSettingValueString = self.psd[powerType][powerSetting]
            if powerSettingValueString == "":
                powerSettingValue = 0
            else:
                powerSettingValue = int(powerSettingValueString)
            messagestring = "[" + str(powerType) + ", " + str(powerSetting) + \
            "] = " + str(powerSettingValue)
            self.logdispatch.log(LogPriority.DEBUG, messagestring)
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            powerSettingValue = 0
        return powerSettingValue

###############################################################################

    def setPowerSetting(self, powerType, powerSetting, powerSettingValue):
        '''Set a power setting on a system
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param powerType: Like AC Power or Battery Power
        :param powerSetting: Like sleep or disksleep
        :param powerSettingValue: Value to set setting to
        :returns: boolean - true
        @note: None

        '''
        success = False
        if not powerSettingValue == self.getPowerSetting(powerType, powerSetting):
            if powerType == "Battery Power":
                command = [self.pmset, "-b", powerSetting, str(powerSettingValue)]
                self.ch.executeCommand(command)
            elif powerType == "AC Power":
                command = [self.pmset, "-c", powerSetting, str(powerSettingValue)]
                self.ch.executeCommand(command)
            if powerSettingValue == self.getPowerSetting(powerType, powerSetting, True):
                success = True
        else:
            success = True
        return success

###############################################################################

    def resultAppend(self, pMessage=""):
        '''reset the current kveditor values to their defaults.
        @author: ekkehard j. koch

        :param self: essential if you override this definition
        :param pMessage:  (Default value = "")
        :returns: boolean - true
        @note: kveditorName is essential

        '''
        datatype = type(pMessage)
        if datatype == str:
            if not (pMessage == ""):
                messagestring = pMessage
                if (self.detailedresults == ""):
                    self.detailedresults = messagestring
                else:
                    self.detailedresults = self.detailedresults + "\n" + \
                    messagestring
        elif datatype == list:
            if not (pMessage == []):
                for item in pMessage:
                    messagestring = item
                    if (self.detailedresults == ""):
                        self.detailedresults = messagestring
                    else:
                        self.detailedresults = self.detailedresults + "\n" + \
                        messagestring
        else:
            raise TypeError("pMessage with value" + str(pMessage) + \
                            "is of type " + str(datatype) + " not of " + \
                            "type " + str(str) + \
                            " or type " + str(list) + \
                            " as expected!")
Ejemplo n.º 8
0
class DisableFTP(Rule):

    def __init__(self, config, environ, logdispatcher, statechglogger):
        Rule.__init__(self, config, environ, logdispatcher,
                              statechglogger)
        self.rulenumber = 266
        self.rulename = 'DisableFTP'
        self.logger = logdispatcher
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.helptext = "This rule disables FTP services for the Mac"
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}

        # init CIs
        datatype = 'bool'
        key = 'DISABLEFTP'
        instructions = "To prevent DisableFTP from being disabled, set " + \
            "the value of DISABLEFTP to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
    
    def report(self):
        try:
            self.detailedresults = ""
            compliant = True
            cmd = ["/bin/launchctl", "list"]
            self.ch = CommandHelper(self.logger)
            if self.ch.executeCommand(cmd):
                output = self.ch.getOutput()
                for line in output:
                    if re.search("com\.apple\.ftpd", line):
                        self.detailedresults += "FTP is running and it shouldn't\n"
                        compliant = False
                        break
            else:
                self.detailedresults += "Unable to list running services\n"
                compliant = False
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant
    
    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            self.detailedresults = ""
    
            #clear out event history so only the latest fix is recorded
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            cmd = ["/bin/launchctl", "disable", "system/com.apple.ftpd"]
            self.ch.executeCommand(cmd)
            cmd = ["/bin/launchctl", "unload",
                   "/System/Library/LaunchDaemons/ftp.plist"]
            if not self.ch.executeCommand(cmd):
                success = False
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                cmd = ["/bin/launchctl", "enable", "system/com.apple.ftpd"]
                event = {"eventtype": "comm",
                         "command": cmd}
                self.statechglogger.recordchgevent(myid, event)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                cmd = ["/bin/launchctl", "load", "-w",
                       "/System/Library/LaunchDaemons/ftp.plist"]
                event = {"eventtype": "comm",
                         "command": cmd}
                self.statechglogger.recordchgevent(myid, event)
            self.rulesuccess = success
                
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", success,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
class DisableSIRIandContinuityFeatures(Rule):
    def __init__(self, config, environ, logdispatch, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logdispatch, statechglogger)

        self.logger = logdispatch
        self.rulenumber = 310
        self.rulename = "DisableSIRIandContinuityFeatures"
        self.formatDetailedResults("initialize")
        self.rootrequired = True
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']},
                           'fisma': 'low'}
        datatype = "bool"
        key = "SIRICONTINUITY"
        instructions = "To disable this rule set the value of " + \
            "SIRICONTINUITY to False"
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        
        datatype = "string"
        key = "MACHOMEDIR"
        instructions = "Enter the current user's home directory here " + \
            " which is usually in the location of: /Users/username\n" + \
            "If left blank, we will try to retrieve the home directory " + \
            "inside the rule\n"
        default = ""
        self.homeci = self.initCi(datatype, key, instructions, default)
        '''Directory location for testing only'''
#         self.profile = "/Users/username/stonix/src/" + \
#                        "stonix_resources/files/" + \
#                        "stonix4macRestrictionsiCloudDictationSpeech.mobileconfig"
        self.profile = "/Applications/stonix4mac.app/Contents/" + \
                       "Resources/stonix.app/Contents/MacOS/" + \
                       "stonix_resources/files/" + \
                       "stonix4macRestrictionsiCloudDictationSpeech.mobileconfig"
        self.identifier = "097AD858-A863-4130-989F-D87CCE7E393A"
        self.home = ""
        self.ch = CommandHelper(self.logger)
        self.siripath1 = "/Library/Containers/com.apple.SiriNCService" + \
            "/Data/Library/Preferences/com.apple.Siri.plist" 
        self.siriparams1 = "StatusMenuVisible"
        self.siripath2 = "/Library/Preferences/com.apple.assistant.support" + \
            ".plist"
        self.siriparams2 = "Assistant\ Enabled"
        self.sethelptext()

    def setupHomeDir(self):
        home = ""
        cmd = "/bin/echo $HOME"
        if self.ch.executeCommand(cmd):
            output = self.ch.getOutputString()
            if output:
                home = output.strip()
        return home
                    
    def report(self):
        try:
            self.detailedresults = ""
            self.defaults1 = True
            self.defaults2 = True
            self.profilecomp = True
            compliant = True
            if not self.homeci.getcurrvalue():
                self.home = self.setupHomeDir()
            if self.home:
                if os.path.exists(self.home):
                    if os.path.exists(self.home + self.siripath1):
                        cmd = "/usr/bin/defaults read " + \
                               self.home + self.siripath1 + " " + \
                               self.siriparams1
                        if self.ch.executeCommand(cmd):
                            output = self.ch.getOutputString().strip()
                            if output != "1":
                                self.undo1 = output
                                self.detailedresults += "Didn't get the " + \
                                    "desired results for StatusMenuVisible\n"
                                self.defaults1 = False
                        else:
                            self.detailedresults += "Unable to run defaults " + \
                                "read command on " + self.siripath1 + "\n"
                            self.defaults1 = False
                    if os.path.exists(self.home + self.siripath2):
                        cmd = "/usr/bin/defaults read " + \
                               self.home + self.siripath2 + " " + \
                               self.siriparams2
                        if self.ch.executeCommand(cmd):
                            output = self.ch.getOutputString().strip()
                            if output != "0":
                                self.undo2 = output
                                self.detailedresults += "Didn't get the " + \
                                    "desired results for " + \
                                    "Assistant Enabled\n"
                                self.defaults2 = False
                        else:
                            self.detailedresults += "Unable to run defaults " + \
                                "read command on " + self.siripath2 + "\n"
                            self.defaults2 = False
                else:
                    self.detailedresults += "Home directory entered does not exist\n"
                    compliant = False
            else:
                self.detailedresults += "Unable to retrieve your home directory\n"
                compliant = False

            found = False
            cmd = ["/usr/bin/profiles", "-P"]
            if not self.ch.executeCommand(cmd):
                self.detailedresults += "Unable to run profiles command\n"
            else:
                output = self.ch.getOutput()
                if output:
                    for line in output:
                        if search("^There are no configuration profiles installed", line.strip()):
                            self.detailedresults += "There are no configuration profiles installed\n"
                            break
                        elif search(escape(self.identifier) + "$", line.strip()):
                            found = True
                            break
            if not found:
                self.detailedresults += "All desired profiles aren't isntalled\n"
                self.profilecomp = False
            self.compliant = self.defaults1 & self.defaults2 & \
            self.profilecomp & compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            self.detailedresults = ""
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            if not self.defaults1:
                cmd = ["/usr/bin/defaults", "write", self.home + self.siripath1,
                       self.siriparams1, "-bool", "yes"]
                if self.ch.executeCommand(cmd):
                    if self.ch.getReturnCode() != 0:
                        success = False
                    else:
                        undocmd = ["/usr/bin/defaults", "write", self.home + \
                                   self.siripath1, self.siriparams1, self.undo1]
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "comm",
                                 "command": undocmd}
                        self.statechglogger.recordchgevent(myid, event)
            if not self.defaults2:
                cmd = "/usr/bin/defaults write " + self.home + \
                self.siripath2 + " " + self.siriparams2 + " -bool no"
                if self.ch.executeCommand(cmd):
                    if self.ch.getReturnCode() != 0:
                        success = False
                    else:
                        undocmd = "/usr/bin/defaults write " + self.home + \
                            self.siripath2 + " " + self.siriparams2 + \
                            " " + self.undo2
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "commandstring",
                                 "command": undocmd}
                        self.statechglogger.recordchgevent(myid, event)
            if not self.profilecomp:
                if os.path.exists(self.profile):
                    cmd = ["/usr/bin/profiles", "-I", "-F", self.profile]
                    if not self.ch.executeCommand(cmd):
                        success = False
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        cmd = ["/usr/bin/profiles", "-R", "-p", self.identifier]
                        event = {"eventtype": "comm",
                                 "command": cmd}
                        self.statechglogger.recordchgevent(myid, event)
                else:
                    success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 10
0
class InstallBanners(RuleKVEditor):
    '''Install and configure warning banners, to be displayed at startup.'''
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """

        RuleKVEditor.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 51
        self.rulename = 'InstallBanners'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.compliant = False
        self.guidance = [
            'CIS', 'NSA 2.3.7.2', 'CCE 4188-9', 'CCE 4431-3', 'CCE 3717-6',
            'CCE 4554-2', 'CCE 4603-7', 'CCE 4760-5', 'CCE 4301-8',
            'CCE 4698-7', 'CCE 4222-6', 'CCE 4103-8', 'CCE 4870-2',
            'CCE 4896-7'
        ]
        self.iditerator = 0
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        # init CIs
        datatype = 'bool'
        key = 'INSTALLBANNERS'
        instructions = "To prevent the installation of warning banners, " + \
            "set the value of InstallBanners to False.\n\n!DEBIAN USERS! Due to " + \
            "a bug in gdm3 (gnome 3) which has not been patched on debian, we are forced to " + \
            "change the user's display manager to something else in order to be compliant " + \
            "with the login banner requirement. As a result, the login banner changes will " + \
            "only take effect after a system reboot."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        # Initial setup and deterministic resolution of variables
        constlist = [
            WARNINGBANNER, GDMWARNINGBANNER, GDM3WARNINGBANNER,
            ALTWARNINGBANNER, OSXSHORTWARNINGBANNER
        ]
        if not self.checkConsts(constlist):
            self.applicable = {'type': 'white', 'family': []}
            return

        self.initobjs()
        self.setcommon()
        self.islinux()
        self.ismac()

    def initobjs(self):
        ''' '''

        try:

            self.ph = Pkghelper(self.logger, self.environ)
            self.ch = CommandHelper(self.logger)
            self.linux = False
            self.mac = False
            self.gnome2 = False
            self.gnome3 = False
            self.kde = False
            self.lightdm = False
            self.badline = False

        except Exception:
            raise

    def setgnome2(self):
        '''set up all variables for use with gnome2-based systems
        
        @author: Breen Malmberg


        '''

        self.gnome2 = True
        self.gnome2bannertext = GDMWARNINGBANNER

    def forcelightdm(self):
        '''force debian systems using gdm3 to
        change to the lightdm display manager
        in order to comply with login/display
        banner requirements


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        self.logger.log(LogPriority.DEBUG,
                        "Forcing display manager to be lightdm...")

        if self.gnome2:
            cmd = '/usr/sbin/dpkg-reconfigure --frontend=noninteractive gdm'
        elif self.gnome3:
            cmd = '/usr/sbin/dpkg-reconfigure --frontend=noninteractive gdm3'
        else:
            cmd = '/usr/sbin/dpkg-reconfigure --frontend=noninteractive lightdm'

        cmd2 = "/usr/sbin/dpkg --configure lightdm"

        self.lightdm = True
        path = '/etc/X11/default-display-manager'
        opt = '/usr/sbin/lightdm\n'
        mode = 0o644
        uid = 0
        gid = 0

        try:

            if not self.setFileContents(path, opt, [uid, gid, mode]):
                retval = False

            if not self.ph.check('lightdm'):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Package: lightdm not installed yet. Installing lightdm..."
                )
                if not self.ph.install('lightdm'):
                    retval = False
                    self.detailedresults += "\nFailed to install lightdm"

            if not self.ch.executeCommand(cmd):
                retval = False
                self.detailedresults += "\nFailed to set lightdm as default display manager"

            self.ch.executeCommand(cmd2)

            if retval:
                self.detailedresults += "\nThe display manager has been changed to lightdm. These changes will only take effect once the system has been restarted."
            else:
                self.detailedresults += "\nFailed to change default display manager to lightdm. Login banner may not display."

        except Exception:
            raise
        return retval

    def setgnome3(self):
        '''set up all variables for use with gnome3-based systems
        
        @author: Breen Malmberg


        '''

        self.gnome3 = True
        self.gnome3bannertext = GDM3WARNINGBANNER

    def setkde(self):
        '''set up all variables for use with kde-based systems
        
        @author: Breen Malmberg


        '''

        self.kde = True
        self.kdebannertext = ALTWARNINGBANNER
        self.kdelocs = [
            '/etc/kde/kdm/kdmrc', '/etc/kde3/kdm/kdmrc', '/etc/kde4/kdm/kdmrc',
            '/usr/share/config/kdm/kdmrc', '/usr/share/kde4/config/kdm/kdmrc'
        ]
        self.kdefile = '/usr/share/kde4/config/kdm/kdmrc'
        for loc in self.kdelocs:
            if os.path.exists(loc):
                self.kdefile = str(loc)
        tmpfile = self.kdefile + '.stonixtmp'

        key1 = 'GreetString'
        val1 = '"' + str(self.kdebannertext) + '"'
        self.greetbanner = [key1, val1]
        key2 = 'UserList'
        val2 = 'false'
        key3 = 'UseTheme'
        val3 = 'false'
        key4 = 'PreselectUser'
        val4 = 'None'
        key5 = 'LogoArea'
        val5 = 'None'
        key6 = 'GreeterPos'
        val6 = '45,45'
        key7 = 'AntiAliasing'
        val7 = 'false'
        key8 = 'SortUsers'
        val8 = 'false'
        key9 = 'GreetFont'
        val9 = 'Serif,20,-1,5,50,0,0,0,0,0'
        key10 = 'FailFont'
        val10 = 'Sans Serif,10,-1,5,75,0,0,0,0,0'
        bkey1 = 'PreselectUser'
        bval1 = 'None'
        self.kdedict = {
            "X-*-Greeter": {
                key2: val2,
                key3: val3,
                key4: val4,
                key5: val5,
                key6: val6,
                key7: val7,
                key8: val8,
                key9: val9,
                key10: val10
            },
            "X-:*-Greeter": {
                bkey1: bval1
            }
        }

        self.kdeditor = KVEditorStonix(self.statechglogger, self.logger,
                                       "tagconf", self.kdefile, tmpfile,
                                       self.kdedict, "present", "closedeq")

    def setlightdm(self):
        '''set up all variables for use with lightdm-based systems
        
        @author: Breen Malmberg


        '''

        self.lightdm = True
        self.ldmbannertext = ALTWARNINGBANNER
        key1 = '/usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf'
        val1 = [
            '[SeatDefaults]', 'allow-guest=false', 'greeter-hide-users=true',
            'greeter-show-manual-login=true', 'autologin-user='******'/etc/lightdm/lightdm.conf.d/stonixlightdm.conf'
        val3 = [
            '[SeatDefaults]',
            'greeter-setup-script=/bin/sh -c "until /usr/bin/zenity ' +
            '--width=600 --height=400 --title=WARNING --text-info ' +
            '--filename=' + str(self.loginbannerfile) + '; do :; done"'
        ]
        self.lightdmdict = {key1: val1, key2: val2, key3: val3}

    def setlinuxcommon(self):
        '''set up all variables for use with linux-based systems
        
        @author: Breen Malmberg


        '''

        if not self.sshdfile:
            self.sshdfile = '/etc/ssh/sshd_config'
        self.bannerfiles = [
            "/etc/banners/in.ftpd", "/etc/banners/in.rlogind",
            "/etc/banners/in.rshd", "/etc/banners/in.telnetd", "/etc/banner"
        ]

    def setcommon(self):
        '''set up all variables for use with all systems
        
        @author: Breen Malmberg


        '''

        self.loginbannerfile = "/etc/issue"
        self.sshbannerfile = "/etc/banner"

        self.sshdfile = ""
        self.sshdlocs = [
            "/etc/sshd_config", "/etc/ssh/sshd_config",
            "/private/etc/ssh/sshd_config", "/private/etc/sshd_config"
        ]
        for loc in self.sshdlocs:
            if os.path.exists(loc):
                self.sshdfile = str(loc)

        self.sshbannerdict = {"Banner": self.sshbannerfile}

    def setmac(self):
        '''set up all variables for use with darwin-based systems
        
        @author: Breen Malmberg


        '''

        self.mac = True
        self.bannertext = WARNINGBANNER

        if not self.sshdfile:
            self.sshdfile = '/private/etc/sshd_config'
        self.ftpwelcomelocs = ["/etc/ftpwelcome", "/private/etc/ftpwelcome"]
        self.ftpwelcomefile = '/private/etc/ftpwelcome'
        for loc in self.ftpwelcomelocs:
            if os.path.exists(loc):
                self.ftpwelcomefile = str(loc)
        self.policybanner = "/Library/Security/PolicyBanner.txt"

        self.addKVEditor(
            "FileServerBannerText", "defaults",
            "/Library/Preferences/com.apple.AppleFileServer", "", {
                "loginGreeting":
                [re.escape(WARNINGBANNER), "'\"" + WARNINGBANNER + "\"'"]
            }, "present", "",
            "To prevent the installation of a warning banner," +
            " set the value of InstallBanners to False", self.ci)
        self.addKVEditor(
            "loginwindowBannerText", "defaults",
            "/Library/Preferences/com.apple.loginwindow", "", {
                "LoginwindowText": [
                    re.escape(OSXSHORTWARNINGBANNER),
                    '"' + OSXSHORTWARNINGBANNER + '"'
                ]
            }, "present", "",
            "To prevent the installation of a warning banner," +
            " set the value of InstallBanners to False", self.ci)

    def ismac(self):
        '''determine whether the current system is macintosh, or darwin-based
        
        @author: Breen Malmberg


        '''

        try:
            if self.environ.getosfamily() == 'darwin':
                self.setmac()
                self.logger.log(
                    LogPriority.DEBUG,
                    "System is Mac OS. Configuring Mac banners...")
        except Exception:
            raise

    def islinux(self):
        '''determine whether the current system is linux-based, and set all
        distro-specific variables
        
        @author: Breen Malmberg


        '''

        try:

            if self.environ.getosfamily() == 'linux':
                self.linux = True

                self.setlinuxcommon()

                if os.path.exists("/usr/sbin/gdm3"):
                    self.setgnome3()
                elif os.path.exists("/usr/sbin/gdm"):
                    self.ch.executeCommand("/usr/sbin/gdm --version")
                    gnomeversionstring = self.ch.getOutputString()
                    if re.search("GDM\s+3\.", gnomeversionstring,
                                 re.IGNORECASE):
                        self.setgnome3()
                    else:
                        self.setgnome2()
                if os.path.exists("/usr/sbin/lightdm"):
                    self.setlightdm()
                if os.path.exists("/usr/bin/startkde"):
                    self.setkde()

        except Exception:
            raise

    def getFileContents(self, filepath, returntype='list'):
        '''Retrieve and return file contents (in list format) of a given file path

        :param filepath: string full path to file to read
        :param returntype: string valid values: 'list', 'string' (Default value = 'list')
        :returns: filecontents
        :rtype: list
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG,
                        "Retrieving contents of file " + filepath)

        try:
            if returntype == 'list':
                filecontents = []
            elif returntype == 'string':
                filecontents = ''
            else:
                filecontents = ''
                self.detailedresults += "\nReturntype parameter must be " + \
                    "either 'list' or 'string!'"
            if os.path.exists(filepath):
                f = open(filepath, 'r')
                if returntype == 'list':
                    filecontents = f.readlines()
                elif returntype == 'string':
                    filecontents = f.read()
                f.close()
            else:
                self.detailedresults += '\nCould not find specified file: ' + \
                    str(filepath) + '. Returning empty value...'
        except Exception:
            raise
        return filecontents

    def setFileContents(self, filepath, contents, perms=[0, 0, 0o644]):
        '''write (or append) specified contents to specified file

        :param filepath: string full path to file
        :param contents: list/string object to write to file (can be either
            list or string)
        :param mode: string indicates the IO method to use to open the file.
            Valid values are 'w' for write, and 'a' for append
        :param perms: integer-list of size 3. first index/position in list
            indicates octal permissions flag; second indicates uid; third
            indicates gid (Default value = [0)
        :param 0: 
        :param 0644]: 
        :returns: retval
        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True
        tmpPath = filepath + ".stonixtmp"

        self.logger.log(LogPriority.DEBUG,
                        "Writing changes to file " + filepath)

        try:

            if os.path.exists(filepath):
                f = open(tmpPath, "w")
                if isinstance(contents, list):
                    f.writelines(contents)
                else:
                    f.write(contents)
                f.close()

                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {'eventtype': 'conf', 'filepath': filepath}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange(filepath, tmpPath, myid)
                os.rename(tmpPath, filepath)
                os.chmod(filepath, perms[2])
                os.chown(filepath, perms[0], perms[1])
            else:
                # get the parent directory of filepath
                basepath = ("/").join(filepath.split("/")[:-1])
                # create parent directory if it doesn't exist
                if not os.path.exists(basepath):
                    os.makedirs(basepath, 0o755)
                # then create the file
                f = open(filepath, "w")
                if isinstance(contents, list):
                    f.writelines(contents)
                else:
                    f.write(contents)
                f.close()

                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {'eventtype': 'creation', 'filepath': filepath}
                self.statechglogger.recordchgevent(myid, event)
                os.chmod(filepath, perms[2])
                os.chown(filepath, perms[0], perms[1])

            if not fixInflation(filepath, self.logger, perms[2],
                                [perms[0], perms[1]]):
                retval = False

        except Exception:
            retval = False
            raise
        return retval

    def replaceFileContents(self, filepath, contentdict):
        '''replace key from contentdict with value from contentdict, in filepath

        :param filepath: string full path to file
        :param contentdict: dictionary of strings
        :returns: retval
        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True
        appends = []

        self.logger.log(LogPriority.DEBUG,
                        "Replacing any existing, incorrect configurations...")

        try:

            contentlines = self.getFileContents(filepath)
            for line in contentlines:
                for key in contentdict:
                    if any((re.search("\*", key), re.search("\*", line))):
                        if re.search("^" + re.escape(key), re.escape(line)):
                            contentlines = [
                                c.replace(line, key + contentdict[key])
                                for c in contentlines
                            ]
                    else:
                        if re.search("^" + key, line):
                            contentlines = [
                                c.replace(line, key + contentdict[key])
                                for c in contentlines
                            ]

            for key in contentdict:
                if key.strip() not in contentlines:
                    appends.append(key + contentdict[key])
            self.appendFileContents(filepath, contentlines, appends)

        except Exception:
            self.rulesuccess = False
            raise
        return retval

    def appendFileContents(self, filepath, contents, appends):
        '''

        :param filepath: 
        :param contents: 
        :param appends: 

        '''

        self.logger.log(LogPriority.DEBUG,
                        "Appending missing configuration lines...")

        for a in appends:
            contents.append(a + "\n")

        self.setFileContents(filepath, contents)

    def reportFileContents(self, filepath, searchparams):
        '''verify that the give key:value pairs in contentdict exist in filepath

        :param filepath: string full path to file
        :param searchparams: list or string to search in file contents
        :returns: retval
        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True
        foundDict = {}
        findresult = 999
        foundstring = False

        try:

            if not os.path.exists(filepath):
                retval = False
                self.detailedresults += "\nRequired configuration file " + filepath + " does not exist"
                return retval

            if isinstance(searchparams, str):
                contents = self.getFileContents(filepath, "string")
                findresult = contents.find(searchparams)
                if findresult != -1:
                    foundstring = True
                if not foundstring:
                    retval = False
                    self.logger.log(
                        LogPriority.DEBUG,
                        "str.find() return code = " + str(findresult))
                    self.detailedresults += "\nSearch parameter:\n" + searchparams + "\nis missing from " + filepath
            elif isinstance(searchparams, list):
                for p in searchparams:
                    foundDict[p] = False
                contentlines = self.getFileContents(filepath)
                for line in contentlines:
                    for p in searchparams:
                        if line.find(p) != -1:
                            foundDict[p] = True
                for i in foundDict:
                    if not foundDict[i]:
                        retval = False
                        self.detailedresults += "\nSearch parameter:\n" + i + "\nis missing from " + filepath
        except Exception:
            raise
        return retval

    def checkCommand(self, cmd, val="", regex=True):
        '''check the output of a given command to see if it matches given value

        :param cmd: 
        :param val:  (Default value = "")
        :param regex:  (Default value = True)
        :returns: retval
        :rtype: boolean

@author: Breen Malmberg

        '''

        retval = True

        try:

            self.ch.executeCommand(cmd)
            retcode = self.ch.getReturnCode()
            # gconftool-2 returns either 0 or -1 on error
            if re.search("gconftool-2", cmd):
                if retcode == -1:
                    retval = False
                    errstr = self.ch.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
                    self.detailedresults += "\nCommand:\n" + cmd + "\nFailed"
            else:
                # all other programs return 0 on success
                if retcode != 0:
                    retval = False
                    errstr = self.ch.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
            if val:
                outputstr = self.ch.getOutputString()
                if not regex:
                    # outputstr = outputstr.strip()
                    # val = val.strip()
                    if outputstr.find(val) == -1:
                        retval = False
                        self.detailedresults += "\nDesired output:\n"
                        self.detailedresults += val
                        self.detailedresults += "\n\nActual output:\n"
                        self.detailedresults += outputstr
                else:
                    if not re.search(val, outputstr, re.IGNORECASE):
                        retval = False
                        self.logger.log(
                            LogPriority.DEBUG,
                            "Could not find search regex:\n" + val +
                            "\nin command output")

        except Exception:
            raise
        return retval

    def report(self):
        '''The report method examines the current configuration and determines
        whether or not it is correct. If the config is correct then the
        self.compliant, self.detailed results and self.currstate properties are
        updated to reflect the system status. self.rulesuccess will be updated
        if the rule does not succeed.


        :returns: self.compliant

        :rtype: boolean

@author Breen Malmberg

        '''

        self.compliant = True
        self.detailedresults = ""

        try:

            if self.linux:
                if not self.reportlinux():
                    self.compliant = False
            elif self.mac:
                if not self.reportmac():
                    self.compliant = False
            else:
                self.compliant = False
                self.detailedresults += '\nCould not identify operating system, or operating system not supported.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def getgnome3version(self):
        '''get the gnome3 version as a float


        :returns: g3ver

        :rtype: float
@author: Breen Malmberg

        '''

        g3ver = 0.0
        cmd = '/usr/sbin/gdm3 --version'
        backupcmd = '/usr/bin/gnome-session --version'
        if not any((self.checkCommand(cmd), self.checkCommand(backupcmd))):
            return g3ver
        else:
            outlines = self.ch.getOutput()
            for line in outlines:
                sline = line.split()
                if len(sline) > 1:
                    splitver = sline[1].split('.')
                    combinedver = splitver[0] + '.' + splitver[1]
                    g3ver = float(combinedver)
                else:
                    try:
                        g3ver = float(sline[0])
                    except Exception:
                        return g3ver
        return g3ver

    def reportlinux(self):
        '''run report functionality for linux-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

            if all((self.gnome3,
                    re.search('debian', self.environ.getostype(),
                              re.IGNORECASE))):
                if self.getgnome3version() < 3.15:
                    self.gnome3 = False
                    self.forcelightdm()
                    self.setlightdm()

            if not self.reportlinuxcommon():
                compliant = False
            if self.gnome2:
                if not self.reportgnome2():
                    compliant = False
            elif self.gnome3:
                if not self.reportgnome3():
                    compliant = False
            if self.lightdm:
                if not self.reportlightdm():
                    compliant = False
            if self.kde:
                if not self.reportkde():
                    compliant = False

        except Exception:
            raise
        return compliant

    def reportlinuxcommon(self):
        '''run report functionality which is common to linux platforms


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

        compliant = True

        try:

            for f in self.bannerfiles:
                if not self.reportFileContents(f, WARNINGBANNER):
                    compliant = False

            if not self.reportcommon():
                compliant = False

        except Exception:
            raise
        return compliant

    def reportcommon(self):
        '''run report functionality which is common to all platforms


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

        compliant = True
        if not os.path.exists(self.sshdfile):
            self.detailedresults += "Required configuration file " + self.ssdhfile + \
                                    " does not exist.\n"
            return False
        contents = readFile(self.sshdfile, self.logger)
        self.commoneditor = KVEditorStonix(self.statechglogger, self.logger,
                                           "conf", self.sshdfile,
                                           self.sshdfile + ".tmp",
                                           self.sshbannerdict, "present",
                                           "space")
        if not self.commoneditor.report():
            compliant = False
            self.detailedresults += self.sshdfile + " doesn't contain correct contents\n"
        return compliant

    def reportgnome2(self):
        '''run report functionality for gnome2-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

            gconf = "/usr/bin/gconftool-2"
            confDict = {
                "/apps/gdm/simple-greeter/disable_user_list": "true",
                "/apps/gdm/simple-greeter/banner_message_enable": "true",
                "/apps/gdm/simple-greeter/banner_message_text":
                ALTWARNINGBANNER
            }  # gdm 2 db cannot reliably store/preserve/interpret newline characters
            gconfcommands = []
            for item in confDict:
                gconfcommands.append(gconf + " -g " + item)

            for gcmd in gconfcommands:
                if not self.checkCommand(gcmd, confDict[gcmd.split()[2]],
                                         False):
                    compliant = False

        except Exception:
            raise
        return compliant

    def reportgnome3(self):
        '''run report functionality for gnome3-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

            if not self.checkprofilecfg():
                compliant = False
            if not self.checkkeyfilecfg():
                compliant = False

        except Exception:
            raise
        return compliant

    def checkprofilecfg(self):
        ''' '''

        compliant = True

        profilefile = "/etc/dconf/profile/gdm"
        profileoptslist = ["user-db:user", "system-db:gdm"]
        if not self.reportFileContents(profilefile, profileoptslist):
            compliant = False

        return compliant

    def checkkeyfilecfg(self):
        ''' '''

        compliant = True

        keyfile = "/etc/dconf/db/gdm.d/01-banner-message"
        keyfileoptslist = [
            "[org/gnome/login-screen]", "banner-message-enable=true",
            "banner-message-text='" + GDM3WARNINGBANNER + "'",
            "disable-user-list=true"
        ]

        if not self.reportFileContents(keyfile, keyfileoptslist):
            compliant = False

        return compliant

    def reportlightdm(self):
        '''run report functionality for lightdm-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

            for item in self.lightdmdict:
                if not self.reportFileContents(item, self.lightdmdict[item]):
                    compliant = False
                    self.detailedresults += '\nRequired configuration text not found in: ' + str(
                        item)

        except Exception:
            raise
        return compliant

    def reportkde(self):
        '''run report functionality for kde-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

        try:

            if not os.path.exists(self.kdefile):
                retval = False
                self.detailedresults += 'Required configuration file: ' + str(
                    self.kdefile) + ' not found'
            else:
                if not self.kdeditor.report():
                    retval = False
                    if self.kdeditor.fixables:
                        self.detailedresults += '\nThe following required options are missing from ' + \
                            str(self.kdefile) + ':\n' + '\n'.join(str(f) for f in self.kdeditor.fixables) \
                            + '\n'

        except Exception:
            raise
        return retval

    def reportmac(self):
        '''run report functionality for macintosh, or darwin-based systems


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

        try:
            if not RuleKVEditor.report(self, True):
                self.detailedresults += '\nEither file server banner text ' + \
                    'or login window banner text is incorrect, or both'
                retval = False
            if os.path.exists(self.ftpwelcomefile):
                if not self.reportFileContents(self.ftpwelcomefile,
                                               self.bannertext):
                    retval = False
                    self.detailedresults += '\nIncorrect configuration ' + \
                        'text in: ' + str(self.ftpwelcomefile)
            else:
                retval = False
                self.detailedresults += '\nRequired configuration file: ' + \
                    str(self.ftpwelcomefile) + ' not found'

            if os.path.exists(self.policybanner):
                if not self.reportFileContents(self.policybanner,
                                               self.bannertext):
                    retval = False
                    self.detailedresults += '\nIncorrect configuration ' + \
                        'text in: ' + str(self.policybanner)
            else:
                retval = False
                self.detailedresults += '\nRequired configuration file: ' + \
                    str(self.policybanner) + ' not found'
            if not self.reportcommon():
                retval = False
        except Exception:
            raise
        return retval

    def fix(self):
        '''Install warning banners, set the warning text and configure the
        file permissions for the warning banner files.


        :returns: success

        :rtype: boolean

@author Breen Malmberg

        '''

        self.rulesuccess = True
        self.detailedresults = ""
        self.iditerator = 0

        eventlist = self.statechglogger.findrulechanges(self.rulenumber)
        for event in eventlist:
            self.statechglogger.deleteentry(event)

        try:

            if self.ci.getcurrvalue():
                if not self.fixcommon():
                    self.rulesuccess = False
                if self.linux:
                    if not self.fixlinux():
                        self.rulesuccess = False
                elif self.mac:
                    if not self.fixmac():
                        self.rulesuccess = False
                else:
                    self.rulesuccess = False
                    self.detailedresults += '\nCould not identify ' + \
                        'operating system, or operating system not supported.'
            else:
                self.detailedresults += '\nThe configuration item for ' + \
                    'this rule was not enabled so nothing was done.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def fixlinux(self):
        '''run fix functionality for linux-based systems


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

            if not self.fixlinuxcommon():
                success = False
            if self.gnome2:
                if not self.fixgnome2():
                    success = False
            elif self.gnome3:
                if not self.fixgnome3():
                    success = False
            if self.lightdm:
                if not self.fixlightdm():
                    success = False
            if self.kde:
                if not self.fixkde():
                    success = False

        except Exception:
            raise
        return success

    def fixlinuxcommon(self):
        '''run fix functionality which is common to linux platforms


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

            for f in self.bannerfiles:
                if not self.setFileContents(f, WARNINGBANNER):
                    success = False

        except Exception:
            raise
        return success

    def fixcommon(self):
        '''run fix functionlity which is common to all platforms


        :returns: success

        :rtype: boolean

@author: Breen Malmberg

        '''

        success = True

        if self.commoneditor.fixables:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            self.commoneditor.setEventID(myid)
            if not self.commoneditor.fix():
                debug = self.sshdfile + " editor fix is returning false\n"
                self.logger.log(LogPriority.DEBUG, debug)
                success = False
            elif not self.commoneditor.commit():
                debug = self.sshdfile + " editor commit is returning false\n"
                self.logger.log(LogPriority.DEBUG, debug)
                success = False
            if not success:
                self.detailedresults += "Unable to correct contents of " + \
                    self.sshdfile + "\n"
        return success

    def fixgnome2(self):
        '''run fix functionality for gnome2-based systems
        (implementation based on: https://access.redhat.com/solutions/32480)


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True
        gconf = "/usr/bin/gconftool-2"
        confDict = {
            "/apps/gdm/simple-greeter/disable_user_list":
            "boolean true",
            "/apps/gdm/simple-greeter/banner_message_enable":
            "boolean true",
            "/apps/gdm/simple-greeter/banner_message_text":
            'string "$(cat /etc/issue)"'
        }
        gconfcommands = []
        for item in confDict:
            gconfcommands.append(
                gconf + " -s " + item +
                " --config-source=xml:readwrite:/etc/gconf/gconf.xml.system --direct --type "
                + confDict[item])

        try:

            if not self.setFileContents("/etc/issue", GDMWARNINGBANNER):
                success = False
                self.detailedresults += "\nFailed to properly configure the banner text in file /etc/issue"

            # write configuration options to gnome system-wide database
            for gcmd in gconfcommands:
                if not self.checkCommand(gcmd):
                    success = False

        except Exception:
            raise
        return success

    def fixgnome3(self):
        '''run fix functionality for gnome3-based systems


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

            if not self.setFileContents("/etc/issue", GDM3WARNINGBANNER):
                success = False
                self.detailedresults += "\nFailed to properly configure the banner text in file /etc/issue"

            self.logger.log(LogPriority.DEBUG,
                            "Applying gdm profile configurations...")

            # configure the gdm profile
            greeterdefaults = ""
            gdefaultslocs = [
                "/usr/share/gdm/greeter-dconf-defaults",
                "/usr/share/gdm/greeter.dconf-defaults"
            ]
            for loc in gdefaultslocs:
                if os.path.exists(loc):
                    greeterdefaults = loc
            profilebasedir = "/etc/dconf/profile"
            profilepath = profilebasedir + "/gdm"
            profileconflist = [
                "user-db:user\n", "system-db:gdm\n",
                "file-db:" + greeterdefaults + "\n"
            ]
            if not os.path.exists(profilebasedir):
                self.logger.log(
                    LogPriority.DEBUG,
                    "gdm profile base directory does not exist. Creating it..."
                )
                os.makedirs(profilebasedir, 0o755)
            if not self.setFileContents(profilepath, profileconflist):
                success = False

            self.logger.log(LogPriority.DEBUG,
                            "Applying gdm keyfile configurations...")

            # configure the gdm keyfile
            gdmkeyfilebase = "/etc/dconf/db/gdm.d"
            gdmkeyfile = gdmkeyfilebase + "/01-banner-message"
            gdmkeyfileconflist = [
                "[org/gnome/login-screen]\n", "banner-message-enable=true\n",
                "banner-message-text='" + GDM3WARNINGBANNER + "'\n",
                "disable-user-list=true\n"
            ]
            if not os.path.exists(gdmkeyfilebase):
                self.logger.log(
                    LogPriority.DEBUG,
                    "gdm keyfile base directory does not exist. Creating it..."
                )
                os.makedirs(gdmkeyfilebase, 0o755)
            if not self.setFileContents(gdmkeyfile, gdmkeyfileconflist):
                success = False

            self.logger.log(
                LogPriority.DEBUG,
                "Updating dconf with new configuration settings...")

            # update dconf
            dconfupdate = "/usr/bin/dconf update"
            if not self.checkCommand(dconfupdate):
                success = False

            if success:
                self.detailedresults += "\nSuccessfully applied new banner configuration settings. On some systems, a reboot may be required to view updated settings"

        except Exception:
            raise
        return success

    def fixlightdm(self):
        '''run fix functionality for lightdm-based systems


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True
        contentlines = []

        try:

            if not os.path.exists('/etc/lightdm/lightdm.conf.d'):
                os.makedirs('/etc/lightdm/lightdm.conf.d/', 0o755)
            for f in self.lightdmdict:
                contentlines = []
                if isinstance(self.lightdmdict[f], list):
                    for opt in self.lightdmdict[f]:
                        contentlines.append(opt + '\n')
                    if not self.setFileContents(f, contentlines):
                        success = False
                else:
                    if not self.setFileContents(f, self.lightdmdict[f]):
                        success = False
        except Exception:
            raise
        return success

    def fixkde(self):
        '''run fix functionality for kde-based systems


        :returns: success

        :rtype: boolean
@author: Breen Malmberg
@change: Breen Malmberg - 5/25/2016 - moved the commit calls to ensure
they are only called if the fix calls completed successfully

        '''

        success = True

        try:
            if not self.kdefile:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Unable to identify kde configuration file. " +
                    "Can not continue with fix")
                self.detailedresults += "Unable to identify kde " + \
                    "configuration file. Can not continue with fix."
                success = False
                return success

            fileCreated = False
            if not os.path.exists(self.kdefile):
                if createFile(self.kdefile, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation", "filepath": self.kdefile}
                    self.statechglogger.recordchgevent(myid, event)
                    fileCreated = True
                    debug = "kdmrc file successfully created at " + \
                        self.kdefile
                    self.logger.log(LogPriority.DEBUG, debug)
                    setPerms(self.kdefile, [0, 0, 0o644], self.logger)
                    self.reportkde()
                else:
                    self.detailedresults += "\nCould not create kdmrc file " + \
                        "at " + self.kdefile
                    return False

            if not self.kdeditor.fix():
                success = False
                self.detailedresults += "KVEditor fix failed. Fix not applied"
            else:
                if not fileCreated:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.kdeditor.setEventID(myid)
                if not self.kdeditor.commit():
                    success = False
                    self.detailedresults += 'kdeditor commit failed. ' + \
                        'Fix not applied'
            key1 = 'GreetString'
            val1 = '"' + self.kdebannertext + '"'
            data = {"X-*-Greeter": {key1: val1}}
            tmpfile = self.kdefile + ".stonixtmp2"
            greeteditor = KVEditorStonix(self.statechglogger, self.logger,
                                         "tagconf", self.kdefile, tmpfile,
                                         data, "present", "closedeq")
            if not greeteditor.report():
                if greeteditor.fix():
                    if not greeteditor.commit():
                        if not fileCreated:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.kdeditor.setEventID(myid)
                            success = False
                        self.detailedresults += "\nGreetString commit failed"
                else:
                    success = False
                    self.detailedresults += "\nGreetString fix failed"
        except Exception:
            raise
        return success

    def fixmac(self):
        '''run fix functionality for macintosh, or darwin-based systems


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:
            if not RuleKVEditor.fix(self, True):
                success = False
            if not self.setFileContents(self.ftpwelcomefile, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.ftpwelcomefile)
            if not self.setFileContents(self.policybanner, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.policybanner)
        except Exception:
            raise
        return success
Ejemplo n.º 11
0
class Freebsd(object):
    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    :version:
    :author:Derek T Walker 08-06-2012'''
    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/sbin/pkg_add -r -f "
        self.remove = "/usr/sbin/pkg_delete "
###############################################################################

    def installpackage(self, package):
        '''
         Install a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be installed, must be 
            recognizable to the underlying package manager.
        @return bool :
        @author'''
        try:
            installed = False
            #             retval = call(self.install + package,stdout=None,shell=True)
            self.ch.executeCommand(self.install + package)
            if self.ch.getReturnCode() == 0:
                #             if retval == 0:
                self.detailedresults += package + " pkg installed successfully"
                installed = True
            else:
                self.detailedresults += package + " pkg not able to install"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return installed
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################

    def removepackage(self, package):
        '''
         Remove a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be removed, must be 
            recognizable to the underlying package manager.
        @return : bool 
        @author'''
        try:
            self.detailedresults = ""
            removed = False
            stringToMatch = package + "(.*)"
            self.ch.executeCommand(["/usr/sbin/pkg_info"])
            output = self.ch.getOutput()
            #             temp = Popen(["/usr/sbin/pkg_info"],stdout=PIPE,shell=True)
            #             details = temp.stdout.readlines()
            #             temp.stdout.close()
            for cell in output:
                cell2 = cell.split(" ")
                if re.search(stringToMatch, cell2[0]):
                    retval = call(self.remove + cell2[0],
                                  stdout=None,
                                  shell=True)
                    if retval == 0:
                        self.detailedresults += package + " pkg removed \
successfully"

                        removed = True
                    else:
                        self.detailedresults += package + " pkg not able\
to be removed"

            if not self.detailedresults:
                self.detailedresults += package + " pkg not found to remove"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return removed
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if 
        the package is installed.

        @param string package : Name of the package whose installation status 
            is to be checked, must be recognizable to the underlying package 
            manager.
        @return : bool
        @author'''
        try:
            self.detailedresults = ""
            present = False
            stringToMatch = package + "(.*)"
            self.ch.executeCommand(["/usr/sbin/pkg_info"])
            output = self.ch.getOutput()
            #             temp = Popen(["/usr/sbin/pkg_info"],stdout=PIPE,shell=False)
            #             info = temp.stdout.readlines()
            #             temp.stdout.close()
            for cell in output:
                cell2 = cell.split(" ")
                if re.search(stringToMatch, cell2[0]):
                    self.detailedresults += package + " pkg found"
                    present = True
            if not self.detailedresults:
                self.detailedresults += package + " pkg not found"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return present
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################

    def getInstall(self):
        return self.install
###############################################################################

    def getRemove(self):
        return self.remove
Ejemplo n.º 12
0
class SecureCUPS(Rule):
    '''With this rule, you can:
        Disable the CUPS service
        Configure CUPS service
        Disable Printer Browsing
        Limit Printer Browsing
        Disable Print Server Capabilities
        Set the Default Auth Type
        Setup default set of policy blocks for CUPS
    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 128
        self.rulename = 'SecureCUPS'
        self.rulesuccess = True
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = ['CCE 4420-6', 'CCE 4407-3']
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'family': ['linux']
        }

        # init CIs
        datatype1 = 'bool'
        key1 = 'SECURECUPS'
        instructions1 = 'To prevent to STONIX from securing the CUPS service, set the value of ' + \
        'SecureCUPS to False.'
        default1 = True
        self.SecureCUPS = self.initCi(datatype1, key1, instructions1, default1)

        datatype2 = 'bool'
        key2 = 'DISABLECUPS'
        instructions2 = 'To have STONIX completely disable the CUPS service on this system, set ' + \
        'the value of DisableCUPS to True.'
        default2 = False
        self.DisableCUPS = self.initCi(datatype2, key2, instructions2,
                                       default2)

        datatype3 = 'bool'
        key3 = 'DISABLEPRINTBROWSING'
        instructions3 = 'If this machine is to be used as a print server and you wish to enable automatic client discovery of ' \
                        'its shared printers/print resources, then set the value of DISABLEPRINTBROWSING TO False'
        default3 = True
        self.DisablePrintBrowsing = self.initCi(datatype3, key3, instructions3,
                                                default3)

        datatype4 = 'string'
        key4 = 'PRINTBROWSESUBNET'
        instructions4 = 'If this machine is to be used as a print server and you wish to enable automatic client discovery of ' \
                        'its shared printers/print resources, you may wish to limit that discovery to clients only on a certain subnet. ' \
                        'Please customize the value of PRINTBROWSESUBNET to match your networks requirements. Ex: 192.168.0.0/16'
        default4 = ''
        self.PrintBrowseSubnet = self.initCi(datatype4, key4, instructions4,
                                             default4)

        datatype5 = 'bool'
        key5 = 'DISABLEGENERICPORT'
        instructions5 = 'To prevent remote users from potentially connecting ' + \
        'to and using locally configured printers by disabling the CUPS print ' + \
        'server sharing capabilities, set the value of DisableGenericPort to ' + \
        'True.'
        default5 = False
        self.DisableGenericPort = self.initCi(datatype5, key5, instructions5,
                                              default5)

        datatype6 = 'bool'
        key6 = 'SETDEFAULTAUTHTYPE'
        instructions6 = 'To prevent the defaultauthtype for cups from being ' + \
        'set to Digest, set the value of SetDefaultAuthType to False.'
        default6 = True
        self.SetDefaultAuthType = self.initCi(datatype6, key6, instructions6,
                                              default6)

        datatype7 = 'bool'
        key7 = 'SETUPDEFAULTPOLICYBLOCKS'
        instructions7 = "To prevent default policy blocks for cups from " + \
        "being defined in the cups config file, set the value of " + \
        "SetupDefaultPolicyBlocks to False. Note that if you choose to setup " + \
        "the default set of policy blocks you can (and probably should) edit " + \
        "them in the cups config file afterward to customize these policies to " + \
        "your site's particular needs."
        default7 = False
        self.SetupDefaultPolicyBlocks = self.initCi(datatype7, key7,
                                                    instructions7, default7)

        self.localize()

    def localize(self):
        '''set various settings and variables and objects based on
        which OS is currently running


        :returns: void
        @author: Breen Malmberg

        '''

        self.linux = False
        self.darwin = False

        if self.environ.getosfamily() == 'darwin':
            self.darwin = True
        if self.environ.getosfamily() == 'linux':
            self.linux = True

        self.initObjs()
        self.setVars()

    def initObjs(self):
        '''initialize all required class objects


        :returns: void
        @author: Breen Malmberg

        '''

        if self.linux:
            self.ph = Pkghelper(self.logger, self.environ)
        if self.darwin:
            pass
        self.sh = ServiceHelper(self.environ, self.logger)
        self.serviceTarget = ""
        self.ch = CommandHelper(self.logger)

    def setVars(self):
        '''set all class variables depending on which
        OS is currently running


        :returns: void
        @author: Breen Malmberg
        @change: Breen Malmberg - 2/8/2017 - fixed a typo with the var name of the dict
                for cupsd conf options;

        '''

        try:

            # linux config
            if self.linux:
                self.configfileperms = '0640'
                errorlog = "/var/log/cups/error_log"
                logfileperms = '0644'
                self.pkgname = "cups"
                self.svcname = "cups"
                accesslog = "/var/log/cups/access_log"

            self.cupsfilesopts = {}
            self.cupsfilesconf = ""
            self.cupsdconf = ""
            self.cupsdconfremopts = {}

            # darwin config
            if self.darwin:
                accesslog = "/private/var/log/cups/access_log"
                # a value of 'strict' causes issues connecting to printers on some mac systems
                self.configfileperms = '0644'
                logfileperms = '0644'
                errorlog = "/private/var/log/cups/error_log"
                self.svclongname = "/System/Library/LaunchDaemons/org.cups.cupsd.plist"
                self.svcname = "org.cups.cupsd"

# common config
            cupsdconflocs = [
                '/etc/cups/cupsd.conf', '/private/etc/cups/cupsd.conf'
            ]
            for loc in cupsdconflocs:
                if os.path.exists(loc):
                    self.cupsdconf = loc
            self.tmpcupsdconf = self.cupsdconf + ".stonixtmp"

            cupsfileslocs = [
                '/etc/cups/cups-files.conf',
                '/private/etc/cups/cups-files.conf'
            ]
            for loc in cupsfileslocs:
                if os.path.exists(loc):
                    self.cupsfilesconf = loc
            self.tmpcupsfilesconf = self.cupsfilesconf + ".stonixtmp"

            # options for cups-files.conf
            self.cupsfilesopts["ConfigFilePerm"] = self.configfileperms
            self.cupsfilesopts["ErrorLog"] = errorlog
            self.cupsfilesopts["LogFilePerm"] = logfileperms

            # cupsd conf default configuration options
            loglevel = "warn"
            self.cupsdconfopts = {"LogLevel": loglevel}
            self.cupsdconfopts["AccessLog"] = accesslog
            self.cupsdconfopts["AccessLogLevel"] = "config"

            # cupsd conf remove these options
            if self.DisableGenericPort.getcurrvalue():
                self.cupsdconfremopts = {"Port": "631"}

            # this line added to prevent kvaconf trying to load files
            # it can't access during __init__, if the program is being
            # run in user mode
            if self.environ.geteuid() == 0:

                ## create kveditor objects
                if os.path.exists(self.cupsfilesconf):
                    kvtype1 = "conf"
                    path1 = self.cupsfilesconf
                    tmpPath1 = path1 + ".stonixtmp"
                    data1 = self.cupsfilesopts
                    intent1 = "present"
                    configType1 = "space"
                    self.KVcupsfiles = KVEditorStonix(self.statechglogger,
                                                      self.logger, kvtype1,
                                                      path1, tmpPath1, data1,
                                                      intent1, configType1)

                if os.path.exists(self.cupsdconf):
                    kvtype2 = "conf"
                    path2 = self.cupsdconf
                    tmpPath2 = path2 + ".stonixtmp"
                    data2 = self.cupsdconfopts
                    intent2 = "present"
                    configType2 = "space"
                    self.KVcupsd = KVEditorStonix(self.statechglogger,
                                                  self.logger, kvtype2, path2,
                                                  tmpPath2, data2, intent2,
                                                  configType2)

            # policy blocks
            self.serveraccess = """# Restrict access to the server...
<Location />
  Encryption Required
  Order allow,deny
</Location>"""
            self.adminpagesaccess = """# Restrict access to the admin pages...
<Location /admin>
  Encryption Required
  Order allow,deny
</Location>"""
            self.configfilesaccess = """# Restrict access to configuration files...
<Location /admin/conf>
  AuthType Default
  Encryption IfRequested
  Require user @SYSTEM
  Order allow,deny
</Location>"""
            self.logfileaccess = """# Restrict access to log files...
<Location /admin/log>
  AuthType Default
  Encryption IfRequested
  Require user @SYSTEM
  Order allow,deny
</Location>"""
            self.defaultprinterpolicies = """# Set the default printer/job policies...
<Policy default>
  # Job-related operations must be done by the owner or an administrator...
  <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>

  # All administration operations require an administrator to authenticate...
  <Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>

  # All printer operations require a printer operator to authenticate...
  <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Accept-Jobs CUPS-Reject-Jobs>
    AuthType Default
    Require user @SYSTEM
    Order deny,allow
  </Limit>

  # Only the owner or an administrator can cancel or authenticate a job...
  <Limit Cancel-Job CUPS-Authenticate-Job>
    Require user @OWNER @SYSTEM
    Order deny,allow
  </Limit>

  <Limit All>
    Order deny,allow
  </Limit>
</Policy>"""

        except Exception:
            raise

    def sanityCheck(self):
        '''perform sanity check on the cups configuration files


        :returns: sane

        :rtype: bool
@author: Breen Malmberg

        '''

        sane = True

        sanitycheck = "/usr/sbin/cupsd -t"

        try:

            self.ch.executeCommand(sanitycheck)
            retcode = self.ch.getReturnCode()
            output = self.ch.getOutput()
            if retcode != 0:
                sane = False
                self.detailedresults += "\nError while running command: " + str(
                    sanitycheck)
            for line in output:
                if re.search("Bad|Missing|Incorrect|Error|Wrong", line,
                             re.IGNORECASE):
                    sane = False
                    self.detailedresults += "\n" + line

        except Exception:
            raise
        return sane

    def updateOpts(self):
        '''update the kveditor values for the different CIs
        based on their current user-specified values


        :returns: void
        @author: Breen Malmberg
        @change: Breen Malmberg - 2/8/2017 - KVcupsdrem will now only be processed if
                self.DisableGenericPort CI is True

        '''

        try:

            if self.DisableCUPS.getcurrvalue():
                self.PrintBrowseSubnet.updatecurrvalue(False)

            if self.DisablePrintBrowsing.getcurrvalue():
                self.cupsdconfopts["Browsing"] = "Off"
                self.cupsdconfopts["BrowseAllow"] = "none"
                self.cupsdconfopts["BrowseWebIF"] = "No"

            if self.PrintBrowseSubnet.getcurrvalue():
                self.cupsdconfopts["Browsing"] = "On"
                self.cupsdconfopts["BrowseOrder"] = "allow,deny"
                self.cupsdconfopts["BrowseDeny"] = "all"
                self.cupsdconfopts[
                    "BrowseAllow"] = self.PrintBrowseSubnet.getcurrvalue()

            if self.DisableGenericPort.getcurrvalue():
                self.cupsdconfremopts["Port"] = "631"
            if self.SetDefaultAuthType.getcurrvalue():
                self.cupsdconfopts["DefaultAuthType"] = "Negotiate"

            # don't try to create, or update, the kv object,
            # if the file it's based on doesn't exist.
            # this would cause tracebacks in kveditor
            if os.path.exists(self.cupsfilesconf):
                kvtype1 = "conf"
                path1 = self.cupsfilesconf
                tmpPath1 = path1 + ".stonixtmp"
                data1 = self.cupsfilesopts
                intent1 = "present"
                configType1 = "space"
                self.KVcupsfiles = KVEditorStonix(self.statechglogger,
                                                  self.logger, kvtype1, path1,
                                                  tmpPath1, data1, intent1,
                                                  configType1)
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Location of required configuration file cups-files.conf could not be determined"
                )
            if os.path.exists(self.cupsdconf):
                kvtype2 = "conf"
                path2 = self.cupsdconf
                tmpPath2 = path2 + ".stonixtmp"
                data2 = self.cupsdconfopts
                intent2 = "present"
                configType2 = "space"
                self.KVcupsd = KVEditorStonix(self.statechglogger, self.logger,
                                              kvtype2, path2, tmpPath2, data2,
                                              intent2, configType2)
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Location of required configuration file cupsd.conf could not be determined"
                )

        except Exception:
            raise

    def report(self):
        '''run report methods/actions appropriate for the current
        OS and return compliancy status


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG, "\n\nREPORT()\n\n")

        # DEFAULTS
        self.detailedresults = ""
        self.compliant = True
        badopts = [
            "^HostNameLookups\s+Double", "^Sandboxing\s+strict",
            "^FatalErrors\s+config"
        ]
        badoptsfiles = [self.cupsdconf, self.cupsfilesconf]

        try:

            # check for bad config options in files
            for f in badoptsfiles:
                if os.path.exists(f):
                    fh = open(f, 'r')
                    contentlines = fh.readlines()
                    fh.close()
                    for line in contentlines:
                        for opt in badopts:
                            if re.search(opt, line, re.IGNORECASE):
                                self.compliant = False
            # if these opts exist, then we don't want to do anything else
            ## except just remove them
            if not self.compliant:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Bad configuration options found in cups config files. Will now remove them."
                )
                self.formatDetailedResults('report', self.compliant,
                                           self.detailedresults)
                return self.compliant

            if self.linux:
                if not self.ph.check(self.pkgname):
                    self.detailedresults += "\nCUPS not installed on this system. Nothing to secure."
                    self.formatDetailedResults('report', self.compliant,
                                               self.detailedresults)
                    return self.compliant

            # update kv objects with any new
            # user-specified information
            self.updateOpts()

            if self.SecureCUPS.getcurrvalue():
                # is cups secured?
                if not self.reportSecure():
                    self.compliant = False
                # make sure config syntax is correct
                if not self.sanityCheck():
                    self.compliant = False

            if self.DisableCUPS.getcurrvalue():
                # is cups disabled?
                if not self.reportDisabled():
                    self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults('report', self.compliant,
                                   self.detailedresults)
        return self.compliant

    def reportDisabled(self):
        '''return True if cups is disabled
        return False if cups is enabled


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            if self.linux:
                if self.sh.auditService(self.svcname,
                                        serviceTarget=self.serviceTarget):
                    retval = False
                    self.detailedresults += "\nThe " + str(
                        self.svcname) + " service is still configured to run"
            elif self.darwin:
                if self.sh.auditService(self.svclongname,
                                        serviceTarget=self.svcname):
                    retval = False
                    self.detailedresults += "\nThe " + str(
                        self.svcname) + " service is still configured to run"

        except Exception:
            raise
        return retval

    def checkPolicyBlocks(self):
        '''report on whether default policy blocks are currently set up
        in cups configuration. Note that if these already exist, we
        do not want to overwrite them as the local admin may have
        them customised to their specific environment.


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG, "\n\nCHECKPOLICYBLOCKS()\n\n")

        retval = True
        rootfound = False
        adminfound = False
        adminconffound = False
        defpolicyfound = False

        try:

            if os.path.exists(self.cupsdconf):
                self.logger.log(
                    LogPriority.DEBUG,
                    "\n\nCUPSD CONF EXISTS. OPENING FILE AND READING CONTENTS...\n\n"
                )
                f = open(self.cupsdconf, 'r')
                contentlines = f.readlines()
                f.close()

                self.logger.log(
                    LogPriority.DEBUG,
                    "\n\nCONTENTS READ. CHECKING FOR POLICY BLOCKS...\n\n")
                for line in contentlines:
                    if re.search('\<Location \/\>', line, re.IGNORECASE):
                        rootfound = True
                    if re.search('\<Location \/admin\>', line, re.IGNORECASE):
                        adminfound = True
                    if re.search('\<Location \/admin\/conf\>', line,
                                 re.IGNORECASE):
                        adminconffound = True
                    if re.search('\<Policy default\>', line, re.IGNORECASE):
                        defpolicyfound = True
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "\n\ncupsd.conf file does not exist. Nothing to check...\n\n"
                )
                return retval

            if not rootfound:
                retval = False
                self.detailedresults += "\nCUPS Root location policy not defined"
            if not adminfound:
                retval = False
                self.detailedresults += "\nCUPS admin location policy not defined"
            if not adminconffound:
                retval = False
                self.detailedresults += "\nCUPS admin/conf location policy not defined"
            if not defpolicyfound:
                retval = False
                self.detailedresults += "\nCUPS Default Policy block not defined"

            if retval:
                self.logger.log(LogPriority.DEBUG,
                                "\n\nALL POLICY BLOCKS OK\n\n")

        except Exception:
            raise
        return retval

    def reportSecure(self):
        '''run report actions common to all platforms


        :returns: retval

        :rtype: bool
@author: Breen Malmberg
@change: Breen Malmberg - 2/8/2017 - KVcupsdrem will now only be run if
        self.DisableGenericPort CI is True

        '''

        self.logger.log(LogPriority.DEBUG, "\n\nREPORTSECURE()\n\n")

        retval = True

        try:

            if self.SecureCUPS.getcurrvalue():

                if os.path.exists(self.cupsdconf):
                    # Report on cupsd.conf change/add options
                    self.KVcupsd.setData(self.cupsdconfopts)
                    self.KVcupsd.setIntent("present")
                    self.KVcupsd.report()

                    # Report on cupsd.conf remove options
                    self.KVcupsd.setData(self.cupsdconfremopts)
                    self.KVcupsd.setIntent("notpresent")
                    self.KVcupsd.report()

                    if self.KVcupsd.fixables:
                        retval = False
                        self.detailedresults += "\nThe following configuration options, in " + str(
                            self.cupsdconf) + ", are incorrect:\n" + "\n".join(
                                self.KVcupsd.fixables)

                    if not self.checkPolicyBlocks():
                        retval = False
                else:
                    pass

                if os.path.exists(self.cupsfilesconf):
                    self.KVcupsfiles.report()
                    if self.KVcupsfiles.fixables:
                        retval = False
                        self.detailedresults += "\nThe following configuration options, in " + str(
                            self.cupsfilesconf
                        ) + ", are incorrect:\n" + "\n".join(
                            self.KVcupsfiles.fixables)
                else:
                    pass

            else:
                self.detailedresults += "\nNeither SecureCUPS nor DisableCUPS CI's was enabled. Nothing was done."

        except Exception:
            raise
        return retval

    def fix(self):
        '''run fix methods/actions appropriate for the current
        OS and return success status of fix


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

        # DEFAULTS
        self.detailedresults = ""
        success = True
        self.iditerator = 0
        badopts = [
            "^HostNameLookups\s+Double", "^Sandboxing\s+strict",
            "^FatalErrors\s+config"
        ]
        badoptsfiles = [self.cupsdconf, self.cupsfilesconf]
        foundbadopts = False

        try:

            # fix bad opts; do not record state change,
            ## so that a possible revert, afterward, will
            ## not revert back to the bad state
            ## this code is a one-off for a hotfix and
            ## should probably be discontinued in some future
            ## release at some point
            for f in badoptsfiles:
                contentlines = []
                if os.path.exists(f):
                    fh = open(f, 'r')
                    contentlines = fh.readlines()
                    fh.close()
                    for line in contentlines:
                        for opt in badopts:
                            if re.search(opt, line, re.IGNORECASE):
                                foundbadopts = True
                                contentlines = [
                                    c.replace(line, '\n') for c in contentlines
                                ]
                    # finished building new contentlines; now write them for each file
                    fn = open(f, 'w')
                    fn.writelines(contentlines)
                    fn.close()
                    os.chmod(f, 0o644)
            if foundbadopts:
                # do not continue with rest of fix because that would save state for this fix run
                self.logger.log(
                    LogPriority.DEBUG,
                    "Reloading cups service to read configuration changes...")
                if self.darwin:
                    self.sh.reloadService(self.svclongname,
                                          serviceTarget=self.svcname)
                else:
                    self.sh.reloadService(self.svcname,
                                          serviceTarget=self.serviceTarget)
                self.logger.log(
                    LogPriority.DEBUG,
                    "Removed bad configuration options from cups config files. Exiting..."
                )
                self.formatDetailedResults('fix', success,
                                           self.detailedresults)
                return success

            # Are any of the CIs enabled?
            # If not, then exit, returning True
            if not self.SecureCUPS.getcurrvalue() and \
            not self.DisableCUPS.getcurrvalue():
                self.detailedresults += "\nNo CI was enabled, so nothing was done."
                self.logger.log(
                    LogPriority.DEBUG,
                    "SecureCUPS rule was run, but neither SecureCUPS, nor DisableCUPS CI's were enabled so nothing was done!"
                )
                self.formatDetailedResults('fix', success,
                                           self.detailedresults)
                return success

            if self.linux and not self.ph.check("cups"):
                self.detailedresults += "\nCUPS is not installed. Nothing to do."
                self.formatDetailedResults('fix', success,
                                           self.detailedresults)
                return success

            if self.SecureCUPS.getcurrvalue():
                if not self.fixSecure():
                    success = False

            if self.DisableCUPS.getcurrvalue():
                if not self.disableCUPS():
                    success = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults('fix', success, self.detailedresults)
        return success

    def fixSecure(self):
        '''run fix actions common to all platforms


        :returns: retval

        :rtype: bool
@author: Breen Malmberg
@change: Breen Malmberg - 2/8/2017 - added inline comments; changed the way KVCupsdrem
        was being handled (will not be run if there are no options to remove; aka if
        self.DisableGenericPort CI is False)

        '''

        retval = True
        pdefaultfound = False
        serveraccessfound = False
        adminaccessfound = False
        configaccessfound = False
        logfileaccessfound = False

        try:

            if os.path.exists(self.cupsdconf):
                # Fix cupsd.conf add/change/remove options
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.KVcupsd.setEventID(myid)
                if self.KVcupsd.fix():
                    if not self.KVcupsd.commit():
                        self.detailedresults += "\nCommit failed for cupsd.conf"
                        self.logger.log(LogPriority.DEBUG,
                                        "Commit failed for KVcupsd")
                        retval = False

            # cups-files conf add/change options
            if os.path.exists(self.cupsfilesconf):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.KVcupsfiles.setEventID(myid)
                if self.KVcupsfiles.fix():
                    if not self.KVcupsfiles.commit():
                        self.detailedresults += "\nCommit failed for cups-files.conf"
                        self.logger.log(LogPriority.DEBUG,
                                        "Commit failed for KVcupsfiles")
                        retval = False

# cupsdconf default policy blocks
# this portion cannot be handled by kveditor because of its
# xml style formatting
            if os.path.exists(self.cupsdconf):
                if self.SetupDefaultPolicyBlocks.getcurrvalue():
                    f = open(self.cupsdconf, 'r')
                    contentlines = f.readlines()
                    f.close()
                    for line in contentlines:
                        if re.search("\<Location \/\>", line, re.IGNORECASE):
                            serveraccessfound = True
                    for line in contentlines:
                        if re.search("\<Location \/admin\>", line,
                                     re.IGNORECASE):
                            adminaccessfound = True
                    for line in contentlines:
                        if re.search("\<Location \/admin\/conf\>", line,
                                     re.IGNORECASE):
                            configaccessfound = True
                    for line in contentlines:
                        if re.search("\<Location \/admin\/log\>", line,
                                     re.IGNORECASE):
                            logfileaccessfound = True
                    for line in contentlines:
                        if re.search("\<Policy default\>", line,
                                     re.IGNORECASE):
                            pdefaultfound = True

                    if not serveraccessfound:
                        contentlines.append("\n" + self.serveraccess + "\n")
                        self.logger.log(
                            LogPriority.DEBUG,
                            "\n\nroot access policy block not found. adding it...\n\n"
                        )

                    if not adminaccessfound:
                        contentlines.append("\n" + self.adminpagesaccess +
                                            "\n")
                        self.logger.log(
                            LogPriority.DEBUG,
                            "\n\nadmin access policy block not found. adding it...\n\n"
                        )

                    if not configaccessfound:
                        contentlines.append("\n" + self.configfilesaccess +
                                            "\n")
                        self.logger.log(
                            LogPriority.DEBUG,
                            "\n\nconfig access policy block not found. adding it...\n\n"
                        )

                    if not logfileaccessfound:
                        contentlines.append("\n" + self.logfileaccess + "\n")
                        self.logger.log(
                            LogPriority.DEBUG,
                            "\n\nlog file access policy block not found. adding it...\n\n"
                        )

                    if not pdefaultfound:
                        contentlines.append("\n" +
                                            self.defaultprinterpolicies + "\n")
                        self.logger.log(
                            LogPriority.DEBUG,
                            "\n\ndefault policy block not found. adding it...\n\n"
                        )

                    tf = open(self.tmpcupsdconf, 'w')
                    tf.writelines(contentlines)
                    tf.close()

                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filename": self.cupsdconf}

                    self.statechglogger.recordfilechange(
                        self.cupsdconf, self.tmpcupsdconf, myid)
                    self.statechglogger.recordchgevent(myid, event)
                    self.logger.log(
                        LogPriority.DEBUG, "\n\nwriting changes to " +
                        str(self.cupsdconf) + " file...\n\n")
                    os.rename(self.tmpcupsdconf, self.cupsdconf)
                    self.logger.log(
                        LogPriority.DEBUG,
                        "\n\nsetting permissions and ownership for " +
                        str(self.cupsdconf) + " file...\n\n")
                    os.chown(self.cupsdconf, 0, 0)
                    if self.linux:
                        os.chmod(self.cupsdconf, 0o640)
                    elif self.darwin:
                        os.chmod(self.cupsdconf, 0o644)

            if not self.reloadCUPS():
                retval = False

        except Exception:
            raise
        return retval

    def reloadCUPS(self):
        '''reload the cups service to read the new configurations


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            # do not attempt to reload the service
            # if the rule is set to disable it
            if self.DisableCUPS.getcurrvalue():
                return retval

            if self.linux:
                if not self.sh.reloadService(self.svcname,
                                             serviceTarget=self.serviceTarget):
                    retval = False
                    self.detailedresults += "|nThere was a problem reloading the " + str(
                        self.svcname) + " service"
            elif self.darwin:
                if not self.sh.reloadService(self.svclongname,
                                             serviceTarget=self.svcname):
                    retval = False
                    self.detailedresults += "|nThere was a problem reloading the " + str(
                        self.svcname) + " service"

        except Exception:
            raise
        return retval

    def disableCUPS(self):
        '''disable the cups service


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            if self.linux:

                if not self.sh.disableService(
                        self.svcname, serviceTarget=self.serviceTarget):
                    retval = False
                    self.detailedresults += "\nThere was a problem disabling the " + str(
                        self.svcname) + " service"

            elif self.darwin:

                if not self.sh.disableService(self.svclongname,
                                              serviceTarget=self.svcname):
                    retval = False
                    self.detailedresults += "\nThere was a problem disabling the " + str(
                        self.svcname) + " service"

        except Exception:
            raise
        return retval
Ejemplo n.º 13
0
class DisableRoot(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 160
        self.rulename = "DisableRoot"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # configuration item instantiation
        datatype = 'bool'
        key = 'DISABLEROOT'
        instructions = "To disable this rule set the value of DISABLEROOT " + \
            "to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.cmdhelper = CommandHelper(self.logger)
        self.guidance = ["NSA 1.3.14"]
        self.sethelptext()

    def report(self):
        '''DisableRoot.report() method to report whether root is disabled or not
        @author: dwalker

        :param self: essential if you override this definition
        :returns: boolean - True if system is compliant, False if not

        '''
        try:
            self.detailedresults = ""
            compliant = False
            cmd = [
                "/usr/bin/dscl", ".", "-read", "/Users/root",
                "AuthenticationAuthority"
            ]
            if not self.cmdhelper.executeCommand(cmd):
                self.detailedresults += "Unable to run the /usr/bin/dscl " + \
                    "command."
                compliant = False
            else:
                output = self.cmdhelper.getOutput()
                error = self.cmdhelper.getError()
                if output:
                    for line in output:
                        if search("No such key", line):
                            compliant = True
                            break
                elif error:
                    for line in error:
                        if search("No such key", line):
                            compliant = True
                            break
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

###############################################################################

    def fix(self):
        '''DisableRoot.fix() method to run the command necessary to disable root
        on the mac.
        @author: dwalker

        :param self: essential if you override this definition
        :returns: boolean - True if able to fix successfully, False if not

        '''
        try:
            if not self.ci.getcurrvalue():
                return
            self.detailedresults = ""

            delete = [
                "/usr/bin/dscl", ".", "-delete", "/Users/root",
                "AuthenticationAuthority"
            ]
            create = [
                "/usr/bin/dscl", ".", "-create", "/Users/root", "passwd", "*"
            ]
            if not self.cmdhelper.executeCommand(delete):
                self.detailedresults += "wasn't able to run the command " + \
                    str(delete) + "\n"
            elif not self.cmdhelper.executeCommand(create):
                self.detailedresults += "wasn't able to run the command " + \
                    str(create) + "\n"
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 14
0
class CheckDupIDs(Rule):
    """This class checks the local accounts database for duplicate IDs. All
    accounts on a system must have unique UIDs. This class inherits the base
    Rule class, which in turn inherits observable.
    
    @note: Per RedHat STIG - CCE-RHEL7-CCE-TBD 2.4.1.2.3, check group references.
    
       All GIDs referenced in /etc/passwd must be defined in /etc/group
    
       Add a group to the system for each GID referenced without a
       corresponding group. Inconsistency in GIDs between /etc/passwd and
       /etc/group could lead to a user having unintended rights.
    
       Watch for LDAP issues (e.g. user default group changed to a group
       coming from LDAP).
    
       For Mac, also check that all the user's primary group ID's are in the
       local directory.

    """
    def __init__(self, config, environ, logger, statechglogger):
        """
        private method to initialize the module

        :param config:
        :param environ:
        :param logger:
        :param statechglogger:
        """

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.commandhelper = CommandHelper(self.logger)
        self.statechglogger = statechglogger
        self.rulenumber = 58
        self.rulename = 'CheckDupIDs'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.guidance = ['CCE-RHEL7-CCE-TBD 2.4.1.2.3']
        self.sethelptext()
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.issuelist = []
        self.auditonly = True

    def report(self):
        """CheckDuplicateIds.report(): produce a report on whether or not local
        accounts databases have duplicate UIDs present.

        :return: self.compliant - boolean; True if compliant, False if not
        """

        try:
            self.detailedresults = ""
            self.issuelist = []
            if self.environ.getosfamily() == 'darwin':
                self.compliant = self.osxcheck()
            else:
                self.compliant = self.nixcheck()
            if self.compliant:
                self.detailedresults = "No duplicate IDs detected."
                self.currstate = 'configured'
            else:
                self.detailedresults = "One or more Duplicate IDs was " + \
                    "detected on this system. For accountability purposes " + \
                    "all accounts are required to have unique UID values. " + \
                    str(self.issuelist)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def osxcheck(self):
        """This version of the check should work for all OS X systems.
        @author: Ekkehard

        :return: result - boolean; True if no duplicates found; False if duplicates found
        """

        try:
            result = False
            nixcheckresult = self.nixcheck()
            oscheckresult = True
            issue = ""
            # Check for duplicate users
            cmd = ["/usr/bin/dscl", ".", "list", "/users", "uid"]
            self.commandhelper.executeCommand(cmd)
            output = self.commandhelper.getOutput()
            userlist = []
            uidlist = []
            for line in output:
                linelist = line.split()
                user = linelist[0]
                uid = linelist[1]
                if user not in userlist:
                    userlist.append(user)
                else:
                    issue = "Duplicate User: '******' (UID = '" + uid + "')"
                    self.issuelist.append(issue)
                    oscheckresult = False
                if uid not in uidlist:
                    uidlist.append(uid)
                else:
                    issue = "Duplicate UID: '" + uid + "' (User = '******')"
                    self.issuelist.append(issue)
                    oscheckresult = False
# Check for duplicate groups
            cmd = ["/usr/bin/dscl", ".", "list", "/groups", "gid"]
            self.commandhelper.executeCommand(cmd)
            output = self.commandhelper.getOutput()
            grouplist = []
            gidlist = []
            for line in output:
                linelist = line.split()
                group = linelist[0]
                gid = linelist[1]
                if group not in grouplist:
                    grouplist.append(group)
                else:
                    issue = "Duplicate Group: '" + group + "' (GID = '" + gid + "')"
                    self.issuelist.append(issue)
                    oscheckresult = False
                if gid not in gidlist:
                    gidlist.append(gid)
                else:
                    issue = "Duplicate GID: '" + gid + "' (Group = '" + group + "')"
                    self.issuelist.append(issue)
                    oscheckresult = False

            if (nixcheckresult & oscheckresult):
                result = True
            else:
                result = False
            return result

        except:
            raise

    def nixcheck(self):
        """This version of the check should work for all u systems. This
        is borrowed from the STOR code so the check methodology is well tested.

        :return: retval - boolean; True if no duplicates; False if duplicates found
        """

        try:
            retval = True
            filelist = ['/etc/passwd', '/etc/group']
            for adb in filelist:
                if os.path.exists(adb):
                    self.logger.log(
                        LogPriority.DEBUG,
                        ['CheckDuplicateIds.nixcheck', "Checking : " + adb])
                    namelist = []
                    idlist = []
                    fdata = open(adb, 'r')
                    for line in fdata:
                        line = line.split(':')
                        try:
                            if len(line) > 2:
                                self.logger.log(LogPriority.DEBUG, [
                                    'CheckDuplicateIds.nixcheck',
                                    "Checking line: " + str(line)
                                ])
                                name = line[0]
                                uid = line[2]
                                self.logger.log(
                                    LogPriority.DEBUG,
                                    "Checking account: " + name + ' ' + uid)
                                if name not in namelist:
                                    namelist.append(name)
                                else:
                                    issue = "Duplicate Name: NAME('" + name + "'; UID('" + uid + "')"
                                    self.issuelist.append(issue)
                                    retval = False
                                if uid not in idlist:
                                    idlist.append(uid)
                                else:
                                    issue = "Duplicate UID: NAME('" + name + "'; UID('" + uid + "')"
                                    self.issuelist.append(issue)
                        except (IndexError):
                            # Some systems have malformed lines in the
                            # accounts db due to poor administration
                            # practices. Go to the next record.
                            continue
                    self.logger.log(LogPriority.DEBUG,
                                    "NAMELIST: " + str(namelist))
                    self.logger.log(LogPriority.DEBUG,
                                    "IDLIST: " + str(idlist))
                    fdata.close()
            return retval

        except:
            raise

    def getcolumn(self, file_to_read="", column=0, separator=":"):
        """Get the data out of <file_to_read> column <column> using <separator>
        
        Intended for use with the /etc/group and /etc/password files for getting
        and comparing group information.

        :param file_to_read:  (Default value = "")
        :param column:  (Default value = 0)
        :param separator:  (Default value = ":")

        :return: column_data - dictionary; columnized data set of specified file
        """

        if file_to_read and isinstance(column, int) and separator:
            reading = open(file_to_read, "r")

            for line in reading.readlines():
                try:
                    column_data = line.split(separator)[column]
                except IndexError:
                    continue
            return column_data

    def checkgrouprefs(self):
        """Per RedHat STIG - CCE-RHEL7-CCE-TBD 2.4.1.2.3, check group references.
        
        All GIDs referenced in /etc/passwd must be defined in /etc/group
        
        Add a group to the system for each GID referenced without a
        corresponding group. Inconsistency in GIDs between /etc/passwd and
        /etc/group could lead to a user having unintended rights.
        
        Watch for LDAP issues (e.g. user default group changed to a group
        coming from LDAP).

        """

        group_groups = self.getcolumn("/etc/group", 2)
        pwd_groups = self.getcolumn("/etc/passwd", 3)

        for group in pwd_groups:
            if not group in group_groups:
                message = "Group: " + str(
                    group) + " is not in the passwd file."
                self.logger.log(LogPriority.INFO, message)
                self.issuelist.append(message)

    def checkmacgrouprefs(self):
        """Per RedHat STIG - CCE-RHEL7-CCE-TBD 2.4.1.2.3, check group references.
        
           All GIDs referenced in /etc/passwd must be defined in /etc/group
        
           Add a group to the system for each GID referenced without a
           corresponding group. Inconsistency in GIDs between /etc/passwd and
           /etc/group could lead to a user having unintended rights.
        
           Watch for LDAP issues (e.g. user default group changed to a group
           coming from LDAP).
        
        For Mac, check that al the user's primary group ID's are in the local
        directory.

        """

        self.dscl = "/usr/bin/dscl"
        user_groups = []
        dscl_users = [self.dscl, ".", "list", "/users"]
        self.commandhelper.executeCommand(dscl_users)
        output = self.commandhelper.getOutput()
        dscl_users = output

        system_users = ["avahi", "daemon", "nobody", "root"]

        for user in dscl_users:
            if re.match("^_", user) or user in system_users:
                continue

            dscl_user_group = [
                self.dscl, ".", "read", "/Users/" + str(user), "gid"
            ]
            self.commandhelper.executeCommand(dscl_user_group)
            output = self.commandhelper.getOutput()
            self.logger.log(LogPriority.INFO, "output: " + str(output))
            try:
                mygroup = output[0].split()[1]
                user_groups.append(mygroup)
            except (KeyError, IndexError):
                pass

        dscl_groups = [self.dscl, ".", "list", "/Groups"]
        self.commandhelper.executeCommand(dscl_groups)
        output = self.commandhelper.getOutput()
        group_groups = output

        for group in user_groups:
            if not group in group_groups:
                message = "Group: " + str(
                    group) + " is not in the passwd file."
                self.logger.log(LogPriority.INFO, message)
                self.issuelist.append(message)
class STIGConfigureLoginWindowPolicy(Rule):
    '''Deploy LoginWindow Policy configuration profiles for OS X Yosemite 10.10'''
    def __init__(self, config, environ, logdispatch, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logdispatch, statechglogger)

        self.logger = logdispatch
        self.rulenumber = 362
        self.rulename = "STIGConfigureLoginWindowPolicy"
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'fisma': 'high'
        }
        datatype = "bool"
        key = "STIGLOGINCONFIG"
        instructions = "To disable the installation of the login window " + \
            "profile set the value of STIGLOGINCONFIG to False"
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.iditerator = 0
        self.identifier = "mil.disa.STIG.loginwindow.alacarte"
        if search("10\.10.*", self.environ.getosver()):
            #             self.profile = "/Users/username/stonix/src/" + \
            #                 "stonix_resources/files/" + \
            #                 "U_Apple_OS_X_10-10_Workstation_V1R2_STIG_Login_Window_Policy.mobileconfig"
            self.profile = "/Applications/stonix4mac.app/Contents/" + \
                         "Resources/stonix.app/Contents/MacOS/" + \
                         "stonix_resources/files/" + \
                         "U_Apple_OS_X_10-10_Workstation_V1R2_STIG_Login_Window_Policy.mobileconfig"
        elif search("10\.11\.*", self.environ.getosver()):
            #             self.profile = "/Users/username/stonix/src/" + \
            #                 "stonix_resources/files/" + \
            #                 "U_Apple_OS_X_10-11_V1R1_STIG_Login_Window_Policy.mobileconfig"
            self.profile = "/Applications/stonix4mac.app/Contents/" + \
                         "Resources/stonix.app/Contents/MacOS/" + \
                         "stonix_resources/files/" + \
                         "U_Apple_OS_X_10-11_V1R1_STIG_Login_Window_Policy.mobileconfig"
        else:
            #             self.profile = "/Users/username/stonix/src/" + \
            #                 "stonix_resources/files/" + \
            #                 "U_Apple_macOS_10-12_V1R1_STIG_Login_Window_Policy.mobileconfig "
            self.profile = "/Applications/stonix4mac.app/Contents/" + \
                         "Resources/stonix.app/Contents/MacOS/" + \
                         "stonix_resources/files/" + \
                         "U_Apple_macOS_10-12_V1R1_STIG_Login_Window_Policy.mobileconfig"

    def report(self):
        try:
            compliant = False
            self.detailedresults = ""
            self.ch = CommandHelper(self.logger)
            cmd = ["/usr/bin/profiles", "-P"]
            if not self.ch.executeCommand(cmd):
                compliant = False
                self.detailedresults += "Unable to run profiles command\n"
            else:
                output = self.ch.getOutput()
                if output:
                    for line in output:
                        if search(
                                "^There are no configuration profiles installed",
                                line.strip()):
                            compliant = False
                            self.detailedresults += "There are no configuration profiles installed\n"
                            break
                        elif search("mil\.disa\.STIG\.loginwindow\.alacarte$",
                                    line.strip()):
                            self.detailedresults += "Couldn't find loginwindow profile\n"
                            compliant = True
                            break
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            if os.path.exists(self.profile):
                success = True
                self.detailedresults = ""
                self.iditerator = 0
                eventlist = self.statechglogger.findrulechanges(
                    self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)
                cmd = ["/usr/bin/profiles", "-I", "-F", self.profile]
                if not self.ch.executeCommand(cmd):
                    debug = "Unable to install profile\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    cmd = ["/usr/bin/profiles", "-R", "-p", self.identifier]
                    event = {"eventtype": "comm", "command": cmd}
                    self.statechglogger.recordchgevent(myid, event)
            else:
                success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 16
0
class ConfigureDotFiles(Rule):
    '''A user who can modify another user's configuration files can likely execute
    commands with the other user's privileges, including stealing data,
    destroying files, or launching further attacks on the system. This rule
    ensures that no dot files within users' home directories possess the
    world/other - writable permission.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 46
        self.rulename = 'ConfigureDotFiles'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = False
        self.compliant = False
        datatype = 'bool'
        key = 'CONFIGUREDOTFILES'
        instructions = '''To prevent dot files in user home directories from \
being made non-world-writable, set the value of ConfigureDotFiles to False.'''
        default = True
        self.ConfigureDotFiles = self.initCi(datatype, key, instructions,
                                             default)
        self.guidance = ['CIS', 'NSA 2.3.4.3', 'CCE-4561-7']
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

    def report(self):
        '''The report method examines the current configuration and determines
        whether or not it is correct. If the config is correct then the
        self.compliant and self.detailed results properties are
        updated to reflect the system status.


        :returns: self.compliant

        :rtype: boolean
@author: Breen Malmberg

        '''

        # defaults
        dotfilelist = []
        self.compliant = True
        self.detailedresults = ""
        self.cmdhelper = CommandHelper(self.logger)

        try:

            if self.environ.getostype() == 'Mac OS X':
                dotfilelist = self.buildmacdotfilelist()
            else:
                dotfilelist = self.buildlinuxdotfilelist()

            for item in dotfilelist:
                # is item world writable?
                if isWritable(self.logger, item, 'other'):
                    self.compliant = False
                    self.detailedresults += '\nFound world writable dot file: ' \
                                            + str(item)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def buildlinuxdotfilelist(self):
        '''build a list of linux dot files for the current user


        :returns: dotfilelist

        :rtype: list
@author: Breen Malmberg

        '''

        dotfilelist = []

        try:

            f = open('/etc/passwd', 'r')
            contentlines = f.readlines()
            f.close()

            for line in contentlines:

                line = line.split(':')

                if len(line) >= 6 and line[5] and \
                   self.environ.geteuidhome() == str(line[5]):

                    line[2] = int(line[2])
                    if line[2] >= 500 and not re.search('nfsnobody', line[0]):

                        if os.path.exists(line[5]):
                            filelist = os.listdir(line[5])
                            for i in range(len(filelist)):
                                if re.search('^\.', filelist[i]):
                                    dotfilelist.append(line[5] + '/' +
                                                       filelist[i])

        except Exception:
            raise
        return dotfilelist

    def buildmacdotfilelist(self):
        '''build a list of mac dot files for the current user


        :returns: dotfilelist

        :rtype: list
@author: Breen Malmberg

        '''

        dotfilelist = []
        users = []
        homedirs = []

        try:

            cmd = ["/usr/bin/dscl", ".", "-list", "/Users"]
            try:
                self.cmdhelper.executeCommand(cmd)
            except OSError as oser:
                if re.search('DSOpenDirServiceErr', str(oser)):
                    self.detailedresults += '\n' + str(oser)
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
            output = self.cmdhelper.getOutput()
            error = self.cmdhelper.getError()
            if error:
                self.detailedresults += '\nCould not get a list of user ' + \
                    'home directories. Returning empty list...'
                return dotfilelist

            if output:
                for user in output:
                    if not re.search('^_|^root|^\/$', user):
                        users.append(user.strip())
                        debug = "Adding user " + user.strip() + " to users " \
                                + "list"
                        self.logger.log(LogPriority.DEBUG, debug)

            if users:
                for user in users:
                    try:
                        currpwd = pwd.getpwnam(user)
                        homedirs.append(currpwd[5])
                    except KeyError:
                        continue

            if homedirs:
                for homedir in homedirs:
                    if self.environ.geteuidhome() == homedir:
                        filelist = os.listdir(homedir)
                        for i in range(len(filelist)):
                            if re.search('^\.', filelist[i]):
                                dotfilelist.append(homedir + '/' + filelist[i])

        except Exception:
            raise
        return dotfilelist

    def fix(self):
        '''remove any world writable flags from any dot files in user's home
        directory
        
        @author: Breen Malmberg


        '''

        # defaults
        dotfilelist = []
        self.detailedresults = ""
        self.rulesuccess = True

        if self.ConfigureDotFiles.getcurrvalue():

            try:

                if self.environ.getostype() == 'Mac OS X':
                    dotfilelist = self.buildmacdotfilelist()
                else:
                    dotfilelist = self.buildlinuxdotfilelist()

                if dotfilelist:
                    for item in dotfilelist:
                        if os.path.isfile(item):

                            try:
                                os.system('chmod o-w ' + item)
                            except (OSError, IOError):
                                self.rulesuccess = False
                                self.detailedresults += '\nCould not chmod: ' \
                                                        + str(item)

            except (KeyboardInterrupt, SystemExit):
                raise
            except Exception as err:
                self.rulesuccess = False
                self.detailedresults = self.detailedresults + "\n" + \
                    str(err) + " - " + str(traceback.format_exc())
                self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 17
0
class BootSecurity(Rule):
    """The Boot Security rule configures the system to run a job at system boot
    time that handles turning off potential vulnerability points such as: wifi,
    bluetooth, microphones, and cameras.

    """
    def __init__(self, config, environ, logger, statechglogger):
        """
        private method to initialize the module

        :param config: configuration object instance
        :param environ: environment object instance
        :param logger: logdispatcher object instance
        :param statechglogger: statechglogger object instance
        """

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 18
        self.rulename = 'BootSecurity'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = []
        self.applicable = {
            'type': 'white',
            'family': ['linux'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.servicehelper = ServiceHelper(environ, logger)
        self.ch = CommandHelper(self.logdispatch)

        if os.path.exists('/bin/systemctl'):
            self.type = 'systemd'
        elif os.path.exists('/sbin/launchd'):
            self.type = 'mac'
        else:
            self.type = 'rclocal'
            self.rclocalpath = '/etc/rc.local'
            if os.path.islink(self.rclocalpath):
                paths = ['/etc/rc.d/rc.local', '/etc/init.d/rc.local']
                for rcpath in paths:
                    if os.path.isfile(rcpath):
                        self.rclocalpath = rcpath
            self.logdispatch.log(LogPriority.DEBUG,
                                 'Using rc.local file ' + self.rclocalpath)

        datatype = 'bool'
        key = 'BOOTSECURITY'
        instructions = """To disable this rule set the value of BOOTSECURITY to False."""
        default = True
        self.bootci = self.initCi(datatype, key, instructions, default)

        datatype2 = 'bool'
        key2 = 'ENABLEFIPS'
        instructions2 = """!WARNING! DO NOT ENABLE THIS OPTION IF YOUR SYSTEM IS ARLEADY FDE ENCRYPTED! To enable full fips compliance on this system, at boot, set the value of ENABLEFIPS to True."""
        default2 = False
        self.fips_ci = self.initCi(datatype2, key2, instructions2, default2)

        self.set_paths()

    def set_paths(self):
        """

        """

        self.systemd_boot_service_file = "/etc/systemd/system/stonixBootSecurity.service"
        self.rc_boot_script = "/usr/bin/stonix_resources/stonixBootSecurityLinux.py"
        self.systemd_service_name = "stonixBootSecurity.service"
        self.stonix_launchd_plist = "/Library/LaunchDaemons/gov.lanl.stonix.bootsecurity.plist"
        self.stonix_launchd_name = "gov.lanl.stonix.bootsecurity"
        self.grub_file = "/etc/default/grub"
        self.grub_config_file = ""
        grub_configs = [
            "/boot/grub2/grub.cfg", "/boot/efi/EFI/redhat/grub.cfg"
        ]
        for c in grub_configs:
            if os.path.isfile(c):
                self.grub_config_file = c
                break
        self.grub_updater_cmd = ""
        grub_updater_locs = [
            "/usr/sbin/grub2-mkconfig", "/sbin/grub2-mkconfig",
            "/usr/sbin/update-grub", "/sbin/update-grub"
        ]
        for l in grub_updater_locs:
            if os.path.isfile(l):
                self.grub_updater_cmd = l
                break

        if self.grub_updater_cmd in [
                "/usr/sbin/grub2-mkconfig", "/sbin/grub2-mkconfig"
        ]:
            self.grub_updater_cmd += " -o " + self.grub_config_file

    def auditsystemd(self):
        """
        check whether the stonixbootsecurity.service service
        module is loaded

        :return: boolean; True if the stonixbootsecurity.service service
        module is loaded, False if not
        """

        compliant = True

        self.stonix_boot_service_contents = """[Unit]
        Description=Stonix Boot Security
        After=network.target

        [Service]
        ExecStart=/usr/bin/stonix_resources/stonixBootSecurityLinux.py

        [Install]
        WantedBy=multi-user.target
        """

        try:

            # check if service file exists
            if not os.path.isfile(self.systemd_boot_service_file):
                compliant = False
                self.detailedresults += "\nstonix boot service unit does not exist"
            else:
                # check contents of service file
                f = open(self.systemd_boot_service_file, "r")
                contents = f.read()
                f.close()
                if contents != self.stonix_boot_service_contents:
                    compliant = False
                    self.detailedresults += "\nstonix boot service unit contents incorrect"

            # check if service is enabled
            if not self.servicehelper.auditService(self.systemd_service_name):
                compliant = False
                self.detailedresults += "\nstonix boot service is not enabled"

        except:
            raise

        return compliant

    def auditrclocal(self):
        """
        check whether the rclocal configuration file contains the correct
        stonixBootSecurity line entry

        :return: compliant - boolean; True if compliant, False if not
        """

        compliant = True

        try:

            tmppath = self.rc_boot_script + ".stonixtmp"
            data = {"python3": self.rc_boot_script}
            self.rc_boot_security_editor = KVEditorStonix(
                self.statechglogger, self.logdispatch, "conf",
                self.rc_boot_script, tmppath, data, "present", "space")
            if not self.rc_boot_security_editor.report():
                self.detailedresults += "\nThe following config line is missing or incorrect from " + str(
                    self.rc_boot_script) + "\n" + "\n".join(
                        self.rc_boot_security_editor.fixables)
                compliant = False

        except:
            raise

        return compliant

    def auditmac(self):
        """
        check whether the stonixbootsecurity launchd job exists

        :return:
        """

        compliant = True

        self.logdispatch.log(LogPriority.DEBUG,
                             "Looking for macOS launchd job")

        self.stonix_plist_contents = """<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
            <key>Label</key>
            <string>gov.lanl.stonix.bootsecurity</string>
            <key>Program</key>
            <string>/Applications/stonix4mac.app/Contents/Resources/stonix.app/Contents/MacOS/stonix_resources/stonixBootSecurityMac</string>
            <key>RunAtLoad</key>
            <true/>
        </dict>
        </plist>"""

        try:

            if not os.path.exists(self.stonix_launchd_plist):
                compliant = False
                self.detailedresults += "\nCould not locate stonix boot security launchd job"

        except:
            raise

        return compliant

    def report_boot_fips(self):
        """

        :return:
        """

        found_fips = False
        compliant = True

        try:

            if not self.fips_ci.getcurrvalue():
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "fips compliance check disabled. Skipping fips compliance check."
                )
                return compliant
            else:
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "fips compliance check enabled. Checking for fips compliance..."
                )

            # check grub template file for fips
            if self.grub_file:
                f = open(self.grub_file, "r")
                contentlines = f.readlines()
                f.close()

                for line in contentlines:
                    if re.search('^GRUB_CMDLINE_LINUX_DEFAULT=', line, re.I):
                        if re.search("fips=1", line, re.I):
                            found_fips = True

            # check permanent grub config file for fips
            if self.grub_config_file:
                f = open(self.grub_config_file, "r")
                contentlines = f.readlines()
                f.close()

                for line in contentlines:
                    if re.search("fips=1", line, re.I):
                        found_fips = True

            if self.is_luks_encrypted():
                if found_fips:
                    # fips=1 will break boot config if luks encrypted
                    compliant = False
                    self.detailedresults += "\nfips=1 config option found in boot config line. This will break system boot while the system is luks encrypted. Will remove this line and fips compatibility for luks will be configured instead."
                else:
                    self.detailedresults += "\nNo problems detected with boot config"
            else:
                if not found_fips:
                    compliant = False
                    self.detailedresults += "\nfips=1 config option not found in boot config line. As this system is not luks encrypted, this line will be added to the boot config."
                else:
                    self.detailedresults += "\nfips enabled in boot config"

        except:
            raise

        return compliant

    def report(self):
        """
        check whether the current system complies with the boot security settings
        to disable wifi, bluetooth and microphone at boot time

        :return: self.compliant - boolean; True if system is compliant, False if not
        """

        self.detailedresults = ""
        self.compliant = True

        try:

            if self.type == 'mac':
                self.logdispatch.log(LogPriority.DEBUG,
                                     'Checking for Mac plist')
                if not self.auditmac():
                    self.compliant = False
                    self.detailedresults += '\nPlist for stonixBootSecurity Launch Daemon not found.'

            elif self.type == 'systemd':
                self.logdispatch.log(LogPriority.DEBUG,
                                     'Checking for systemd service')
                if not self.auditsystemd():
                    self.compliant = False
                    self.detailedresults += '\nService for stonixBootSecurity not active.'

            elif self.type == 'rclocal':
                self.logdispatch.log(LogPriority.DEBUG, 'Checking rc.local')
                if not self.auditrclocal():
                    self.compliant = False
                    self.detailedresults += '\nstonixBootSecurity-Linux not scheduled in rc.local.'
            else:
                self.compliant = False
                self.detailedresults += "\nThis platform is not supported by STONIX"

            if self.compliant:
                self.detailedresults += '\nstonixBootSecurity correctly scheduled for execution at boot.'

            if not self.report_boot_fips():
                self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.compliant = False
            self.detailedresults += traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def setsystemd(self):
        """
        create a systemd service unit which will run an installed script to
        disable wifi, bluetooth and microphone at boot time

        """

        self.logdispatch.log(LogPriority.DEBUG,
                             "Creating stonix boot security service unit")

        try:

            # create the new service unit
            f = open(self.systemd_boot_service_file, "w")
            f.write(self.stonix_boot_service_contents)
            f.close()
            myid = iterate(self.iditerator, self.rulenumber)
            event = {
                "eventtype": "creation",
                "filepath": self.systemd_boot_service_file
            }
            self.statechglogger.recordchgevent(myid, event)
            os.chown(self.systemd_boot_service_file, 0, 0)
            os.chmod(self.systemd_boot_service_file, 0o644)

            # make the service manager aware of the new service unit
            reloadcmd = '/bin/systemctl daemon-reload'
            try:
                self.ch.executeCommand(reloadcmd)
            except:
                pass

            # ensure that the new service is enabled
            self.servicehelper.enableService('stonixBootSecurity')

        except:
            raise

    def setrclocal(self):
        """
        install and run a boot security script which will disable
        wifi, bluetooth and microphone at boot time

        """

        success = True

        try:

            if not self.rc_boot_security_editor.fix():
                self.logdispatch.log(LogPriority.DEBUG,
                                     "KVEditor failed to fix")
                success = False
            elif not self.rc_boot_security_editor.commit():
                self.logdispatch.log(LogPriority.DEBUG,
                                     "KVEditor failed to commit changes")
                success = False

        except:
            raise

        return success

    def setmac(self):
        """
        install a boot security plist on mac, which will run an oascript
        to disable microphone on mac at boot time

        """

        success = True

        try:

            whandle = open(self.stonix_launchd_plist, 'w')
            whandle.write(self.stonix_plist_contents)
            whandle.close()
            os.chown(self.stonix_launchd_plist, 0, 0)
            os.chmod(self.stonix_launchd_plist, 0o644)

        except:
            raise

        return success

    def fix_boot_fips(self):
        """

        :return:
        """

        success = True
        fixed_fips = False
        tmpfile = self.grub_file + ".stonixtmp"

        try:

            if not self.fips_ci.getcurrvalue():
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "fips compliance option disabled. Skipping fips compliance fix..."
                )
                return success
            else:
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "fips compliance option enabled. Proceeding with fix compliance fix..."
                )

            if self.environ.getosname() == "RHEL":
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "System detected as RHEL. Running RHEL specific fixes...")
                if not self.fix_rhel7_boot_fips():
                    success = False
                return success
            else:
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "System is not RHEL-based. Running generic fixes...")

                f = open(self.grub_file, "r")
                contentlines = f.readlines()
                f.close()

                for line in contentlines:
                    if re.search("^GRUB_CMDLINE_LINUX_DEFAULT=", line, re.I):
                        contentlines = [
                            c.replace(line,
                                      line.strip()[:-1] + ' fips=1"\n')
                            for c in contentlines
                        ]
                        fixed_fips = True

                if not fixed_fips:
                    contentlines.append(
                        'GRUB_CMDLINE_LINUX_DEFAULT="splash quiet audit=1 fips=1"\n'
                    )

                tf = open(tmpfile, "w")
                tf.writelines(contentlines)
                tf.close()

                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": self.grub_file}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange(tmpfile, self.grub_file,
                                                     myid)
                os.rename(tmpfile, self.grub_file)

                self.ch.executeCommand(self.grub_updater_cmd)

        except:
            raise

        return success

    def remove_fips_line(self):
        """
        the fips=1 configuration option at the end of the
        linuxefi boot line in grub config (for efi-based systems)
        causes rhel to revert to an emergency dracut mode instead of
        booting normally, when the system is encrypted with luks
        this method will ensure that line is removed and the grub
        configuration is updated.

        :return:
        """

        success = True

        self.logdispatch.log(
            LogPriority.DEBUG,
            "Attempting to remove fips=1 option from grub boot config...")

        tmpfile = self.grub_file + ".stonixtmp"

        f = open(self.grub_file, "r")
        contentlines = f.readlines()
        f.close()

        for line in contentlines:
            if re.search("^GRUB_CMDLINE_LINUX_DEFAULT=", line, re.I):
                self.logdispatch.log(LogPriority.DEBUG,
                                     "fips=1 found in boot config file")
                line2 = line.replace("fips=1", "")
                self.logdispatch.log(
                    LogPriority.DEBUG,
                    "removing fips=1 from " + str(self.grub_file))
                contentlines = [c.replace(line, line2) for c in contentlines]

        tf = open(tmpfile, "w")
        tf.writelines(contentlines)
        tf.close()

        self.iditerator += 1
        myid = iterate(self.iditerator, self.rulenumber)
        event = {"eventtype": "conf", "filepath": self.grub_file}
        self.statechglogger.recordchgevent(myid, event)
        self.statechglogger.recordfilechange(tmpfile, self.grub_file, myid)
        os.rename(tmpfile, self.grub_file)

        self.logdispatch.log(LogPriority.DEBUG,
                             "regenerating efi boot config file...")
        self.ch.executeCommand(self.grub_updater_cmd)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            self.logdispatch.log(LogPriority.WARNING,
                                 "Failed to update efi boot config file!")

        f = open(self.grub_config_file, "r")
        contentlines = f.readlines()
        f.close()

        for line in contentlines:
            if re.search("fips=1", line, re.I):
                success = False

        if not success:
            self.logdispatch.log(
                LogPriority.WARNING,
                "fips=1 option still found in efi boot configuration!")
        else:
            self.logdispatch.log(
                LogPriority.DEBUG,
                "fips option successfully removed from efi boot configuration")

        return success

    def is_luks_encrypted(self):
        """
        check all drive devices to see if any are luks encrypted

        :return: luks_encrypted
        :rtype: bool
        """

        luks_encrypted = False

        command = "/sbin/blkid"
        devices = []

        if not os.path.isfile(command):
            self.logdispatch.log(
                LogPriority.WARNING,
                "Unable to check devices for luks encryption due to missing utility 'blkid'"
            )
            return luks_encrypted

        self.logdispatch.log(LogPriority.DEBUG,
                             "Checking if any devices are luks encrypted...")

        try:

            self.ch.executeCommand(command)
            output = self.ch.getOutput()
            for line in output:
                if re.search('TYPE="crypto_LUKS"', line, re.I):
                    luks_encrypted = True
                    try:
                        devices.append(str(line.split()[0]))
                    except (IndexError, KeyError):
                        continue

        except:
            raise

        for d in devices:
            if re.search(":", d):
                devices = [d.replace(":", "") for d in devices]

        if luks_encrypted:
            self.logdispatch.log(
                LogPriority.DEBUG,
                "The following devices are luks encrypted:\n" +
                "\n".join(devices))

        return luks_encrypted

    def configure_luks_compatibility(self):
        """
        configure rhel 7 systems, which are LUKS encrypted, to be compatible with fips
        https://access.redhat.com/solutions/137833

        :return:
        """

        prelink_pkg = "prelink"
        prelink = "/usr/sbin/prelink"
        dracut_aes_pkg = "dracut-fips-aesni"
        dracut_fips_pkg = "dracut-fips"
        prelink_conf_file = "/etc/sysconfig/prelink"
        dracut = "/usr/bin/dracut"
        grep = "/usr/bin/grep"

        mv = "/usr/bin/mv"
        prelink_installed = False
        aes_supported = False

        try:

            if self.is_luks_encrypted():
                if not self.remove_fips_line():
                    self.detailedresults += "\nFailed to remove fips=1 from efi boot configuration file. Please run: 'sudo grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg' manually!"

            # check for cpu aes compatibility
            self.ch.executeCommand(grep + " -w aes /proc/cpuinfo")
            outputstring = self.ch.getOutputString()
            if re.search("aes", outputstring, re.I):
                aes_supported = True

            # check if prelink package is installed
            if self.ph.check(prelink_pkg):
                prelink_installed = True

            # install dracut fips package
            self.ph.install(dracut_fips_pkg)

            # install dracut aes package if cpu supports it
            if aes_supported:
                self.ph.install(dracut_aes_pkg)

            # disable prelinking if installed
            if prelink_installed:
                f = open(prelink_conf_file, "w")
                f.write("PRELINKING=no")
                f.close()
                os.chmod(prelink_conf_file, 0o644)
                os.chown(prelink_conf_file, 0, 0)
                self.ch.executeCommand(prelink + " -uav")

            # backup existing initramfs
            self.ch.executeCommand(
                mv + " -v /boot/initramfs-$(uname -r).img{,.bak}")

            # rebuild initramfs (this command may take some time)
            self.ch.executeCommand(dracut)

        except:
            raise

    def fix_rhel7_boot_fips(self):
        """
        enable fips compliance on redhat 7 systems
        https://access.redhat.com/solutions/137833

        :return: success
        :rtype: bool
        """

        success = True
        self.ph = Pkghelper(self.logdispatch, self.environ)
        grubby = "/usr/sbin/grubby"
        findmnt = "/usr/bin/findmnt"

        try:

            # configure the system to be compatible with luks and fips
            self.configure_luks_compatibility()

            # add fips=1 to kernel boot line (requires sytem restart to take effect)
            self.ch.executeCommand(grubby + " --update-kernel=$(" + grubby +
                                   " --default-kernel) --args=fips=1")
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                success = False
                self.detailedresults += "\nFailed to enable fips compliance in kernel boot line"

            # update boot partition info
            uuid = ""
            self.ch.executeCommand(findmnt + " -no uuid /boot")
            retcode = self.ch.getReturnCode()
            if retcode == 0:
                uuid = self.ch.getOutputString()
            else:
                success = False
                self.detailedresults += "\nFailed to update boot partition info"
            if uuid:
                self.ch.executeCommand(
                    "[[ -n $uuid ]] && " + grubby + " --update-kernel=$(" +
                    grubby + " --default-kernel) --args=boot=UUID=${uuid}")

        except:
            raise

        return success

    def fix(self):
        """
        install system job which will run and disable bluetooth, microphone and wifi at boot

        """

        self.detailedresults = ""
        self.rulesuccess = True
        self.iditerator = 0

        try:

            if self.bootci.getcurrvalue():

                if self.type == 'mac':
                    self.logdispatch.log(LogPriority.DEBUG,
                                         'Creating Mac plist')
                    self.setmac()

                elif self.type == 'systemd':
                    self.logdispatch.log(LogPriority.DEBUG,
                                         'Creating systemd service')
                    self.setsystemd()

                elif self.type == 'rclocal':
                    self.logdispatch.log(LogPriority.DEBUG,
                                         'Creating rc.local entry')
                    self.setrclocal()

                else:
                    self.detailedresults = 'ERROR: Fix could not determine where boot job should be scheduled!'
                    self.logdispatch.log(LogPriority.ERROR,
                                         self.detailedresults)
                    self.rulesuccess = False

                if self.fips_ci.getcurrvalue():
                    if not self.fix_boot_fips():
                        self.rulesuccess = False

            else:
                self.logdispatch.log(LogPriority.DEBUG,
                                     "Rule not enabled. Nothing was done.")

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.rulesuccess = False
            self.detailedresults += traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess
Ejemplo n.º 18
0
class Zypper(object):
    '''
    The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    @author: Derek T Walker
    @change: 2012/08/08 Derek Walker - Original Implementation
    @change: 2014/09/10 dkennel - Added -n option to search command string
    @change: 2014/12/24 Breen Malmberg - fixed a typo in the old search string;
            fixed multiple pep8 violations; changed search strings to be match exact and
            search for installed or available separately
    @change: 2015/08/20 eball - Added getPackageFromFile and self.rpm var
    @change: 2016/08/02 eball - Moved checkInstall return out of else block
    @change: 2017/04/19 Breen Malmberg - refactored multiple methods; cleaned up doc
            strings; added logging; added two methods: Update and checkUpdate;
            removed detailedresults reset in __init__ (this should always be handled
            in the calling rule); replaced detailedresults instances with logging;
            added the flag "--quiet" to the install variable
    '''

    def __init__(self, logger):
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.zyploc = "/usr/bin/zypper"
        self.install = self.zyploc + " --non-interactive --quiet install "
        self.remove = self.zyploc + " --non-interactive remove "
        self.searchi = self.zyploc + " --non-interactive search --match-exact -i "
        self.searchu = self.zyploc + " --non-interactive search --match-exact -u "
        self.updates = self.zyploc + " lu "
        self.upzypp = self.zyploc + " up "
        self.rpm = "/usr/bin/rpm -q "
        self.pkgtype = "zypper"

    def installpackage(self, package):
        '''
        Install a package. Return a bool indicating success or failure.

        @param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        @return: installed
        @rtype: bool
        @author: Derek Walker
        @change: Breen Malmberg - 12/24/2014 - fixed method doc string formatting
        @change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop
        '''

        installed = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to install package due to zypper package manager being in-use by another process.")
                installed = False
                return installed
            else:
                self.logger.log(LogPriority.DEBUG, "zypper package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.install + package)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    raise repoError('zypper', retcode)
            except repoError as repoerr:
                if not repoerr.success:
                    installed = False

            if installed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " installed successfully")
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to install package " + str(package))

        except Exception:
            raise
        return installed

    def removepackage(self, package):
        '''
        Remove a package. Return a bool indicating success or failure.

        @param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        @return: removed
        @rtype: bool
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - fixed method doc string formatting;
                fixed an issue with var 'removed' not
                being initialized before it was called
        '''

        removed = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to remove package due to zypper package manager being in-use by another process.")
                removed = False
                return removed
            else:
                self.logger.log(LogPriority.DEBUG, "zypper package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.remove + package)
                retcode = self.ch.getReturnCode()
                if retcode != 0:
                    raise repoError('zypper', retcode)
            except repoError as repoerr:
                if not repoerr.success:
                    removed = False

            if removed:
                self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was removed successfully")
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to remove package " + str(package))

        except Exception:
            raise
        return removed

    def checkInstall(self, package):
        '''
        Check the installation status of a package. Return a bool; True if
        the package is installed.

        @param string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: bool
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - fixed method doc string formatting
        @change: 12/24/2014 - Breen Malmberg - changed var name 'found' to
            'installed'
        @change: 12/24/2014 - Breen Malmberg - now uses correct search syntax
        @change: 12/24/2014 - Breen Malmberg - removed detailedresults update on
            'found but not installed' as this no longer applies to this method
        '''

        installed = True
        errstr = ""
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG, "Timed out while attempting to check status of  package due to zypper package manager being in-use by another process.")
                installed = False
                return installed
            else:
                self.logger.log(LogPriority.DEBUG, "zypper package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.searchi + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('zypper', retcode, str(errstr))
            except repoError as repoerr:
                if not repoerr.success:
                    installed = False

            if installed:
                self.logger.log(LogPriority.DEBUG, " Package " + str(package) + " is installed")
            else:
                self.logger.log(LogPriority.DEBUG, " Package " + str(package) + " is NOT installed")

        except Exception:
            raise
        return installed

    def checkAvailable(self, package):
        '''
        check if given package is available to install on the current system

        @param: package string name of package to search for
        @return: bool
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation
        @change: 12/24/2014 - Breen Malmberg - changed var name 'found' to
            'available'
        @change: 12/24/2014 - Breen Malmberg - fixed search syntax and updated search
            variable name
        @change: Breen Malmberg - 5/1/2017 - replaced detailedresults with logging;
                added parameter validation
        '''

        available = True
        maxtries = 12
        trynum = 0

        while psRunning("zypper"):
            trynum += 1
            if trynum == maxtries:
                self.logger.log(LogPriority.DEBUG,
                                "Timed out while attempting to check availability of package, due to zypper package manager being in-use by another process.")
                available = False
                return available
            else:
                self.logger.log(LogPriority.DEBUG,
                                "zypper package manager is in-use by another process. Waiting for it to be freed...")
                time.sleep(5)

        try:

            try:
                self.ch.executeCommand(self.searchu + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                output = self.ch.getOutput()
                if retcode != 0:
                    raise repoError('zypper', retcode, str(errstr))
                else:
                    for line in output:
                        if re.search(package, line):
                            available = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(repoerr))
                    return False

                if available:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is available to install")
                else:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " is NOT available to install")

        except Exception:
            raise
        return available

    def checkUpdate(self, package=""):
        '''
        check for available updates for specified
        package.
        if no package is specified, then check for
        updates to the entire system.

        @param package: string; name of package to check
        @return: updatesavail
        @rtype: bool
        @author: Breen Malmberg
        '''

        # zypper does not have a package-specific list updates mechanism
        # you have to list all updates or nothing

        updatesavail = True

        try:

            try:
                if package:
                    self.ch.executeCommand(self.updates + " | grep " + package)
                else:
                    self.ch.executeCommand(self.updates)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('zypper', retcode, str(errstr))
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    updatesavail = False

            if package:
                if not updatesavail:
                    self.logger.log(LogPriority.DEBUG, "No updates are available for package " + str(package))
                else:
                    self.logger.log(LogPriority.DEBUG, "Updates are available for package " + str(package))

            else:
                if not updatesavail:
                    self.logger.log(LogPriority.DEBUG, "No updates are available")
                else:
                    self.logger.log(LogPriority.DEBUG, "Updates are available")

        except Exception:
            raise
        return updatesavail

    def Update(self, package=""):
        '''
        update a specified package
        if no package name is specified,
        then update all packages on the system

        @param package: string; name of package to update
        @return: updated
        @rtype: bool
        @author: Breen Malmberg
        '''

        updated = True

        try:

            try:
                self.ch.executeCommand(self.upzypp + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('zypper', retcode, str(errstr))
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    updated = False

        except Exception:
            raise
        return updated

    def getPackageFromFile(self, filename):
        '''Returns the name of the package that provides the given
        filename/path.

        @param: string filename : The name or path of the file to resolve
        @return: string name of package if found, None otherwise
        @author: Eric Ball
        '''

        packagename = ""

        try:

            try:
                self.ch.executeCommand(self.rpm + "-f " + filename)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                outputstr = self.ch.getOutputString()
                if retcode != 0:
                    raise repoError('zypper', retcode, str(errstr))
                    # return ""
                else:
                    packagename = outputstr
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                else:
                    packagename = outputstr

        except Exception:
            raise

        return packagename

    def getInstall(self):
        '''
        return the install command string for the zypper pkg manager

        @return: string
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation
        '''

        return self.install

    def getRemove(self):
        '''
        return the uninstall/remove command string for the zypper pkg manager

        @return: string
        @author: Derek Walker
        @change: 12/24/2014 - Breen Malmberg - added method documentation
        '''

        return self.remove
Ejemplo n.º 19
0
class ConfigureNetworkTime(Rule):
    """The ConfigureNetworkTime class specifies network time servers and enables the appropriate service"""
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 96
        self.rulename = 'ConfigureNetworkTime'
        self.formatDetailedResults("initialize")
        self.logger = logger
        self.sethelptext()
        self.mandatory = True
        self.rootrequired = True
        self.guidance = [
            'CIS', 'NSA(3.10.2)', 'CCE-4134-3', 'CCE-4385-1', 'CCE-4032-9',
            'CCE-4424-8', 'CCE-3487-6'
        ]

        # init CI
        self.ci = self.initCi(
            "bool", "CONFIGURENETWORKTIME",
            "To prevent STONIX from configuring network time servers for this system, set the value of CONFIGURENETWORKTIME to False.",
            True)

        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        self.libc = getLibc()

    def _set_paths(self):
        """

        """

        self.time_package = "chrony"

        self.time_conf_file = ""
        time_conf_files = ["/etc/chrony.conf", "/etc/chrony/chrony.conf"]
        for cf in time_conf_files:
            if os.path.isfile(cf):
                self.time_conf_file = cf
                break

    def _test_connection(self, hostname):
        """

        :param str hostname: host to test connection to
        :return: reachable
        :rtype: bool
        """

        reachable = False

        if isinstance(hostname, list):
            for h in hostname:
                response = os.system("ping -c 1 " + h)
                if response == 0:
                    reachable = True
                    break
        else:
            response = os.system("ping -c 1 " + hostname)
            if response == 0:
                reachable = True

        return reachable

    def _report_install(self):
        """

        :return: installed
        :rtype: bool
        """

        installed = True

        if not self.ph.check('chrony'):
            installed = False

        return installed

    def _report_conf(self):
        """

        :return: configured
        :rtype: bool
        """

        configured = True

        self.time_conf_dict = {
            "driftfile": "/var/lib/chrony/drift",
            "makestep": "1.0 3",
            "rtcsync": "",
            "logdir": "/var/log/chrony",
            "cmddeny": "all",
            "server": []
        }
        for ts in self.time_servers:
            self.time_conf_dict["server"].append(ts)

        tmpfile = self.time_conf_file + ".stonixtmp"

        self.time_conf_editor = KVEditorStonix(self.statechglogger,
                                               self.logger, "conf",
                                               self.time_conf_file, tmpfile,
                                               self.time_conf_dict, "present",
                                               "space")
        if not self.time_conf_editor.report():
            configured = False
            self.detailedresults += "\nThe following configuration options are incorrect in " + str(
                self.time_conf_file) + ":\n" + "\n".join(
                    self.time_conf_editor.fixables)

        return configured

    def _report_linux(self):
        """

        :return: compliant
        :rtype: bool
        """

        self._set_paths()

        compliant = True

        if not self._test_connection(self.time_servers):
            compliant = False
            self.detailedresults += "\nCould not reach network time servers"
        if not self._report_install():
            compliant = False
            self.detailedresults += "\nCould not install network time package"
        elif not self._report_conf():
            compliant = False
            self.detailedresults += "\nFailed to properly configure network time configuration file"

        return compliant

    def report(self):
        """determine whether the fix() method of this rule has run successfully
        yet or not

        :return: self.compliant
        :rtype: bool
        """

        self.detailedresults = ""

        # UPDATE THIS SECTION IF THE CONSTANTS BEING USED IN THIS CLASS CHANGE
        self.constlist = [NTPSERVERSEXTERNAL, NTPSERVERSINTERNAL]
        if not any(self.constlist):
            self.compliant = False
            self.detailedresults += "\nThis rule requires that at least one of the following constants, in localize.py, be defined and not None: NTPSERVERSEXTERNAL, NTPSERVERSINTERNAL"
            self.formatDetailedResults("report", self.compliant,
                                       self.detailedresults)
            return self.compliant

        self.ph = Pkghelper(self.logger, self.environ)
        self.ch = CommandHelper(self.logger)

        if self._test_connection(NTPSERVERSINTERNAL):
            self.time_servers = NTPSERVERSINTERNAL
        else:
            self.time_servers = NTPSERVERSEXTERNAL

        self.compliant = True

        try:

            if self.environ.getosfamily() == "darwin":
                self.ss = "/usr/sbin/systemsetup"
                if not self._report_darwin():
                    self.compliant = False
            else:
                if not self._report_linux():
                    self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def _report_darwin(self):
        """determine rule compliance status for darwin based systems


        :return: configured
        :rtype:
        """

        # defaults
        configured = True
        usingnetworktime = False
        timeserverfound = False

        try:

            cmd = [self.ss, "-getnetworktimeserver"]
            self.ch.executeCommand(cmd)
            self.output = self.ch.getOutput()

            for line in self.output:
                for item in self.time_servers:
                    if re.search(item, line):
                        timeserverfound = True

            cmd2 = [self.ss, "-getusingnetworktime"]
            self.ch.executeCommand(cmd2)
            self.output2 = self.ch.getOutput()

            for line in self.output2:
                if re.search('On', line):
                    usingnetworktime = True

            if not usingnetworktime:
                self.detailedresults += '\nusingnetworktime not set to on'
                configured = False
            if not timeserverfound:
                self.detailedresults += '\ncorrect time server not configured'
                configured = False

        except Exception:
            raise
        return configured

    def fix(self):
        """Decide which fix sub method to run, and run it to configure network time

        :return: self.rulesuccess
        :rtype: bool
        """

        # UPDATE THIS SECTION IF THE CONSTANTS BEING USED IN THIS CLASS CHANGE
        if not self.checkConsts(self.constlist):
            self.rulesuccess = False
            self.formatDetailedResults("fix", self.rulesuccess,
                                       self.detailedresults)
            return self.rulesuccess

        self.detailedresults = ""
        self.iditerator = 0
        self.rulesuccess = True

        try:

            if self.ci.getcurrvalue():

                if self.environ.getosfamily() == "darwin":
                    if not self._fix_darwin():
                        self.rulesuccess = False
                else:
                    if not self._fix_linux():
                        self.rulesuccess = False

            else:
                self.detailedresults += "\nRule was not enabled. No action was taken."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def _fix_linux(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        if not self._fix_install():
            success = False
        else:
            self._set_paths()
            self._report_conf()
        if not self._fix_conf():
            success = False

        return success

    def _fix_install(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        if not self.ph.install(self.time_package):
            success = False
            self.logger.log(LogPriority.DEBUG,
                            "Failed to install network time package")

        return success

    def _fix_conf(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        if not self.time_conf_editor.fix():
            success = False
            self.logger.log(LogPriority.DEBUG, "KVEditor failed to fix")
        elif not self.time_conf_editor.commit():
            success = False
            self.logger.log(LogPriority.DEBUG, "KVEditor failed to commit")

        return success

    def _fix_darwin(self):
        """
        private method to perform fix operations for mac os x machines
        
        :return: fixresult
        :rtype: bool
        """

        parseoutput1 = []
        parseoutput2 = []
        fixresult = True

        try:

            # set network time on
            cmd1 = [self.ss, "-setusingnetworktime", "on"]
            try:
                self.ch.executeCommand(cmd1)
                self.libc.sync()
            except Exception as errmsg:
                self.logger.log(LogPriority.DEBUG, str(errmsg))

            try:

                # set undo cmd to restore original network time state
                for line in self.output2:
                    if re.search('Network Time', line):
                        parseoutput1 = line.split(':')
                originaltimestate = parseoutput1[1].strip()

            except KeyError:
                originaltimestate = "off"

            undocmd1 = self.ss + " -setusingnetworktime " + originaltimestate
            event = {"eventtype": "commandstring", "command": undocmd1}
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            self.statechglogger.recordchgevent(myid, event)

            # set network time server
            cmd2 = [
                self.ss, "-setnetworktimeserver",
                str(self.time_servers[0])
            ]
            try:
                self.ch.executeCommand(cmd2)
            except Exception as errmsg:
                self.logger.log(LogPriority.DEBUG, str(errmsg))

            try:

                # set undo cmd to reinstate original time server
                for line in self.output:
                    if re.search('Network Time Server', line):
                        parseoutput2 = line.split(':')
                originalnetworktimeserver = parseoutput2[1].strip()

            except (IndexError, KeyError):
                originalnetworktimeserver = NTPSERVERSINTERNAL[0]

            undocmd2 = self.ss + " -setusingnetworktime " + \
            originalnetworktimeserver
            event = {"eventtype": "commandstring", "command": undocmd2}
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            self.statechglogger.recordchgevent(myid, event)

        except:
            raise

        return fixresult
Ejemplo n.º 20
0
class AptGet(object):
    '''Linux specific package manager for distributions that use the apt-get
    command to install packages.

    @author: Derek T Walker
    @change: 2012/08/06 dwalker - Original Implementation
    @change: 2015/08/20 eball - Added getPackageFromFile
    '''
    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "sudo DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get -y --force-yes install "
        self.remove = "/usr/bin/apt-get -y remove "
###############################################################################

    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be installed, must be
            recognizable to the underlying package manager.
        @return bool :
        @author dwalker'''
        try:
            self.ch.executeCommand(self.install + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg installed successfully"
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                return True
            else:
                # try to install for a second time
                self.ch.executeCommand(self.install + package)
                if self.ch.getReturnCode() == 0:
                    self.detailedresults = package + \
                        " pkg installed successfully"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return True
                else:
                    self.detailedresults = package + " pkg not able to install"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be removed, must be
            recognizable to the underlying package manager.
        @return bool :
        @author'''

        try:
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                self.detailedresults = package + " pkg removed successfully"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return True
            else:
                self.detailedresults = package + " pkg not able to be removed"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.

        @param: string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: bool :
        @author: dwalker'''

        try:
            stringToMatch = "(.*)" + package + "(.*)"
            self.ch.executeCommand(["/usr/bin/dpkg", "-l", package])
            info = self.ch.getOutput()
            match = False
            for line in info:
                if search(stringToMatch, line):
                    parts = line.split()
                    if parts[0] == "ii":
                        match = True
                        break
                else:
                    continue
            if match:
                self.detailedresults = package + " pkg found and installed\n"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return True
            else:
                self.detailedresults = package + " pkg not installed\n"
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def checkAvailable(self, package):
        try:
            found = False
            retval = call(["/usr/bin/apt-cache", "search", package],
                          stdout=PIPE,
                          stderr=PIPE,
                          shell=False)
            if retval == 0:
                message = Popen(["/usr/bin/apt-cache", "search", package],
                                stdout=PIPE,
                                stderr=PIPE,
                                shell=False)
                info = message.stdout.readlines()
                while message.poll() is None:
                    continue
                message.stdout.close()
                for line in info:
                    if search(package, line):
                        found = True
                if found:
                    self.detailedresults = package + " pkg is available"
                else:
                    self.detailedresults = package + " pkg is not available"
            else:
                self.detailedresults = package + " pkg not found or may be \
misspelled"

            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise
###############################################################################

    def getPackageFromFile(self, filename):
        '''Returns the name of the package that provides the given
        filename/path.

        @param: string filename : The name or path of the file to resolve
        @return: string name of package if found, None otherwise
        @author: Eric Ball
        '''
        try:
            self.ch.executeCommand("dpkg -S " + filename)
            if self.ch.getReturnCode() == 0:
                output = self.ch.getOutputString()
                pkgname = output.split(":")[0]
                return pkgname
            else:
                return None
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def getInstall(self):
        return self.install
###############################################################################

    def getRemove(self):
        return self.remove
Ejemplo n.º 21
0
class ConfigureScreenLocking(RuleKVEditor):
    def __init__(self, config, environ, logdispatcher, statechglogger):
        RuleKVEditor.__init__(self, config, environ, logdispatcher,
                              statechglogger)
        self.logger = logdispatcher
        self.rulenumber = 74
        self.rulename = "ConfigureScreenLocking"
        self.mandatory = True
        self.rootrequired = False
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.effectiveUserID = self.environ.geteuid()
        self.sethelptext()
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 2.3.5.6.1"]
        if self.environ.getosfamily() == "darwin":
            if self.effectiveUserID == 0:
                self.addKVEditor(
                    "SystemAskForPasswordSystem", "defaults",
                    "/Library/Preferences/com.apple.screensaver", "",
                    {"askForPassword": ["1", "-int 1"]}, "present", "",
                    "Ask for password when system wide " +
                    "screen saver is on.", None, False,
                    {"askForPassword": ["0", "-int 0"]})
                self.addKVEditor(
                    "SystemSetScreenSaverIdleTime", "defaults",
                    "/Library/Preferences/com.apple.screensaver", "",
                    {"idleTime": ["840", "-int 840"]}, "present", "",
                    "Sets system the screen saver to " +
                    "activate after 14 minutes of idleTime.", None, False, {
                        "idleTime": [
                            "The domain/default pair of ( .+" +
                            "com\.apple\.screensaver, " +
                            "idleTime) does not " + "exist", None
                        ]
                    })
                self.addKVEditor(
                    "SystemLoginWindowIdleTime", "defaults",
                    "/Library/Preferences/com.apple.screensaver", "",
                    {"loginWindowIdleTime": ["840", "-int 840"]}, "present",
                    "", "Sets system LoginWindowIdleTime to " + "14 minutes.",
                    None, False, {
                        "loginWindowIdleTime": [
                            "The domain/default pair of ( .+" +
                            "com\.apple\.screensaver, " +
                            "loginWindowIdleTime) does not " + "exist", None
                        ]
                    })
            else:
                self.addKVEditor(
                    "AskForPassword", "defaults",
                    "~/Library/Preferences/com.apple.screensaver", "",
                    {"askForPassword": ["1", "-int 1"]}, "present", "",
                    "Ask for password when screen saver is on.", None, False,
                    {"askForPassword": ["0", "-int 0"]})
                self.addKVEditor(
                    "AskForPasswordDelay", "defaults",
                    "~/Library/Preferences/com.apple.screensaver", "",
                    {"askForPasswordDelay": ["0", "-int 0"]}, "present", "",
                    "Delay asking for password by 0 seconds.", None, False, {
                        "askForPasswordDelay": [
                            "The domain/default pair of ( .+" +
                            "com\.apple\.screensaver, " +
                            "askForPassword) does not " + "exist", None
                        ]
                    })
        else:

            datatype = 'bool'
            key = 'CONFIGURESCREENLOCKING'
            instructions = "To prevent the configuration of idle screen locking, set the value of CONFIGURESCREENLOCKING to False."
            default = True
            self.ci = self.initCi(datatype, key, instructions, default)

            #self.gnomeInst variable determines in the fixGnome method
            #at the beginning if we even proceed.  If False we don't proceed
            #and is fine.  Gets set to True in reportGnome method if
            #either gconftool-2 or gsettings binaries exist.
            self.gnomeInst = False
            self.useGconf = True
            self.iditerator = 0
            self.cmdhelper = CommandHelper(self.logger)
            self.ph = Pkghelper(self.logger, self.environ)
            self.ch = CommandHelper(self.logger)

            self.euid = self.environ.geteuid()

    def report(self):
        """ConfigureScreenLocking.report() method to report whether system
        is configured to screen locking NSA standards.  If the system is linux,
        although many desktops are available, this rule will only check the
        two most popular desktops, KDE, and Gnome.
        @author: dwalker

        :param self: essential if you override this definition
        :return: self.compliant
        :rtype: bool
        """

        self.detailedresults = ""
        self.compliant = True

        try:

            compliant = True
            self.detailedresults = ""
            if self.environ.osfamily == 'linux':
                if not self.check_package():
                    compliant = False
                    if self.euid != 0:
                        self.detailedresults += "\nThis is expected if not running with elevated privileges since STONIX " \
                                                "requires elevated privileges to install packages. Please run STONIX with elevated privileges " \
                                                "and run the fix for this rule again, to fix this issue."

                if self.ph.check("gdm") or self.ph.check("gdm3"):
                    self.gnomeInstalled = True
                    if not self.reportGnome():
                        self.detailedresults += "\nGnome GUI environment " + \
                                                "does not appear to be correctly configured " + \
                                                "for screen locking parameters."
                        compliant = False
                    else:
                        self.detailedresults += "\nGnome GUI environment " + \
                                                "appears to be correctly configured for " + \
                                                "screen locking parameters."
                else:
                    self.gnomeInstalled = False
                    self.detailedresults += "\nGnome not installed.  No need to configure for gnome."

                if self.ph.check("kdm") or self.ph.check("kde-workspace") or \
                                      self.ph.check("sddm") or self.ph.check("patterns-kde-kde_yast"):
                    self.kdeInstalled = True
                    if not self.reportKde():
                        self.detailedresults += "\nKDE GUI environment " + \
                                                "does not appear to be correctly configured " + \
                                                "for screen locking parameters."
                        compliant = False
                    else:
                        self.detailedresults += "\nKDE GUI environment " + \
                                                "appears to be correctly configured for " + \
                                                "screen locking parameters."
                else:
                    self.kdeInstalled = False
                    self.detailedresults += "\nKDE not installed.  No need to configure for kde."

            elif self.environ.getosfamily() == "darwin":
                compliant = self.reportMac()

            self.compliant = compliant

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def check_package(self):
        """
        for rhel 7 and similar generation linux rpm-based systems, the
        'screen' package is required by the STIG
        for rhel 8 and beyond (and similar), the 'tmux' package
        is required.

        :return: installed
        :rtype: bool
        """

        self.screen_pkg = ""
        installed = True

        if self.ph.checkAvailable("tmux"):
            self.screen_pkg = "tmux"
        elif self.ph.check("tmux"):
            self.screen_pkg = "tmux"
        else:
            self.screen_pkg = "screen"

        if not self.ph.check(self.screen_pkg):
            self.detailedresults += "\nThe required package: " + str(
                self.screen_pkg) + " is not installed"
            installed = False
        else:
            self.detailedresults += "\nThe required package: " + str(
                self.screen_pkg) + " is installed"

        return installed

    def reportMac(self):
        """Mac osx specific report submethod
        
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool - True if system is compliant, False if it isn't

        """
        success = RuleKVEditor.report(self, True)
        return success

    def reportGnome(self):
        """determines if gnome is installed, if so, checks to see if the
        return value strings from running the gconftool-2 command are
        correct.  Gconftool-2 command only works in root mode so if not root
        do not audit gnome and just return true
        
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool
        @change: dwalker - mass refactor, added comments

        """

        compliant = True
        gsettings = "/usr/bin/gsettings"
        gconf = "/usr/bin/gconftool-2"
        self.gesttingsidletime = ""
        self.gconfidletime = ""
        self.setcmds = ""
        #may need to change code in the future to make gconf and
        #gsettings mutually exclusive with if elif else self.gnomeInstalled = False
        #if they are found to conflict with each other
        if os.path.exists(gconf):
            getcmds = {
                "/apps/gnome-screensaver/idle_activation_enabled": "true",
                "/apps/gnome-screensaver/lock_enabled": "true",
                "/apps/gnome-screensaver/mode": "blank-only",
                "/desktop/gnome/session/idle_delay": "15"
            }
            #make a copy of the getcmds dictionary
            tempdict = dict(getcmds)
            #check each value using the gconftool-2 command
            for cmd in getcmds:
                #set the get command for each value
                cmd2 = gconf + " --get " + cmd
                #execute the command
                self.cmdhelper.executeCommand(cmd2)
                #get output and error output
                output = self.cmdhelper.getOutput()
                error = self.cmdhelper.getError()
                #the value is set
                if output:
                    #check this one separately since the value is
                    #also able to be less than 15 mins and be compliant
                    if cmd == "/desktop/gnome/session/idle_delay":
                        if int(output[0].strip()) > 15:
                            self.detailedresults += "Idle delay value " + \
                                "is not 15 or lower (value: " + \
                                output[0].strip() + ")\n"
                        #if they have a value less than 15 mins this is ok
                        #and we set the self.gconfidletime variable which is
                        #used during setting gconf values in the fixGnome method
                        elif int(output[0].strip()) < 15:
                            self.gconfidletime = output[0].strip()
                            del tempdict[cmd]
                        #value is correct so remove it from the tempdic
                        else:
                            del tempdict[cmd]
                    #check if value is correct with associated key
                    elif output[0].strip() != getcmds[cmd]:
                        self.detailedresults += cmd2 + " didn't produce the " + \
                            "desired value after being run which is " + \
                            getcmds[cmd] + "\n"
                    #value is correct so remove it from the tempdict
                    else:
                        del tempdict[cmd]
                #else the value isn't set
                elif error:
                    self.detailedresults += "There is no value set for:\n" + \
                                            cmd2 + "\n"
            #if tempdict still has leftover values then
            #there were values that weren't correctly set
            #and is non compliant
            if tempdict:
                compliant = False
                #set self.setcmds variable to be a copy of tempdict which
                #we use later in the fix to determine if we fix this portion
                #of the rule or not
                self.setcmds = tempdict
        if os.path.exists(gsettings):
            self.gnomeInst = True
            #use gsettings command to see if the correct values are set
            #for each key in getcmds dictionary
            getcmds = {
                " get org.gnome.desktop.screensaver " + "idle-activation-enabled":
                "true",
                " get org.gnome.desktop.screensaver lock-enabled": "true",
                " get org.gnome.desktop.screensaver lock-delay": "0",
                " get org.gnome.desktop.screensaver picture-opacity": "100",
                " get org.gnome.desktop.screensaver picture-uri": "''",
                " get org.gnome.desktop.session idle-delay": "900"
            }
            #run each gsettings get command for each key and get value
            for cmd in getcmds:
                cmd2 = gsettings + cmd
                self.cmdhelper.executeCommand(cmd2)
                output = self.cmdhelper.getOutput()
                error = self.cmdhelper.getError()
                if output:
                    #check this one separately since the value is
                    #also able to be less than 900 secs and be compliant
                    if cmd == " get org.gnome.desktop.session idle-delay":
                        try:
                            splitOut = output[0].split()
                            if len(splitOut) > 1:
                                num = splitOut[1]
                            else:
                                num = splitOut[0]
                            if int(num) > 900:
                                compliant = False
                                self.detailedresults += "Idle delay value " + \
                                    "is not 900 seconds (value: " +\
                                    num + ")\n"
                            #if they have a value less than 900 secs this is ok
                            #and we set the self.gsettingsidletime variable which is
                            #used during setting gsettings values in the fixGnome method
                            # elif int(num) < 900:
                            #     self.gsettingsidletime = num
                            elif int(num) == 0:
                                compliant = False
                                self.detailedresults += "Idle delay set  " + \
                                    "to 0, meaning it is disabled.\n"
                            else:
                                self.gsettingsidletime = "900"
                        except ValueError:
                            self.detailedresults += "Unexpected result: " + \
                                '"' + cmd2 + '" output was not a number\n'
                            compliant = False
                    elif cmd == " get org.gnome.desktop.screensaver lock-delay":
                        try:
                            splitOut = output[0].split()
                            if len(splitOut) > 1:
                                num = splitOut[1]
                            else:
                                num = splitOut[0]
                            if int(num) != 0:
                                compliant = False
                                self.detailedresults += "Lock delay is not " + \
                                    "set to 0\n"
                        except ValueError:
                            self.detailedresults += "Unexpected result: " + \
                                '"' + cmd2 + '" output was not a number\n'
                            compliant = False
                    elif output[0].strip() != getcmds[cmd]:
                        self.detailedresults += '"' + cmd2 + \
                        "\" produced value: " + output[0].strip() + \
                        " instead of the desired value: " + getcmds[cmd] + "\n"
                        compliant = False
                elif error:
                    if re.search("No such key", error[0], re.I):
                        continue
                    self.detailedresults += "There is no value set for:" + \
                        cmd2 + "\n"
                    compliant = False
            if self.environ.geteuid() == 0:
                # instantiate a kveditor to ensure self.dconfsettings file
                # contains correct contents
                self.dconfsettings = "/etc/dconf/db/local.d/local.key"
                if os.path.exists(self.dconfsettings):
                    self.dconfdata = {
                        "org/gnome/desktop/screensaver": {
                            "idle-activation-enabled": "true",
                            "lock-enabled": "true",
                            "lock-delay": "0",
                            "picture-opacity": "100",
                            "picture-uri": "\'\'"
                        },
                        "org/gnome/desktop/session": {
                            "idle-delay": "uint32 900"
                        }
                    }
                    self.kveditordconf = KVEditorStonix(
                        self.statechglogger, self.logger, "tagconf",
                        self.dconfsettings, self.dconfsettings + ".tmp",
                        self.dconfdata, "present", "closedeq")
                    if not self.kveditordconf.report():
                        self.detailedresults += self.dconfsettings + \
                                                " doesn't cotain correct contents\n"
                        compliant = False
                else:
                    compliant = False
                    self.detailedresults += self.dconfsettings + " not found\n"

                # check self.dconfuserprofile file to ensure existence
                # and/or correct contents
                self.dconfuserprofile = "/etc/dconf/profile/user"
                self.userprofilecontent = "user-db:user\n" + \
                                          "system-db:local"
                if os.path.exists(self.dconfuserprofile):
                    contents = readFile(self.dconfuserprofile, self.logger)
                    contentstring = ""
                    for line in contents:
                        contentstring += line
                    if not re.search(self.userprofilecontent, contentstring):
                        compliant = False
                        self.detailedresults += "Correct contents weren't " + \
                                                "found in " + self.dconfuserprofile + "\n"
                else:
                    compliant = False
                    self.detailedresults += self.dconfuserprofile + " not " + \
                                            "found\n"

                # the following file locks the settings we earlier set with the
                # gsettings command so that they don't default upon logout and/or
                # reboot.  Check the file for the correct contents
                self.dconfsettingslock = "/etc/dconf/db/local.d/locks/stonix-settings.conf"
                self.dconflockdata = [
                    "/org/gnome/desktop/session/idle-delay",
                    "/org/gnome/desktop/screensaver/idle-activation-enabled",
                    "/org/gnome/desktop/screensaver/lock-enabled",
                    "/org/gnome/desktop/screensaver/lock-delay",
                    "/org/gnome/desktop/screensaver/picture-uri"
                ]

                locks_missing = []
                if os.path.exists(self.dconfsettingslock):
                    contents = readFile(self.dconfsettingslock, self.logger)
                    for line in contents:
                        if line.strip() not in self.dconflockdata:
                            locks_missing.append(line.strip())

                    if locks_missing:
                        self.detailedresults += "\nThe following settings should be locked from changes by the user but aren't:\n" + "\n".join(
                            locks_missing)
                        compliant = False
                else:
                    compliant = False
                    self.detailedresults += "\nGnome settings lock file not found"

        return compliant

    def reportKde(self):
        """determines if kde is installed, if so, ensures kde is configured
        by enabling screenlocking, automatically going black after 14 minutes
        and if inactivity ensues after 14 minutes, screen fully locks after 1
        additional minute of inactivity for a total of 15 minutes activity
        
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool

        """
        self.kdefix = {}
        if self.ph.manager == "apt-get" or self.ph.manager == "zypper":
            self.rcpath = ".config/kscreenlockerrc"
            self.kdeprops = {
                "Daemon": {
                    "Autolock": "true",
                    "LockGrace": "60",
                    "LockOnResume": "true",
                    "Timeout": "14"
                }
            }
        else:
            self.rcpath = ".kde/share/config/kscreensaverrc"
            self.kdeprops = {
                "ScreenSaver": {
                    "Enabled": "true",
                    "Lock": "true",
                    "LockGrace": "60000",
                    "Timeout": "840"
                }
            }
        if self.environ.geteuid() == 0:
            contents = readFile("/etc/passwd", self.logger)
            for line in contents:
                temp = line.split(":")
                try:
                    username = temp[0]
                    homepath = temp[5]
                except IndexError:
                    self.logdispatch.log(LogPriority.DEBUG, [
                        'ConfigureScreenLocking',
                        'IndexError processing ' + str(temp)
                    ])
                    continue
                kdeparent1 = os.path.join(homepath, ".kde")
                kdeparent2 = os.path.join(homepath, ".kde4")
                kdefile = os.path.join(homepath, self.rcpath)
                if not os.path.exists(kdeparent1) and not os.path.exists(
                        kdeparent2):
                    # User does not user KDE
                    continue
                elif not os.path.exists(kdefile):
                    self.kdefix[username] = homepath
                    self.detailedresults += kdefile + " not found for " + \
                        str(username) + "\n"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    continue
                elif not self.searchFile(kdefile):
                    self.detailedresults += "Did not find " + \
                        "required contents " + "in " + username + \
                        "'s " + kdefile + "\n"
                    self.kdefix[username] = homepath
            if self.kdefix:
                self.detailedresults += "The following users don't " + \
                                        "have kde properly configured for " + \
                                        "screen locking:\n"
                for user in self.kdefix:
                    self.detailedresults += user + "\n"
                return False
            else:
                return True
        else:
            kdeparent1 = os.path.join(self.environ.geteuidhome(), ".kde")
            kdeparent2 = os.path.join(self.environ.geteuidhome(), ".kde4")
            kdefile = os.path.join(self.environ.geteuidhome(), self.rcpath)
            if not os.path.exists(kdeparent1) and not os.path.exists(
                    kdeparent2):
                self.detailedresults += "Current user doesn't use kde.  " + \
                    "No need to configure.\n"
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                return True
            else:
                if not os.path.exists(kdefile):
                    self.detailedresults += "Your " + kdefile + \
                        " file doesn't exist.\n"
                    return False
                elif not self.searchFile(kdefile):
                    self.detailedresults += "Did not find " + \
                        "required contents in " + kdefile + "\n"
                    return False
                else:
                    return True

    def fix(self):
        """ConfigureScreenLocking.fix() method to correct screen locking
        
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool - True if fix is successful, False if it isn't

        """

        self.detailedresults = ""
        self.rulesuccess = True
        self.iditerator = 0

        try:
            self.detailedresults = ""
            success = True
            if self.environ.getosfamily() == "linux":
                if not self.ci.getcurrvalue():
                    self.detailedresults += "Rule not enabled so nothing was done\n"
                    self.logger.log(
                        LogPriority.DEBUG,
                        'Rule was not enabled, so nothing was done')
                    return
                if self.euid == 0:
                    if not self.ph.install(self.screen_pkg):
                        success = False
                        self.logger.log(
                            LogPriority.DEBUG,
                            "Failed to install required package 'screen'")
                else:
                    self.detailedresults += "\nNote: Some required packages may not be installed because STONIX is not running with elevated privileges."
                if self.gnomeInstalled:
                    if not self.fixGnome():
                        success = False
                if self.kdeInstalled:
                    if not self.fixKde():
                        success = False
            elif self.environ.getosfamily() == "darwin":
                if self.environ.geteuid() == 0:
                    self.iditerator = 0
                    eventlist = self.statechglogger.findrulechanges(
                        self.rulenumber)
                    for event in eventlist:
                        self.statechglogger.deleteentry(event)
                success = self.fixMac()
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += "\n" + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess

    def fixKde(self):
        """This method checks if the kde screenlock file is configured
        properly.  Please note, this rule may fail if the owner and group of
        configuration file are not that of the user in question but doesn't
        necessarily mean your system is out of compliance.  If the fix fails
        Please check the logs to determing the real reason of non rule success.
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool - True if KDE is successfully configured, False if it
                isn't

        """

        success = True
        if self.environ.geteuid() == 0:
            self.logdispatch.log(LogPriority.DEBUG,
                                 'ConfigureScreenLocking.fixKde')
            if not self.kdefix:
                return True
            for user in self.kdefix:
                homepath = self.kdefix[user]
                kdefile = os.path.join(homepath, self.rcpath)
                if not self.correctFile(kdefile, user):
                    success = False
                    self.detailedresults += "Unable to configure " + \
                                            kdefile + "\n"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
        else:
            username = ""
            homepath = self.environ.geteuidhome()
            kdefile = os.path.join(homepath, self.rcpath)
            uidnum = int(self.environ.geteuid())
            passwddata = readFile("/etc/passwd", self.logger)
            found = False
            for user in passwddata:
                user = user.split(':')
                try:
                    puidnum = int(user[2])

                    if puidnum == uidnum:
                        username = user[0]
                        found = True
                except IndexError:
                    continue

            if not found:
                self.detailedresults += "Could not obtain your user id.\n" + \
                                        "Stonix couldn't proceed with correcting " + kdefile + "\n"
                success = False

            elif not self.correctFile(kdefile, username):

                self.detailedresults += "Stonix couldn't correct the contents " + \
                                        " of " + kdefile + "\n"
                success = False
        return success

    def fixGnome(self):
        """ensures gnome is configured to automatically screen lock after
        15 minutes of inactivity, if gnome is installed
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool - True if gnome is successfully configured, False if it
                isn't

        """
        info = ""
        success = True
        gconf = "/usr/bin/gconftool-2"
        gsettings = "/usr/bin/gsettings"
        if os.path.exists(gconf):
            #variable self.setcmds still has items left in its dictionary
            #which was set in the reportGnome method, meaning some values
            #either were incorrect or didn't have values.  Go through and
            #set each remaining value that isn't correct

            cmd = ""

            if self.setcmds:
                for item in self.setcmds:
                    if item == "/apps/gnome-screensaver/idle_activation_enabled":
                        cmd = gconf + " --type bool --set /apps/gnome-screensaver/idle_activation_enabled true"
                    elif item == "/apps/gnome-screensaver/lock_enabled":
                        cmd = gconf + " --type bool --set /apps/gnome-screensaver/lock_enabled true"
                    elif item == "/apps/gnome-screensaver/mode":
                        cmd = gconf + ' --type string --set /apps/gnome-screensaver/mode "blank-only"'
                    elif item == "/desktop/gnome/session/idle_delay":
                        if self.gconfidletime:
                            cmd = gconf + " --type int --set /desktop/gnome/session/idle_delay " + \
                                self.gconfidletime
                        else:
                            cmd = gconf + " --type int --set /desktop/gnome/session/idle_delay 15"
                    if self.cmdhelper.executeCommand(cmd):
                        if self.cmdhelper.getReturnCode() != 0:
                            info += "Unable to set value for " + cmd + "\n"
                            success = False
                    else:
                        info += "Unable to set value for " + cmd + "\n"
                        success = False

        if os.path.exists(gsettings):
            setcmds = [
                "org.gnome.desktop.screensaver idle-activation-enabled true",
                "org.gnome.desktop.screensaver lock-enabled true",
                "org.gnome.desktop.screensaver lock-delay 0",
                "org.gnome.desktop.screensaver picture-opacity 100",
                "org.gnome.desktop.screensaver picture-uri ''",
                "org.gnome.desktop.session idle-delay 900"
            ]
            # " set org.gnome.desktop.session idle-delay " + self.gsettingsidletime]
            for cmd in setcmds:
                cmd2 = gsettings + " set " + cmd
                self.cmdhelper.executeCommand(cmd2)
                if self.cmdhelper.getReturnCode() != 0:
                    success = False
                    info += "Unable to set value for " + cmd + \
                        " using gsettings\n"

            # Set gsettings with dconf
            # Unlock dconf settings
            # Create dconf settings lock file
            if self.environ.geteuid() == 0:
                if not os.path.exists(self.dconfsettingslock):
                    if not createFile(self.dconfsettingslock, self.logger):
                        self.rulesuccess = False
                        self.detailedresults += "Unable to create stonix-settings file\n"
                        self.formatDetailedResults("fix", self.rulesuccess,
                                                   self.detailedresults)
                        return False
                #write correct contents to dconf lock file
                if os.path.exists(self.dconfsettingslock):
                    # Write to the lock file
                    if self.dconflockdata:
                        contents = ""
                        tmpfile = self.dconfsettingslock + ".tmp"
                        for line in self.dconflockdata:
                            contents += line + "\n"
                        if not writeFile(tmpfile, contents, self.logger):
                            self.rulesuccess = False
                            self.detailedresults += "Unable to write contents to " + \
                                "stonix-settings file\n"
                        else:
                            os.rename(tmpfile, self.dconfsettingslock)
                            os.chown(self.dconfsettingslock, 0, 0)
                            os.chmod(self.dconfsettingslock, 0o644)
                            resetsecon(self.dconfsettingslock)
                # Create dconf user profile file
                if not os.path.exists(self.dconfuserprofile):
                    if not createFile(self.dconfuserprofile, self.logger):
                        self.rulesuccess = False
                        self.detailedresults += "Unable to create dconf " + \
                                                "user profile file\n"
                        self.formatDetailedResults("fix", self.rulesuccess,
                                                   self.detailedresults)
                        return False
                # Write dconf user profile
                if os.path.exists(self.dconfuserprofile):
                    tmpfile = self.dconfuserprofile + ".tmp"
                    if not writeFile(tmpfile, self.userprofilecontent,
                                     self.logger):
                        self.rulesuccess = False
                        self.detailedresults += "Unabled to write to dconf user" + \
                            " profile file\n"
                        self.formatDetailedResults("fix", self.rulesuccess,
                                                   self.detailedresults)
                        return False
                    else:
                        os.rename(tmpfile, self.dconfuserprofile)
                        os.chown(self.dconfuserprofile, 0, 0)
                        os.chmod(self.dconfuserprofile, 0o644)
                        resetsecon(self.dconfuserprofile)
                # Fix dconf settings
                if not os.path.exists(self.dconfsettings):
                    if not createFile(self.dconfsettings, self.logger):
                        self.rulesuccess = False
                        self.detailedresults += "Unable to create " + self.dconfsettings + " file \n"
                        self.formatDetailedResults("fix", self.rulesuccess,
                                                   self.detailedresults)
                        return False
                    self.dconfdata = {
                        "org/gnome/desktop/screensaver": {
                            "idle-activation-enabled": "true",
                            "lock-enabled": "true",
                            "lock-delay": "0",
                            "picture-opacity": "100",
                            "picture-uri": "\'\'"
                        },
                        "org/gnome/desktop/session": {
                            "idle-delay": "uint32 900"
                        }
                    }
                    self.kveditordconf = KVEditorStonix(
                        self.statechglogger, self.logger, "tagconf",
                        self.dconfsettings, self.dconfsettings + ".tmp",
                        self.dconfdata, "present", "closedeq")
                    self.kveditordconf.report()
                if self.kveditordconf.fixables:
                    if not self.kveditordconf.fix():
                        success = False
                        self.detailedresults += "Unable to put correct settings inside " + \
                            self.dconfsettings + "\n"
                    elif not self.kveditordconf.commit():
                        success = False
                        self.detailedresults += "Unable to put correct settings inside " + \
                            self.dconfsettings + "\n"
                #run dconf update command to make dconf changes take effect
                if os.path.exists("/bin/dconf"):
                    cmd = "/bin/dconf update"
                    self.cmdhelper.executeCommand(cmd)
                elif os.path.exists("/usr/bin/dconf"):
                    cmd = "/usr/bin/dconf update"
                    self.cmdhelper.executeCommand(cmd)
        self.detailedresults += info
        return success

    def fixMac(self):
        """Mac osx specific fix submethod
        
        @author: dwalker

        :param self: essential if you override this definition
        :returns: bool - True if system is successfully fix, False if it isn't

        """
        success = RuleKVEditor.fix(self, True)
        return success

    def searchFile(self, filehandle):
        """temporary method to separate the code to find directives from the
        rest of the code.  Will put back all in one method eventually
        @author: dwalker

        :param filehandle: string
        :returns: bool

        """
        self.editor = ""
        kvt = "tagconf"
        intent = "present"
        tpath = filehandle + ".tmp"
        conftype = "closedeq"
        self.editor = KVEditorStonix(self.statechglogger, self.logger, kvt,
                                     filehandle, tpath, self.kdeprops, intent,
                                     conftype)
        if not self.editor.report():
            return False
        else:
            return True

    def correctFile(self, kfile, user):
        """separate method to find the correct contents of each file passed in
        as a parameter.
        @author: dwalker

        :param kfile: 
        :param user: 
        :returns: bool

        """
        success = True

        if not os.path.exists(kfile):
            if not createFile(kfile, self.logger):
                self.detailedresults += "Unable to create " + kfile + \
                    " file for the user\n"
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                return False
        if not self.searchFile(kfile):
            if self.editor.fixables:
                if not self.editor.fix():
                    debug = "Kveditor fix is failing for file " + \
                        kfile + "\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    self.detailedresults += "Unable to correct contents for " + \
                        kfile + "\n"
                    return False
                elif not self.editor.commit():
                    debug = "Kveditor commit is failing for file " + \
                            kfile + "\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    self.detailedresults += "Unable to correct contents for " + \
                               kfile + "\n"
                    return False
        uid = getpwnam(user)[2]
        gid = getpwnam(user)[3]

        if uid != "" and gid != "":
            os.chmod(kfile, 0o600)
            os.chown(kfile, uid, gid)
            resetsecon(kfile)
        else:
            success = False
            self.detailedresults += "Unable to obtain uid and gid of " + user + "\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)

        return success

    def undo(self):
        self.detailedresults += "This rule cannot be reverted\n"
        self.rulesuccess = False
        self.formatDetailedResults("undo", self.rulesuccess,
                                   self.detailedresults)
Ejemplo n.º 22
0
class BlockSystemAccounts(Rule):
    """this module ensures that no system accounts have a login shell"""

    def __init__(self, config, enviro, logger, statechglogger):
        """
        private method to initialize this module

        :param config: configuration object instance
        :param enviro: environment object instance
        :param logger: logdispatcher object instance
        :param statechglogger: statechglogger object instance
        """

        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.environ = enviro
        self.rulenumber = 40
        self.rulename = 'BlockSystemAccounts'
        self.formatDetailedResults("initialize")
        self.compliant = False
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        datatype = 'bool'
        key = 'BLOCKSYSACCOUNTS'
        instructions = """If you have system accounts that need to have valid \
shells set the value of this to False, or No."""
        default = True
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.ci = self.initCi(datatype, key, instructions,
                                               default)
        self.guidance = ['CIS', 'NSA(2.3.1.4)', 'cce-3987-5', '4525-2',
                         '4657-3', '4661-5', '4807-4', '4701-9', '4669-8',
                         '4436-2', '4815-7', '4696-1', '4216-8', '4758-9',
                         '4621-9', '4515-3', '4282-0', '4802-5', '4806-6',
                         '4471-9', '4617-7', '4418-0', '4810-8', '3955-2',
                         '3834-9', '4408-1', '4536-9', '4809-0', '3841-4']
        self.iditerator = 0

    def report(self):
        """report on the status of the system's compliance with disallowing
        system accounts to log in

        :return: self.compliant - boolean; True if compliant, False if not

        """

        self.detailedresults = ""
        self.compliant = True
        acceptable_nologin_shells = ["/sbin/nologin", "/dev/null", "", "/usr/bin/false"]
        self.ch = CommandHelper(self.logger)
        self.corrections_needed = []

        try:

            system_login_shells = self.getsysloginshells()
            for acc in system_login_shells:
                if system_login_shells[acc] not in acceptable_nologin_shells:
                    self.compliant = False
                    self.corrections_needed.append(acc)
            if self.corrections_needed:
                self.detailedresults += "\nThe following system accounts can log in:\n" + "\n".join(self.corrections_needed)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def getUIDMIN(self):
        """return this system's minimum user ID start value, if configured

        :return: uid_min - string; system's user id starting value

        """

        uid_min = ""
        logindefs = "/etc/login.defs"

        try:

            # get normal user uid start value
            logindefscontents = readFile(logindefs, self.logger)
            if logindefscontents:
                for line in logindefscontents:
                    if re.search("^UID_MIN", line, re.IGNORECASE):
                        sline = line.split()
                        uid_min = sline[1]

            if not uid_min:
                self.logger.log(LogPriority.DEBUG, "Unable to determine UID_MIN")

        except IndexError:
            pass
        except IOError:
            self.logger.log(LogPriority.DEBUG, "Failed to read uid_min from file")
            return uid_min

        return uid_min

    def getsystemaccounts(self):
        """
        return a list of system accounts

        :return: system_accounts_list - list of system accounts

        """

        system_accounts_list = []

        if self.environ.getosfamily() == "darwin":
            try:
                system_accounts_list = ["root", "nobody"]
                get_sys_accounts_cmd = "/usr/bin/dscl . list /Users | grep -i _"
                self.ch.executeCommand(get_sys_accounts_cmd)
                system_accounts_list += self.ch.getOutput()
            except OSError:
                self.logger.log(LogPriority.DEBUG, "Failed to retrieve list of system accounts")
                return system_accounts_list
        else:
            exclude_accounts = ["halt", "shutdown", "sync", "root"]
            system_accounts_list = []
            uid_min = self.getUIDMIN()
            if not uid_min:
                uid_min = "500"
            f = open("/etc/passwd", "r")
            contentlines = f.readlines()
            f.close()

            try:

                for line in contentlines:
                    sline = line.split(":")
                    if int(sline[2]) < int(uid_min):
                        if sline[0] not in exclude_accounts:
                            system_accounts_list.append(sline[0])

            except IndexError:
                pass

        return system_accounts_list

    def getloginshell(self, account):
        """
        retrieve the login shell, of the passed account, from passwd

        :param account: string; name of user account to get info for
        :return: loginshell - string; default login shell path for account
        """

        loginshell = ""

        try:
            f = open("/etc/passwd", "r")
            contentlines = f.readlines()
            f.close()
        except IOError:
            self.logger.log(LogPriority.DEBUG, "Could not read from passwd file")
            return loginshell

        try:

            for line in contentlines:
                if re.search("^"+account, line, re.IGNORECASE):
                    sline = line.split(":")
                    loginshell = sline[6]

        except IndexError:
            pass

        return loginshell

    def getsysloginshells(self):
        """
        return a dictionary of system accounts and their login shells

        :return: system_login_shells - dictionary of system accounts and their login shells
        """

        system_login_shells = {}
        system_accounts = self.getsystemaccounts()
        for acc in system_accounts:

            system_login_shells[acc] = self.getloginshell(acc).strip()

        return system_login_shells

    def setdefaultloginshell(self, account, shell):
        """
        set default shell for given user account

        :param account: string; name of user account to set default shell for
        :param shell: the type of shell to set for the given user account

        """

        change_shell_cmd = "/usr/bin/chsh -s " + shell + " " + account
        self.ch.executeCommand(change_shell_cmd)

    def fix(self):
        """The fix method will apply the required settings to the system.
        self.rulesuccess will be updated if the rule does not succeed.

        :return: self.rulesuccess - boolean; True if fix succeeds, False if not

        """

        self.detailedresults = ""
        self.rulesuccess = True
        path = "/etc/passwd"
        tmppath = path + ".stonixtmp"
        self.iditerator = 0
        newcontentlines = []

        try:

            if not self.ci.getcurrvalue():
                return self.rulesuccess

            f = open(path, "r")
            contentlines = f.readlines()
            f.close()

            for line in contentlines:
                sline = line.split(":")
                if sline[0] in self.corrections_needed:
                    sline[6] = "/sbin/nologin\n"
                line = ":".join(sline)
                newcontentlines.append(line)

            tf = open(tmppath, "w")
            tf.writelines(newcontentlines)

            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'conf',
                     'filepath': path}

            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(path, tmppath, myid)

            os.rename(tmppath, path)
            os.chown(path, 0, 0)
            os.chmod(path, 420)
            resetsecon(path)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess
Ejemplo n.º 23
0
class SecureHomeDir(Rule):
    '''classdocs'''

    def __init__(self, config, environ, logger, statechglogger):
        '''

        @param config:
        @param environ:
        @param logger:
        @param statechglogger:
        '''

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 45
        self.rulename = "SecureHomeDir"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = False
        datatype = 'bool'
        key = 'SECUREHOME'
        instructions = '''To disable this rule set the value of SECUREHOME to False.'''
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.guidance = ['NSA 2.3.4.2']
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.sethelptext()

    def report(self):
        '''report the compliance status of the permissions on all local user
        home directories


        :returns: self.compliant

        :rtype: bool
@author: Derek Walker

        '''

        self.detailedresults = ""
        self.compliant = True
        self.cmdhelper = CommandHelper(self.logger)
        self.WRHomeDirs = []
        self.GWHomeDirs = []

        try:

            if self.environ.getostype() == "Mac OS X":
                self.compliant = self.reportMac()
            else:
                self.compliant = self.reportLinux()

            if not self.WRHomeDirs:
                self.detailedresults += "\nNo world readable home directories found."
            if not self.GWHomeDirs:
                self.detailedresults += "\nNo group writeable home directories found."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def reportMac(self):
        '''check all user local home directories, on Mac OS X, for correct permissions


        :returns: compliant

        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 10/13/2015 - moved grpvals variable up to top where it should be; fixed logging;
                                        will no longer report on /var/empty or /dev/null permissions

        '''

        compliant = True

        try:

            if self.environ.geteuid() == 0:
                # running as root/admin
                homedirs = self.getMacHomeDirs()

                if homedirs:

                    self.logger.log(LogPriority.DEBUG, "Scanning home directories...")
                    for hd in homedirs:
                        if not os.path.exists(hd):
                            self.logger.log(LogPriority.DEBUG, "Skipping directory " + hd + " because it does not exist...")
                            continue

                        self.logger.log(LogPriority.DEBUG, "Checking " + hd)
                        if self.isGW(hd):
                            compliant = False
                            self.detailedresults += "\nThe home directory: " + str(hd) + " is group-writeable"
                            self.GWHomeDirs.append(hd)
                        if self.isWR(hd):
                            compliant = False
                            self.detailedresults += "\nThe home directory: " + str(hd) + " is world-readable"
                            self.WRHomeDirs.append(hd)
                else:
                    self.logger.log(LogPriority.DEBUG, "No home directories found!")
            else:
                # running as a normal user
                homedir = self.getMyHomeDir()
                if os.path.exists(homedir):
                    if self.isGW(homedir):
                        compliant = False
                        self.detailedresults += "\nThe home directory: " + str(homedir) + " is group-writeable"
                        self.GWHomeDirs.append(homedir)
                    if self.isWR(homedir):
                        compliant = False
                        self.detailedresults += "\nThe home directory: " + str(homedir) + " is world-readable"
                        self.WRHomeDirs.append(homedir)
                else:
                    self.logger.log(LogPriority.DEBUG, "Skipping directory " + homedir + " because it does not exist...")

        except Exception:
            raise

        return compliant

    def isGW(self, path):
        '''determine if a given path is group writeable

        :param path: string; absolute file path to scan
        :returns: groupwriteable
        :rtype: bool
@author: Breen Malmberg

        '''

        groupwriteable = False

        try:

            mode = os.stat(path).st_mode
            groupwriteable = bool(mode & stat.S_IWGRP)

        except Exception:
            raise

        return groupwriteable

    def isWR(self, path):
        '''determine if a given path is world readable

        :param path: string; absolute file path to scan
        :returns: worldreadable
        :rtype: bool
@author: Breen Malmberg

        '''

        worldreadable = False

        try:

            mode = os.stat(path).st_mode
            worldreadable = bool(mode & stat.S_IROTH)

        except Exception:
            raise

        return worldreadable

    def getMacHomeDirs(self):
        '''get a list of user home directories on the Mac


        :returns: homedirs

        :rtype: list
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG, "Building list of Mac local user home directories...")

        HomeDirs = []
        getAccountsList = ["/usr/bin/dscl", ".", "list", "/Users"]
        UsersList = []

        try:

            self.cmdhelper.executeCommand(getAccountsList)

            retcode = self.cmdhelper.getReturnCode()
            if retcode != 0:
                errstr = self.cmdhelper.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
                return HomeDirs

            AccountsList = self.cmdhelper.getOutput()

            # filter out any system accounts
            # (we want only user accounts)
            if AccountsList:
                for acc in AccountsList:
                    if not re.search("^_", acc, re.IGNORECASE) and not re.search("^root", acc, re.IGNORECASE):
                        UsersList.append(acc)

            if UsersList:
                for u in UsersList:
                    try:
                        currpwd = pwd.getpwnam(u)
                    except KeyError:
                        UsersList.remove(u)
                        continue
                    HomeDirs.append(currpwd[5])

            if not HomeDirs:
                self.logger.log(LogPriority.DEBUG, "No home directories found")
            else:
                HomeDirs = self.validateHomedirs(HomeDirs)

        except Exception:
            raise

        return HomeDirs

    def getLinuxHomeDirs(self):
        '''get a list of user home directories on Linux platforms


        :returns: homedirs

        :rtype: list
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG, "Building list of Linux user home directories...")

        HomeDirs = []
        awk = "/usr/bin/awk"
        passwd = "/etc/passwd"
        invalidshells = ["/sbin/nologin", "/sbin/halt", "/sbin/shutdown", "/dev/null", "/bin/sync"]
        getacctscmd = awk + " -F: '{ print $1 }' " + passwd
        acctnames = []
        usernames = []

        try:

            # establish the minimum user id on this system
            uid_min = self.getUIDMIN()
            if not uid_min:
                uid_min = "500"

            if os.path.exists(awk):
                # build a list of user (non-system) account names
                self.cmdhelper.executeCommand(getacctscmd)
                acctnames = self.cmdhelper.getOutput()
            else:
                self.logger.log(LogPriority.DEBUG, "awk utility not installed! What kind of Linux are you running??")
                # alternate method of getting account names in case system
                # does not have the awk utility installed..
                f = open(passwd, 'r')
                contents = f.readlines()
                f.close()

                for line in contents:
                    sline = line.split(':')
                    if len(sline) > 1:
                        acctnames.append(sline[0])

            if acctnames:
                for an in acctnames:
                    if int(pwd.getpwnam(an).pw_uid) >= int(uid_min):
                        usernames.append(an)
            else:
                self.logger.log(LogPriority.DEBUG, "Could not find any accounts on this system!")
                return HomeDirs

            # further check to see if this might still be a system account
            # which just got added in the user id range somehow (by checking
            # the shell)
            for un in usernames:
                if pwd.getpwnam(un).pw_shell in invalidshells:
                    usernames.remove(un)

            for un in usernames:
                HomeDirs.append(pwd.getpwnam(un).pw_dir)
            # now we should be reasonably certain that the list we have are all
            # valid users (and not system accounts) but let's do one more check
            # to make sure they weren't assigned a home directory some where that
            # we don't want to modify (ex. etc or /root)
            HomeDirs = self.validateHomedirs(HomeDirs)

            if not HomeDirs:
                self.logger.log(LogPriority.DEBUG, "No home directories found")
            else:
                HomeDirs = self.validateHomedirs(HomeDirs)

        except Exception:
            raise

        return HomeDirs

    def validateHomedirs(self, dirs):
        '''strip out common system (and non-existent) directories from the given list of dirs
        and return the resultant list

        :param dirs: list; list of strings containing directories to search
        and modify
        :returns: validateddirs
        :rtype: list

@author: Breen Malmberg

        '''

        validateddirs = []
        systemdirs = ['/tmp', '/var', '/temp', '/', '/bin', '/sbin', '/etc', '/dev', '/root']

        self.logger.log(LogPriority.DEBUG, "Validating list of user home directories...")

        # if the base directory of a given path matches any of the above system directories, then we discard it
        for d in dirs:
            if os.path.exists(d):
                basepath = self.getBasePath(d)
                if basepath not in systemdirs:
                    validateddirs.append(d)
                else:
                    self.logger.log(LogPriority.DEBUG, "An account with a uid in the non-system range had a strange home directory: " + d)
                    self.logger.log(LogPriority.DEBUG, "Excluding this home directory from the list...")
            else:
                self.logger.log(LogPriority.DEBUG, "Home directory: " + d + " does not exist. Excluding it...")

        return validateddirs

    def getBasePath(self, path):
        '''get only the first (base) part of a given path

        :param path: string; full path to get base of
        :returns: basepath
        :rtype: string

@author: Breen Malmberg

        '''

        basepath = "/"

        # break path into list of characters
        pathchars = list(path)

        # remove the first '/' if it is there, to make
        # the list iteration easier
        if pathchars[0] == "/":
            del pathchars[0]

        # iterate over list of characters, adding all characters
        # before the next '/' path divider, to the basepath
        for c in pathchars:
            if c == "/":
                break
            else:
                basepath += c

        return basepath

    def getUIDMIN(self):
        '''return this system's minimum user ID start value, if configured


        :returns: uid_min

        :rtype: string
@author: Breen Malmberg

        '''

        uid_min = ""
        logindefs = "/etc/login.defs"

        try:

            # get normal user uid start value
            logindefscontents = readFile(logindefs, self.logger)
            if logindefscontents:
                for line in logindefscontents:
                    if re.search("^UID_MIN", line, re.IGNORECASE):
                        sline = line.split()
                        uid_min = sline[1]

            if not uid_min:
                self.logger.log(LogPriority.DEBUG, "Unable to determine UID_MIN")

        except Exception:
            raise

        return uid_min

    def reportLinux(self):
        '''check all user local home directories, on Linux platforms, for correct permissions


        :returns: compliant

        :rtype: bool

@author: Derek Walker
@change: Breen Malmberg - 06/28/2018 - re-wrote method

        '''

        compliant = True
        passwd = "/etc/passwd"

        try:

            if not os.path.exists(passwd):
                self.logger.log(LogPriority.DEBUG, "You have no passwd file! Cannot get lits of user home directories! Aborting.")
                compliant = False
                return compliant

            if self.environ.geteuid() == 0:
                # running as root/admin
                homedirs = self.getLinuxHomeDirs()

                if homedirs:

                    self.logger.log(LogPriority.DEBUG, "Scanning home directory permissions...")
                    for hd in homedirs:
                        if not os.path.exists(hd):
                            self.logger.log(LogPriority.DEBUG, "Skipping directory " + hd + " because it does not exist...")
                            continue

                        self.logger.log(LogPriority.DEBUG, "Checking " + hd)
                        if self.isGW(hd):
                            compliant = False
                            self.detailedresults += "\nThe home directory: " + str(hd) + " is group-writeable"
                            self.GWHomeDirs.append(hd)
                        if self.isWR(hd):
                            compliant = False
                            self.detailedresults += "\nThe home directory: " + str(hd) + " is world-readable"
                            self.WRHomeDirs.append(hd)
                else:
                    self.logger.log(LogPriority.DEBUG, "No home directories found!")
            else:
                # running as a normal user
                homedir = self.getMyHomeDir()
                if os.path.exists(homedir):
                    self.logger.log(LogPriority.DEBUG, "Checking " + homedir)
                    if self.isGW(homedir):
                        compliant = False
                        self.detailedresults += "\nThe home directory: " + str(homedir) + " is group-writeable"
                        self.GWHomeDirs.append(homedir)
                    if self.isWR(homedir):
                        compliant = False
                        self.detailedresults += "\nThe home directory: " + str(homedir) + " is world-readable"
                        self.WRHomeDirs.append(homedir)
                else:
                    self.logger.log(LogPriority.DEBUG, "Skipping directory " + homedir + " because it does not exist...")

        except Exception:
            raise

        return compliant

    def getMyHomeDir(self):
        '''return the home directory for the currently logged-in user


        :returns: HomeDir

        :rtype: string
@author: Breen Malmberg

        '''

        HomeDir = ""
        findHomeDir = "echo $HOME"
        uuid = self.environ.geteuid()

        try:

            # precautionary check
            if uuid <= 100:
                self.logger.log(LogPriority.DEBUG, "This method should only be run by non-system, user accounts!")
                return HomeDir

            self.cmdhelper.executeCommand(findHomeDir)
            retcode = self.cmdhelper.getReturnCode()
            if retcode != 0:
                errstr = self.cmdhelper.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
                return HomeDir

            HomeDir = self.cmdhelper.getOutputString()

            # backup method (if the $HOME env is not set for the current user)
            if not HomeDir:
                HomeDir = pwd.getpwuid(uuid).pw_dir

        except Exception:
            raise

        return HomeDir

    def fix(self):
        '''remove group-write and other-read permissions on all local user home directories


        :returns: self.rulesuccess

        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 10/13/2015 - will now fix /dev/null permissions when run;
                                        will no longer modify /var/empty or /dev/null

        '''

        self.iditerator = 0
        self.rulesuccess = True
        self.detailedresults = ""

        try:

            if not self.ci.getcurrvalue():
                self.detailedresults += "\nRule was not enabled. Nothing was done."
                return self.rulesuccess

            if self.environ.geteuid() == 0:
                eventlist = self.statechglogger.findrulechanges(self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)

            if self.GWHomeDirs:
                for hd in self.GWHomeDirs:
                    self.logger.log(LogPriority.DEBUG, "Removing group-write permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod g-w " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False
                        self.detailedresults += "\nUnable to remove group write permission on directory: " + hd

            if self.WRHomeDirs:
                for hd in self.WRHomeDirs:
                    self.logger.log(LogPriority.DEBUG, "Removing world read permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod o-r " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False
                        self.detailedresults += "\nUnable to remove world read permission on directory: " + hd
                    self.logger.log(LogPriority.DEBUG, "Also ensuring no world write permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod o-w " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 24
0
class NoCachedFDEKeys(Rule):
    def __init__(self, config, environ, logdispatch, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logdispatch, statechglogger)

        self.logger = logdispatch
        self.rulenumber = 271
        self.rulename = "NoCachedFDEKeys"
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.rootrequired = True
        datatype = "bool"
        key = "NOCACHEDFDEKEYS"
        instructions = "To disable this rule set the value of " + \
            "NOCACHEDFDEKEYS to False"
        default = True
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.ci = self.initCi(datatype, key, instructions, default)

    def report(self):
        '''

        :return:
        '''
        try:
            self.detailedresults = ""
            compliant = True
            self.ch = CommandHelper(self.logger)
            cmd = "/usr/bin/pmset -g"
            if self.ch.executeCommand(cmd):
                output = self.ch.getOutput()
                error = self.ch.getError()
                if output:
                    for line in output:
                        if re.search("DestroyFVKeyOnStandby", line):
                            line = line.strip()
                            temp = line.split()
                            if temp[1] != "1":
                                self.detailedresults += "Incorrect value " + \
                                    "for DestroyFVKeyOnStandy key\n"
                                debug = "Incorrect value for " + \
                                    "DestroyFVKeyOnStandby key\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                compliant = False
                elif error:
                    debug = "Error in running pmset command\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    compliant = False
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        try:
            self.detailedresults = ""
            success = True
            if self.ci.getcurrvalue():
                self.iditerator = 0
                eventlist = self.statechglogger.findrulechanges(
                    self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)
                success = True
                if not self.compliant:
                    cmd = "/usr/bin/pmset -a destroyfvkeyonstandby 1"
                    if self.ch.executeCommand(cmd):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        undocmd = "/usr/bin/pmset -a destroyfvkeyonstandby 0"
                        event = {
                            "eventtype": "commandstring",
                            "command": undocmd
                        }
                        self.statechglogger.recordchgevent(myid, event)
                    else:
                        success = False
                self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 25
0
class ScheduleStonix(Rule):
    """Schedule Stonix to run randomly throughout the week and once in a user
    context
    
    @author: Breen Malmberg
    
    @change: 09/25/2017 - Breen Malmberg - Eliminated redundant code; separated out
            some code into its own methods; Improved method comment blocks; Improved
            in-line code comments; Added logic to ensure that no two STONIX jobs can
            end up being scheduled to run at the same time
    @change: 04/17/2018 - Breen Malmberg - Fixed the key being specified in the mac os
            dict for the jobs from "Day" to "Weekday"
    @change: 05/02/2018 - Breen Malmberg - Fixed a missed logic path for setting a flag
            to False indicating that the config file(s) needed to be created on mac os
            (they were never getting created the paths didn't exist to begin with; this
            appears to have been a recent regression); added debug logging to the fixmac()
            method; removed an unused import "FindUserLoggedIn"


    """
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 268
        self.rulename = 'ScheduleStonix'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.sethelptext()
        self.rulesuccess = True
        self.guidance = ['']
        self.applicable = {
            'type': 'white',
            'family': ['darwin', 'linux', 'solaris', 'freebsd']
        }

        datatype = "int"
        keyfd = "FIXDAY"
        keyfh = "FIXHOUR"
        keyfm = "FIXMINUTE"
        keyrd = "REPORTDAY"
        keyrh = "REPORTHOUR"
        keyrm = "REPORTMINUTE"
        keyufh = "USERCONTEXTFIXHOUR"
        keyufm = "USERCONTEXTFIXMINUTE"
        instruct_fd = "Enter the day of the week you would like the fix job to run (1-7). 1 = Monday. 7 = Sunday. This value cannot be the same as REPORTDAY value."
        instruct_fh = "Enter the hour of the day you would like the fix job to run (0-23). 0 = Midnight. This value cannot be the same as USERCONTEXTFIXHOUR value."
        instruct_fm = "Enter the minute of the hour you would like the fix job to run (0-59)."
        instruct_rd = "Enter the day of the week you would like the report job to run (1-7) 1 = Monday. 7 = Sunday. This value cannot be the same as FIXDAY value."
        instruct_rh = "Enter the hour of the day you would like the report job to run (0-23) 0 = Midnight. This value cannot be the same as USERCONTEXTFIXHOUR value."
        instruct_rm = "Enter the minute of the hour you would like the report job to run (0-59)."
        instruct_ufh = "Enter the hour of the day you would like the user-context fix job to run (0-23). This value cannot be the same as FIXHOUR value or REPORTHOUR value."
        instruct_ufm = "Enter the minute of the hour you would like the user-context fix job to run (0-59)."
        default = 0

        datatype2 = "bool"
        key2 = "CONFIGUREJOBTIMESMANUALLY"
        instruct2 = "Set the value of CONFIGUREJOBTIMESMANUALLY to True, in order to manually specify when each STONIX job should run (as opposed to using randomly generated times)."
        default2 = False
        self.manualjobtimesCI = self.initCi(datatype2, key2, instruct2,
                                            default2)

        datatype3 = "bool"
        key3 = "SCHEDULEFIXJOBS"
        instruct3 = "To prevent STONIX from being schedule to run in fix mode, set the value of SCHEDULEFIXJOBS to False"
        default3 = True
        self.schedulefixjobsCI = self.initCi(datatype3, key3, instruct3,
                                             default3)

        self.fixdayCI = self.initCi(datatype, keyfd, instruct_fd, default)
        self.fixhourCI = self.initCi(datatype, keyfh, instruct_fh, default)
        self.fixminuteCI = self.initCi(datatype, keyfm, instruct_fm, default)
        self.reportdayCI = self.initCi(datatype, keyrd, instruct_rd, default)
        self.reporthourCI = self.initCi(datatype, keyrh, instruct_rh, default)
        self.reportminuteCI = self.initCi(datatype, keyrm, instruct_rm,
                                          default)
        self.userfixhourCI = self.initCi(datatype, keyufh, instruct_ufh,
                                         default)
        self.userfixminuteCI = self.initCi(datatype, keyufm, instruct_ufm,
                                           default)

        self.initVars()
        if self.firstRun():
            self.initTimes()

    def initTimes(self):
        """ """

        self.genJobTimes()
        self.syncCITimes()
        if self.checkJobCollisions():
            return self.initTimes()

    def syncCITimes(self):
        """ """

        self.fixdayCI.updatecurrvalue(self.adminfixday)
        self.fixhourCI.updatecurrvalue(self.adminfixhour)
        self.fixminuteCI.updatecurrvalue(self.adminfixminute)
        self.reportdayCI.updatecurrvalue(self.adminreportday)
        self.reporthourCI.updatecurrvalue(self.adminreporthour)
        self.reportminuteCI.updatecurrvalue(self.adminreportminute)
        self.userfixhourCI.updatecurrvalue(self.userfixhour)
        self.userfixminuteCI.updatecurrvalue(self.userfixminute)

    def firstRun(self):
        """ """

        firstrun = True
        total = 0

        total = self.fixdayCI.getcurrvalue() + \
        self.fixhourCI.getcurrvalue() + \
        self.fixminuteCI.getcurrvalue() + \
        self.reportdayCI.getcurrvalue() + \
        self.reporthourCI.getcurrvalue() + \
        self.reportminuteCI.getcurrvalue() + \
        self.userfixhourCI.getcurrvalue() + \
        self.userfixminuteCI.getcurrvalue()

        if total > 0:
            firstrun = False

        return firstrun

    def buildFiles(self):
        """dynamically build the conf and script files
        based on the generated or entered times
        
        @author: Breen Malmberg


        """

        # define the Mac OS X weekly STONIX report launchd job
        self.stonixplistreport = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>gov.lanl.stonix.report</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/stonix4mac.app/Contents/Resources/stonix.app/Contents/MacOS/stonix</string>
        <string>-c</string>
        <string>-r</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Weekday</key>
            <integer>""" + str(self.adminreportday) + """</integer>
            <key>Hour</key>
            <integer>""" + str(self.adminreporthour) + """</integer>
            <key>Minute</key>
            <integer>""" + str(self.adminreportminute) + """</integer>
        </dict>
    </array>
</dict>
</plist>"""

        # define the Mac OS X weekly STONIX fix launchd job
        self.stonixplistfix = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>gov.lanl.stonix.fix</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/stonix4mac.app/Contents/Resources/stonix.app/Contents/MacOS/stonix</string>
        <string>-c</string>
        <string>-f</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Weekday</key>
            <integer>""" + str(self.adminfixday) + """</integer>
            <key>Hour</key>
            <integer>""" + str(self.adminfixhour) + """</integer>
            <key>Minute</key>
            <integer>""" + str(self.adminfixminute) + """</integer>
        </dict>
    </array>
</dict>
</plist>"""

        # define the once-daily STONIX user-context launch agent job
        self.stonixplistuser = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>gov.lanl.stonix.user</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/stonix4mac.app/Contents/Resources/stonix.app/Contents/MacOS/stonix</string>
        <string>-c</string>
        <string>-""" + self.usermode + """</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>""" + str(self.userfixhour) + """</integer>
            <key>Minute</key>
            <integer>""" + str(self.userfixminute) + """</integer>
        </dict>
    </array>
</dict>
</plist>"""

        self.userstonixscript = """#! /usr/bin/env python

#Created on Jan 13, 2014
#
#This script is used by ScheduleStonix.py
#This script will run stonix.py, in user context mode, once daily
#
#@author: Breen Malmberg


import os,time,getpass,pwd,re

#defaults
username = getpass.getuser()
userhome = ''
scriptsuccess = True

#get current user's home directory
for p in pwd.getpwall():
    if p[0] in username:
        if re.search('^/home/',p[5]) or re.search('^/Users/',p[5]):
            userhome = p[5]

todaysdate = time.strftime("%d%m%Y")
stonixscriptpath = '/usr/sbin/stonix.py'
stonixtempfolder = userhome + '/.stonix/'
alreadyran = False

#if the script has not already run today

if os.path.exists(stonixtempfolder + 'userstonix.log'):

    f = open(stonixtempfolder + 'userstonix.log','r')
    contentlines = f.readlines()
    f.close()

    for line in contentlines:
        line = line.split()
        #script log file entries should follow the format: usernameDDMMYYYY
        if re.search('^' + username,line[0]) and re.search(todaysdate,line[1]):
            alreadyran = True

    #if the script has not yet run today, then run it        
    if not alreadyran:

        try:

            #run stonix -f in user context
            os.system(stonixscriptpath + ' -c""" + self.usermode + """')

        except IOError:
            exitcode = IOError.errno
            print IOError.message
            scriptsuccess = False
        except OSError:
            exitcode = OSError.errno
            print OSError.message
            scriptsuccess = False

        if scriptsuccess:

            i = 0

            for line in contentlines:
                if re.search('^' + username,line) and not re.search(todaysdate,line):
                    line = username + ' ' + todaysdate
                    contentlines[i] = line
                    i += 1

            #create/update log entry
            f = open(stonixtempfolder + 'userstonix.log','w')
            f.writelines(contentlines)
            f.close()

        else:

            print "user-stonix.py script failed to run properly"
            exit(exitcode)"""

    def initVars(self):
        """initialize all of the time variables
        to be used by the class
        
        @author: Breen Malmberg


        """

        self.svchelper = ServiceHelper(self.environ, self.logger)

        # init cronfilelocation to blank
        self.cronfilelocation = "/etc/crontab"

        self.userscriptfile = "/etc/profile.d/user-stonix.py"

        self.crontimedict = {}

        # get the STONIX executable path
        self.stonixpath = self.environ.get_script_path()

        self.userfixhour = 0
        self.userfixminute = 0
        self.adminfixday = 0
        self.adminfixhour = 0
        self.adminfixminute = 0
        self.adminreportday = 0
        self.adminreporthour = 0
        self.adminreportminute = 0

    def setUserTimes(self):
        """ """

        self.adminfixday = self.fixdayCI.getcurrvalue()
        self.adminfixhour = self.fixhourCI.getcurrvalue()
        self.adminfixminute = self.fixminuteCI.getcurrvalue()
        self.adminreportday = self.reportdayCI.getcurrvalue()
        self.adminreporthour = self.reporthourCI.getcurrvalue()
        self.adminreportminute = self.reportminuteCI.getcurrvalue()
        self.userfixhour = self.userfixhourCI.getcurrvalue()
        self.userfixminute = self.userfixminuteCI.getcurrvalue()

        self.crontimedict['report'] = str(self.adminreportminute) + ' ' + str(
            self.adminreporthour) + ' \* \* ' + str(self.adminreportday)
        self.crontimedict['fix'] = str(self.adminfixminute) + ' ' + str(
            self.adminfixhour) + ' \* \* ' + str(self.adminfixday)

    def checkJobCollisions(self):
        """check to make sure none of the generated
        job times overlap, or are invalid
        return True if invalid or overlap
        return False if valid and not overlap


        :returns: collisions

        :rtype: list

@author: Breen Malmberg

        """

        self.logger.log(LogPriority.DEBUG,
                        "Checking for job time collisions...")

        collisions = []

        # job collision validation
        if self.fixdayCI.getcurrvalue() == self.reportdayCI.getcurrvalue():
            collisions.append("afd")
        if self.userfixhourCI.getcurrvalue() == self.fixhourCI.getcurrvalue():
            collisions.append("ufh")
        if self.userfixhourCI.getcurrvalue() == self.reporthourCI.getcurrvalue(
        ):
            collisions.append("arh")

        if collisions:
            self.logger.log(LogPriority.DEBUG, "Job time collision detected.")

        return collisions

    def validateUserTimes(self):
        """check the entered user-defined cron job times to see if they are valid


        :returns: valid

        :rtype: bool
@author: Breen Malmberg

        """

        valid = True
        formatvalid = True
        rangevalid = True
        collisions = []

        fixday = self.fixdayCI.getcurrvalue()
        fixhour = self.fixhourCI.getcurrvalue()
        fixminute = self.fixminuteCI.getcurrvalue()
        reportday = self.reportdayCI.getcurrvalue()
        reporthour = self.reporthourCI.getcurrvalue()
        reportminute = self.reportminuteCI.getcurrvalue()
        userfixhour = self.userfixhourCI.getcurrvalue()
        userfixminute = self.userfixminuteCI.getcurrvalue()

        self.logger.log(LogPriority.DEBUG,
                        "Validating user-entered job times...")

        self.logger.log(LogPriority.DEBUG,
                        "Checking job times format (integer length).")

        # length (digits) input validation
        il = []
        if len(str(fixday).lstrip("0")) > 1:
            formatvalid = False
            il.append("fixday should be 1 integer in length")
        if len(str(reportday).lstrip("0")) > 1:
            formatvalid = False
            il.append("reportday should be 1 integer in length")
        if len(str(fixhour).lstrip("0")) > 2:
            formatvalid = False
            il.append("fixhour should be no more than 2 integers in length")
        if len(str(fixminute).lstrip("0")) > 2:
            formatvalid = False
            il.append("fixminute should be no more than 2 integers in length")
        if len(str(reporthour).lstrip("0")) > 2:
            formatvalid = False
            il.append("reporthour should be no more than 2 integers in length")
        if len(str(reportminute).lstrip("0")) > 2:
            formatvalid = False
            il.append(
                "reportminute should be no more than 2 integers in length")
        if len(str(userfixhour).lstrip("0")) > 2:
            formatvalid = False
            il.append(
                "userfixhour should be no more than 2 integers in length")
        if len(str(userfixminute).lstrip("0")) > 2:
            formatvalid = False
            il.append(
                "userfixminute should be no more than 2 integers in length")

        if not formatvalid:
            self.detailedresults += "\nIncorrect job time format detected. Incorrect integer length:\n" + "\n".join(
                il)

        # range input validation
        oor = []
        if fixday not in list(range(1, 8)):
            rangevalid = False
            oor.append("fixday: " + str(fixday))
        if reportday not in list(range(1, 8)):
            rangevalid = False
            oor.append("reportday: " + str(reportday))
        if fixhour not in list(range(0, 24)):
            rangevalid = False
            oor.append("fixhour: " + str(fixhour))
        if reporthour not in list(range(0, 24)):
            rangevalid = False
            oor.append("reporthour: " + str(reporthour))
        if fixminute not in list(range(0, 60)):
            rangevalid = False
            oor.append("fixminute: " + str(fixminute))
        if reportminute not in list(range(0, 60)):
            rangevalid = False
            oor.append("reportminute: " + str(reportminute))
        if userfixhour not in list(range(0, 24)):
            rangevalid = False
            oor.append("userfixhour: " + str(userfixhour))
        if userfixminute not in list(range(0, 60)):
            oor.append("userfixminute: " + str(userfixminute))

        if not rangevalid:
            self.detailedresults += "\nIncorrect job time format detected. Job time(s) outside valid range:\n" + "\n".join(
                oor)

        # fix any job time collisions
        collisions = self.checkJobCollisions()
        if collisions:
            if "afd" in collisions:
                self.detailedresults += "\nThere is a time collision between admin fix day and admin report day"
            if "ufh" in collisions:
                self.detailedresults += "\nThere is a time collision between user fix hour and admin fix hour"
            if "urh" in collisions:
                self.detailedresults += "\nThere is a time collision between user fix hour and admin report hour"

        if not rangevalid:
            valid = False
        if not formatvalid:
            valid = False
        if collisions:
            valid = False

        if valid:
            self.detailedresults += "\nAll user-entered job times are valid. Proceeding..."
        else:
            self.detailedresults += "\nUser-entered job time(s) not valid. Please correct the issue(s) and run fix again, to set your custom job time(s)."

        return valid

    def randomExcept(self, lowest, highest, exclude=None):
        """generate a random number between lowest and highest (including lowest)
        but excluding the given number exclude
        (result will never be = highest)

        :param lowest: int; lower bound
        :param highest: int; upper bound
        :param exclude: int|list; integer or range of integers to exclude from result
                default value for exclude is None
        :returns: x
        :rtype: int

@author: Breen Malmberg

        """

        x = 0

        if lowest == highest:
            self.logger.log(
                LogPriority.WARNING,
                "lowest argument must be less than highest argument")
            if lowest != exclude:
                x = lowest
            return x

        if lowest > highest:
            self.logger.log(
                LogPriority.WARNING,
                "lowest argument must be less than highest argument")
            return x

        if type(exclude).__name__ == "str":
            exclude = int(exclude)
        elif type(exclude).__name__ == "NoneType":
            pass
        else:
            self.logger.log(
                LogPriority.WARNING,
                "Incorrect type for argument: exclude. Needs to be either list or int. Got: "
                + type(exclude).__name__)
            return x

        x = random.randrange(lowest, highest)
        if exclude == None:
            return x

        if type(exclude).__name__ == "list":
            if x in exclude:
                x = self.randomExcept(lowest, highest, exclude)
        elif type(exclude).__name__ == "int":
            if x == exclude:
                x = self.randomExcept(lowest, highest, exclude)

        return x

    def genJobTimes(self, *args):
        """Generate random times to run the STONIX jobs
        Build the crontimedict used by Linux
        
        @author: Breen Malmberg

        :param *args: 

        """

        genall = False

        self.logger.log(LogPriority.DEBUG,
                        "Generating new, random job times...")

        if "all" in args:
            genall = True
        elif not args:
            genall = True
        else:
            if "afd" in args:
                self.adminfixday = random.randrange(1, 8)
            if "afh" in args:
                self.adminfixhour = random.randrange(17, 24)
            if "afm" in args:
                self.adminfixminute = random.randrange(0, 60)
            if "ard" in args:
                self.adminreportday = random.randrange(1, 8)
            if "arh" in args:
                self.adminreporthour = random.randrange(17, 24)
            if "arm" in args:
                self.adminreportminute = random.randrange(0, 60)
            if "ufh" in args:
                self.userfixhour = random.randrange(17, 24)
            if "ufm" in args:
                self.userfixminute = random.randrange(0, 60)

        if genall:
            self.adminfixday = random.randrange(1, 8)
            self.adminfixhour = random.randrange(17, 24)
            self.adminfixminute = random.randrange(0, 60)

            self.adminreportday = random.randrange(1, 8)
            self.adminreporthour = random.randrange(17, 24)
            self.adminreportminute = random.randrange(0, 60)

            self.userfixhour = random.randrange(17, 24)
            self.userfixminute = random.randrange(0, 60)

        self.crontimedict['report'] = str(self.adminreportminute) + ' ' + str(
            self.adminreporthour) + ' * * ' + str(self.adminreportday)
        self.crontimedict['fix'] = str(self.adminfixminute) + ' ' + str(
            self.adminfixhour) + ' * * ' + str(self.adminfixday)

    def getFileContents(self, filepath):
        """Read file contents from given filepath, into a list
        Return the list (of strings)
        Return empty list if given filepath does not exist

        :param filepath: 
        :returns: contents
        :rtype: list

@author: Breen Malmberg

        """

        contents = []

        if os.path.exists(filepath):
            f = open(filepath, 'r')
            contents = f.readlines()
            f.close()

        return contents

    def report(self):
        """Linux:
        Check that Cron file(s) exist and that they contain the correct job entries
        Check that the STONIX user script exists and contains the correct contents
        
        Mac:
        Check that the launchd files exist
        Check that each launchd file contains the correct contents


        :returns: self.compliant

        :rtype: bool

@author: Breen Malmberg

        """

        self.compliant = True
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        if self.schedulefixjobsCI.getcurrvalue():
            self.usermode = 'f'
        else:
            self.usermode = 'r'

        if self.manualjobtimesCI.getcurrvalue():
            self.logger.log(LogPriority.DEBUG,
                            "Using user-configured job times...")
            if self.validateUserTimes():
                self.setUserTimes()
            else:
                self.compliant = False
                self.detailedresults += "\nThe user-specified cron job time was invalid."
                self.formatDetailedResults("report", self.compliant,
                                           self.detailedresults)
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return self.compliant
            collisions = self.checkJobCollisions()
            for item in collisions:
                self.genJobTimes(item)
            if collisions:
                self.detailedresults += "\nOne of the times entered was invalid because it would cause more than one of the jobs to run at the same time. The offending time entry has been changed to a new, randomly generated one."
                self.syncCITimes()
        else:
            self.logger.log(LogPriority.DEBUG,
                            "Using randomly generated job times...")
            if self.checkJobCollisions():
                self.genJobTimes()
                self.syncCITimes()

        self.logger.log(
            LogPriority.DEBUG,
            "Building configuration files text, using job times...")
        self.buildFiles()

        self.logger.log(LogPriority.DEBUG,
                        "STONIX path set to: " + str(self.stonixpath))

        try:

            # if Mac OS, check Mac launch daemon and agent jobs
            if self.environ.getostype() == "Mac OS X":
                if not self.reportMac():
                    self.compliant = False
            else:
                # else check Linux cron jobs
                if not self.reportLinux():
                    self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += "\n" + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def reportLinux(self):
        """Check for STONIX Cron job entries in the Linux crontab


        :returns: retval

        :rtype: bool

@author: Breen Malmberg

        """

        retval = True

        self.cronfileexists = True
        self.reportjob = False
        self.fixjob = False
        self.userjob = True
        stonixreportjob = ' root nice -n 19 ' + str(
            self.stonixpath) + '/stonix.py -cr'
        stonixfixjob = ' root nice -n 19 ' + str(
            self.stonixpath) + '/stonix.py -cdf'

        # check for existence of system crontab file
        if not os.path.exists(self.cronfilelocation):
            self.cronfileexists = False
            self.detailedresults += '\nSystem crontab file does not exist'
        else:
            # check for STONIX cron job entries
            contents = self.getFileContents(self.cronfilelocation)
            if not contents:
                self.detailedresults += '\nSystem crontab file was empty'
            else:
                # report entry
                for line in contents:
                    self.logger.log(
                        LogPriority.DEBUG, "Comparing line:\n" + str(line) +
                        "to stonixreportjob:\n" + str(stonixreportjob))
                    if re.search(stonixreportjob, line, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG, "REPORT JOB FOUND")
                        self.reportjob = True
                    else:
                        self.logger.log(LogPriority.DEBUG,
                                        "Lines do NOT match")

                if not self.reportjob:
                    self.detailedresults += '\nSTONIX report job not found\n'
                else:
                    self.detailedresults += '\nSTONIX report job found\n'

                self.logger.log(
                    LogPriority.DEBUG,
                    "After report entry check: reportjob is " +
                    str(self.reportjob))

                # fix entry
                for line in contents:
                    self.logger.log(
                        LogPriority.DEBUG, "Comparing line:\n" + str(line) +
                        "to stonixfixjob:\n" + str(stonixfixjob))
                    if re.search(stonixfixjob, line, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG, "FIX JOB FOUND")
                        self.fixjob = True
                    else:
                        self.logger.log(LogPriority.DEBUG,
                                        "Lines do NOT match")

                if not self.fixjob:
                    self.detailedresults += '\nSTONIX fix job not found\n'
                else:
                    self.detailedresults += '\nSTONIX fix job found\n'

                self.logger.log(
                    LogPriority.DEBUG,
                    "After fix entry check: fixjob is " + str(self.fixjob))

            self.logger.log(
                LogPriority.DEBUG,
                "Before permissions check: retval is " + str(retval))

            # check perms of system crontab
            crontabperms = getOctalPerms(self.cronfilelocation)
            if crontabperms != 600:
                self.logger.log(
                    LogPriority.DEBUG,
                    "crontab perms should be 600. Got: " + str(crontabperms))
                self.detailedresults += "\nIncorrect permissions detected on system crontab file"
                retval = False

        self.logger.log(LogPriority.DEBUG,
                        "Before user script checks: retval is " + str(retval))

        # check user script
        if not os.path.exists(self.userscriptfile):
            self.userjob = False
            self.detailedresults += '\nSTONIX user script not found'
        else:
            self.detailedresults += '\nSTONIX user script found'
            contents = self.getFileContents(self.userscriptfile)

            userjobline = "os.system(stonixscriptpath + ' -c" + self.usermode + "')"

            stripped_contents = list(map(str.strip, contents))

            if userjobline not in stripped_contents:
                self.userjob = False
                self.detailedresults += '\nSTONIX user script has incorrect contents'
            else:
                self.detailedresults += '\nSTONIX user script has correct contents'
            # check perms on user script file
            userscriptperms = getOctalPerms(self.userscriptfile)
            if userscriptperms != 755:
                self.detailedresults += "\nIncorrect permissions detected on user script file: " + str(
                    self.userscriptfile)

        self.logger.log(LogPriority.DEBUG,
                        "Before Final Checks: retval is " + str(retval))

        if not self.cronfileexists:
            retval = False
        if not self.reportjob:
            retval = False
        if not self.fixjob:
            retval = False
        if not self.userjob:
            retval = False

        return retval

    def reportMac(self):
        """Check for the existence of the necessary STONIX launchd jobs
        Check that each STONIX launchd job contains the correct contents


        :returns: retval

        :rtype: bool

@author Breen Malmberg

        """

        # defaults
        retval = True
        pathsneeded = [
            '/Library/LaunchDaemons/gov.lanl.stonix.report.plist',
            '/Library/LaunchDaemons/gov.lanl.stonix.fix.plist',
            '/Library/LaunchAgents/gov.lanl.stonix.user.plist'
        ]

        plistdict = {
            '/Library/LaunchDaemons/gov.lanl.stonix.report.plist':
            self.stonixplistreport,
            '/Library/LaunchDaemons/gov.lanl.stonix.fix.plist':
            self.stonixplistfix,
            '/Library/LaunchAgents/gov.lanl.stonix.user.plist':
            self.stonixplistuser
        }
        systemservicetargets = [
            "gov.lanl.stonix.fix", "gov.lanl.stonix.report"
        ]
        self.macadminfixjob = True
        self.macadminreportjob = True
        self.macuserjob = True

        try:

            # if a required path is missing, return False
            for path in pathsneeded:
                if not os.path.exists(path):
                    retval = False
                    self.detailedresults += "Path doesn't exist: " + path + "\n"
                    if path == '/Library/LaunchDaemons/gov.lanl.stonix.report.plist':
                        self.macadminreportjob = False
                    elif path == '/Library/LaunchDaemons/gov.lanl.stonix.fix.plist':
                        self.macadminfixjob = False
                    elif path == '/Library/LaunchAgents/gov.lanl.stonix.user.plist':
                        self.macuserjob = False

                # if path exists, check for required plist content
                else:
                    contents = readFile(path, self.logger)

                    # get contents of appropriate plist script in a list
                    scrptcontents = plistdict[path].splitlines(True)

                    # compare contents of plist script
                    i = 0
                    try:
                        for i in range(len(contents)):
                            if not re.search('<integer>', contents[i]):
                                if contents[i].strip(
                                ) != scrptcontents[i].strip():
                                    retval = False
                                    self.detailedresults += "line not correct: " + str(
                                        contents[i]) + " in file: " + str(
                                            path) + "\n"
                                    if re.search('report', path,
                                                 re.IGNORECASE):
                                        self.macadminreportjob = False
                                    if re.search('fix', path, re.IGNORECASE):
                                        self.macadminfixjob = False
                                    if re.search('user', path, re.IGNORECASE):
                                        self.macuserjob = False

                            else:
                                if not re.search(
                                        '<integer>[0-9][0-9]{0,1}<\/integer>',
                                        contents[i]):
                                    retval = False
                                    self.detailedresults += "integer line wrong: " + str(
                                        contents[i]) + " in file: " + str(
                                            path) + "\n"
                                    if re.search('report', path,
                                                 re.IGNORECASE):
                                        self.macadminreportjob = False
                                    if re.search('fix', path, re.IGNORECASE):
                                        self.macadminfixjob = False
                                    if re.search('user', path, re.IGNORECASE):
                                        self.macuserjob = False
                    except IndexError:
                        pass

            # This part needs to be changed to work with re-designed servicehelperTwo
            self.ch.executeCommand("/bin/launchctl print-disabled system/")
            disabledserviceslist = self.ch.getOutput()
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                errmsg = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errmsg)
            for line in disabledserviceslist:
                for item in systemservicetargets:
                    if re.search(item + '\"\s+\=\>\s+true', line,
                                 re.IGNORECASE):
                        retval = False
                        self.detailedresults += "\nA required STONIX service: gov.lanl.stonix.user.plist is currently disabled"

        except Exception:
            raise
        return retval

    def fix(self):
        """Mac OS X: Create or edit STONIX launchd jobs as necessary
        Linux: Create or edit STONIX Cron job entries as necessary

        :return: self.rulesuccess
        :rtype: bool

        """

        self.detailedresults = ""
        self.rulesuccess = True

        try:

            if self.manualjobtimesCI.getcurrvalue():
                if not self.validateUserTimes():
                    self.rulesuccess = False
                    self.detailedresults += "\nThe user-specified cron job time was incorrectly specified."
                    self.formatDetailedResults("fix", self.rulesuccess,
                                               self.detailedresults)
                    self.logger.log(LogPriority.INFO, self.detailedresults)
                    return self.rulesuccess

            # If Mac OS, do fixes for Mac
            if self.environ.getostype() == "Mac OS X":
                if not self.fixMac():
                    self.rulesuccess = False
            # else do fixes for Linux
            else:
                if not self.fixLinux():
                    self.rulesuccess = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += "\n" + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            self.rulesuccess = False
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess

    def fixLinux(self):
        """Create or edit STONIX Cron jobs for Linux, as necessary

        :return: retval
        :rtype: bool

        """

        retval = True

        # create the report and fix Cron entry strings
        reportstring = '\n' + str(self.reportminuteCI.getcurrvalue(
        )) + ' ' + str(self.reporthourCI.getcurrvalue()) + ' * * ' + str(
            self.reportdayCI.getcurrvalue()) + ' root nice -n 19 ' + str(
                self.stonixpath) + '/stonix.py' + ' -cr'
        fixstring = '\n' + str(self.fixminuteCI.getcurrvalue()) + ' ' + str(
            self.fixhourCI.getcurrvalue()) + ' * * ' + str(
                self.fixdayCI.getcurrvalue()) + ' root nice -n 19 ' + str(
                    self.stonixpath
                ) + '/stonix.py' + ' -cdf &> /var/log/stonix-lastfix.log\n'

        # create Cron file if it doesn't exist
        if not self.cronfileexists:
            contents = []
            f = open(self.cronfilelocation, 'w')
            contents.append('## This file created by STONIX\n\n')
            contents.append(reportstring)
            if self.schedulefixjobsCI.getcurrvalue():
                contents.append(fixstring)
            f.writelines(contents)
            f.close()
        else:

            contents = self.getFileContents(self.cronfilelocation)

            # add report line if it doesn't exist
            if not self.reportjob:
                # remove any existing erroneous job times
                for line in contents:
                    if re.search("stonix.*-cr", line, re.IGNORECASE):
                        contents = [c.replace(line, '') for c in contents]
                contents.append(reportstring)

            if self.schedulefixjobsCI.getcurrvalue():
                # add fix line if it doesn't exist
                if not self.fixjob:
                    # remove any existing erroneous job times
                    for line in contents:
                        if re.search("stonix.*-cdf", line, re.IGNORECASE):
                            contents = [c.replace(line, '') for c in contents]
                    contents.append(fixstring)

            f = open(self.cronfilelocation, 'w')
            f.writelines(contents)
            f.close()
            os.chown(self.cronfilelocation, 0, 0)
            os.chmod(self.cronfilelocation, 0o600)

        if not self.userjob:
            self.logger.log(LogPriority.DEBUG,
                            "Fixing the STONIX user script...")
            # create the user script
            if not os.path.exists(self.userscriptfile):
                try:
                    if not os.path.exists('/etc/profile.d'):
                        os.makedirs('/etc/profile.d', 0o755)
                        os.chown('/etc/profile.d', 0, 0)

                    self.logger.log(
                        LogPriority.DEBUG,
                        "Creating user script file and writing correct contents..."
                    )
                    f = open(self.userscriptfile, 'w')
                    f.write(self.userstonixscript)
                    f.close()

                    os.chown(self.userscriptfile, 0, 0)
                    os.chmod(self.userscriptfile, 0o755)
                except Exception:
                    retval = False
                    self.logger.log(LogPriority.DEBUG,
                                    "Failed to create user script file")

            # write correct contents
            else:

                try:

                    self.logger.log(
                        LogPriority.DEBUG,
                        "Writing correct contents to user script file...")
                    f = open(self.userscriptfile, 'w')
                    f.write(self.userstonixscript)
                    f.close()

                    os.chown(self.userscriptfile, 0, 0)
                    os.chmod(self.userscriptfile, 0o755)

                except Exception:
                    retval = False
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Failed to write contents to user script file")

        self.logger.log(
            LogPriority.DEBUG,
            "Setting Permissions and Ownership on crontab file...")

        os.chown(self.cronfilelocation, 0, 0)
        os.chmod(self.cronfilelocation, 0o600)

        return retval

    def fixMac(self):
        """Create or edit STONIX launchd jobs for Mac OS X
        Create or edit the STONIX launch agent user job for Mac OS X

        :return: retval
        :rtype: bool

        """

        retval = True
        self.macadminfixpath = "/Library/LaunchDaemons/gov.lanl.stonix.fix.plist"
        self.macadminreportpath = "/Library/LaunchDaemons/gov.lanl.stonix.report.plist"
        self.macuserpath = "/Library/LaunchAgents/gov.lanl.stonix.user.plist"

        # defaults
        servicedict = {
            '/Library/LaunchDaemons/gov.lanl.stonix.report.plist':
            'gov.lanl.stonix.report',
            '/Library/LaunchDaemons/gov.lanl.stonix.fix.plist':
            'gov.lanl.stonix.fix',
            '/Library/LaunchAgents/gov.lanl.stonix.user.plist':
            'gov.lanl.stonix.user'
        }

        self.logger.log(LogPriority.DEBUG, "Running fixes for Mac OS")

        # create the STONIX launchd jobs
        try:

            if not self.macadminreportjob:
                self.logger.log(
                    LogPriority.DEBUG,
                    "The admin report job is either missing or incorrect. Fixing..."
                )

                # remove existing job file
                if os.path.exists(self.macadminreportpath):
                    os.remove(self.macadminreportpath)

                # create weekly report plist file
                f = open(self.macadminreportpath, 'w')
                f.write(self.stonixplistreport)
                f.close()
                os.chown(self.macadminreportpath, 0, 0)
                os.chmod(self.macadminreportpath, 0o644)
                self.logger.log(
                    LogPriority.DEBUG,
                    "Admin report job has been created and configured")

            if not self.macadminfixjob:
                self.logger.log(
                    LogPriority.DEBUG,
                    "The admin fix job is either missing or incorrect. Fixing..."
                )

                # remove existing job file
                if os.path.exists(self.macadminfixpath):
                    os.remove(self.macadminfixpath)

                if self.schedulefixjobsCI.getcurrvalue():
                    # create weekly fix plist file
                    f = open(self.macadminfixpath, 'w')
                    f.write(self.stonixplistfix)
                    f.close()
                    os.chown(self.macadminfixpath, 0, 0)
                    os.chmod(self.macadminfixpath, 0o644)
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Admin fix job has been created and configured")

            if not self.macuserjob:
                self.logger.log(
                    LogPriority.DEBUG,
                    "The user job is either missing or incorrect. Fixing...")

                # remove existing job file
                if os.path.exists(self.macuserpath):
                    os.remove(self.macuserpath)

                # create once-daily user context plist file
                f = open(self.macuserpath, 'w')
                f.write(self.stonixplistuser)
                f.close()
                os.chown(self.macuserpath, 0, 0)
                os.chmod(self.macuserpath, 0o644)
                self.logger.log(LogPriority.DEBUG,
                                "User job has been created and configured")

            self.logger.log(LogPriority.DEBUG, "Loading STONIX jobs...")

            #!FIXME This part needs to be changed to work with servicehelper
            for item in servicedict:
                if re.search("user", item, re.IGNORECASE):
                    self.logger.log(LogPriority.DEBUG, "Loading user job...")
                    self.ch.executeCommand("/bin/launchctl load " + item)
                else:
                    if re.search("fix", item, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG,
                                        "Loading admin fix job...")
                    if re.search("report", item, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG,
                                        "Loading admin report job...")
                    self.ch.executeCommand("/bin/launchctl enable system/" +
                                           str(servicedict[item]))

        except Exception:
            retval = False
            raise
        return retval
Ejemplo n.º 26
0
    def reportFree(self):
        compliant = True
        self.editor1, self.editor2 = "", ""

        directives1 = {
            "ipv6_network_interfaces": "none",
            "ipv6_activate_all_interfaces": "NO",
            "ip6addrctl_enable": "NO",
            "ip6addrctl_policy": "NO"
        }
        directives2 = {
            "net.ipv6.conf.all.disable_ipv6": "1",
            "net.ipv6.conf.default.disable_ipv6": "1",
            "net.ipv6.conf.lo.disable_ipv6": "1"
        }

        path1 = "/etc/rc.conf"
        path2 = "/etc/sysctl.conf"
        tmpfile1 = "/etc/rc.conf.tmp"
        tmpfile2 = "/etc/sysctl.conf.tmp"

        # try and create /etc/rc.conf if doesn't exist
        if not os.path.exists(path1):
            if not createFile(path1, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path1 + ", so this file will not be configured, " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 420], self.logger):
                compliant = False
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path1, tmpfile1, directives1,
                                          "present", "closedeq")
            if not self.editor1.report():
                compliant = False

        # try and create /etc/sysctl.conf if doesn't exist
        if not os.path.exists(path2):
            if not createFile(path2, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path2 + " so this file will not be configured " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path2):
            if not checkPerms(path2, [0, 0, 384], self.logger):
                compliant = False
            self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path2, tmpfile2, directives2,
                                          "present", "closedeq")
            if not self.editor2.report():
                compliant = False
        else:
            compliant = False

        cmdhelper = CommandHelper(self.logger)
        cmd = ["/sbin/ifconfig", "-a"]
        if not cmdhelper.executeCommand(cmd):
            return False
        output = cmdhelper.getOutput()
        for line in output:
            line = line.strip()
            if re.search("^nd6", line):
                if not re.search("(.)*IFDISABLED(.)*", line):
                    compliant = False
        return compliant
Ejemplo n.º 27
0
class SecureMDNS(Rule):
    '''The Avahi daemon implements the DNS Service Discovery and Multicast DNS
    protocols, which provide service and host discovery on a network. It allows
    a system to automatically identify resources on the network, such as
    printers or web servers. This capability is also known as mDNSresponder
    and is a major part of Zeroconf networking. By default, it is enabled.
    This rule makes a number of configuration changes to the avahi service
    in order to secure it.
    @change: 04/16/2014 ekkehard ci and self.setkvdefaultscurrenthost updates


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 135
        self.rulename = 'SecureMDNS'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.compliant = False
        self.rulesuccess = True
        self.guidance = [
            'NSA(3.7.2)', 'CCE 4136-8', 'CCE 4409-9', 'CCE 4426-3',
            'CCE 4193-9', 'CCE 4444-6', 'CCE 4352-1', 'CCE 4433-9',
            'CCE 4451-1', 'CCE 4341-4', 'CCE 4358-8', 'CCE-RHEL7-CCE-TBD 2.5.2'
        ]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # set up command helper object
        self.ch = CommandHelper(self.logger)

        # init helper classes
        self.sh = ServiceHelper(self.environ, self.logger)
        self.serviceTarget = ""

        if self.environ.getostype() == "Mac OS X":
            self.ismac = True
            self.hasSIP = False
            self.plb = "/usr/libexec/PlistBuddy"
            osxversion = str(self.environ.getosver())
            versplit = osxversion.split(".")
            if len(versplit) > 2:
                minorVersion = int(versplit[1])
                releaseVersion = int(versplit[2])
            elif len(versplit) == 2:
                minorVersion = int(versplit[1])
                releaseVersion = 0
            else:
                self.logger.log(LogPriority.ERROR,
                                "Unexpected version string length")
                raise Exception
            if minorVersion == 10 and releaseVersion < 4:
                self.service = "/System/Library/LaunchDaemons/com.apple." + \
                    "discoveryd.plist"
                self.servicename = "com.apple.networking.discoveryd"
                self.parameter = "--no-multicast"
                self.pbr = self.plb + " -c Print " + self.service + \
                    " | grep 'no-multicast'"
                self.pbf = self.plb + ' -c "Add :ProgramArguments: string ' + \
                    self.parameter + '" ' + self.service
            elif minorVersion > 10:
                self.hasSIP = True
                self.service = "/System/Library/LaunchDaemons/" + \
                    "com.apple.mDNSResponder.plist"
                self.servicename = "com.apple.mDNSResponder.reloaded"
                self.parameter = "NoMulticastAdvertisements"
                self.preferences = "/Library/Preferences/" + \
                    "com.apple.mDNSResponder.plist"
                self.pbr = self.plb + " -c Print " + self.preferences + \
                    " | grep 'NoMulticastAdvertisements'"
                self.pbf = "defaults write " + self.preferences + " " + \
                    self.parameter + " -bool YES"
            else:
                self.service = "/System/Library/LaunchDaemons/" + \
                    "com.apple.mDNSResponder.plist"
                if minorVersion >= 10:
                    self.servicename = "com.apple.mDNSResponder.reloaded"
                else:
                    self.servicename = "com.apple.mDNSResponder"
                self.parameter = "-NoMulticastAdvertisements"
                self.pbr = self.plb + " -c Print " + self.service + \
                    " | grep 'NoMulticastAdvertisements'"
                self.pbf = self.plb + ' -c "Add :ProgramArguments: string ' + \
                    self.parameter + '" ' + self.service
        else:
            self.ismac = False
            # init CIs
            datatype = 'bool'
            mdnskey = 'SECUREMDNS'
            avahikey = 'DISABLEAVAHI'
            mdnsinstructions = 'To configure the Avahi server daemon ' + \
                'securely set the value of SECUREMDNS to True and the ' + \
                'value of DISABLEAVAHI to False.'
            avahiinstructions = 'To completely disable the Avahi server ' + \
                'daemon rather than configure it, set the value of ' + \
                'DISABLEAVAHI to True and the value of SECUREMDNS to False.'
            mdnsdefault = False
            avahidefault = True
            self.SecureMDNS = self.initCi(datatype, mdnskey, mdnsinstructions,
                                          mdnsdefault)
            self.DisableAvahi = self.initCi(datatype, avahikey,
                                            avahiinstructions, avahidefault)

            self.configparser = configparser.SafeConfigParser()

            self.confavahidict = {
                'use-ipv6': {
                    'section': 'server',
                    'val': 'no'
                },
                'check-response-ttl': {
                    'section': 'server',
                    'val': 'yes'
                },
                'disallow-other-stacks': {
                    'section': 'server',
                    'val': 'yes'
                },
                'disable-publishing': {
                    'section': 'publish',
                    'val': 'yes'
                },
                'disable-user-service-publishing': {
                    'section': 'publish',
                    'val': 'yes'
                },
                'publish-addresses': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-hinfo': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-workstation': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-domain': {
                    'section': 'publish',
                    'val': 'no'
                }
            }
            self.confoptions = {
                'server': {
                    'use-ipv6': 'no',
                    'check-response-ttl': 'yes',
                    'disallow-other-stacks': 'yes'
                },
                'publish': {
                    'disable-publishing': 'yes',
                    'disable-user-service-publishing': 'yes',
                    'publish-addresses': 'no',
                    'publish-hinfo': 'no',
                    'publish-workstation': 'no',
                    'publish-domain': 'no'
                }
            }
        self.iditerator = 0

    def report(self):
        '''The report method examines the current configuration and determines
        whether or not it is correct. If the config is correct then the
        self.compliant, self.detailed results and self.currstate properties are
        updated to reflect the system status. self.rulesuccess will be updated
        if the rule does not succeed.


        :returns: bool
        @author: Breen Malmberg
        @change: dwalker - added conditional call to reportmac()
        @change: Breen Malmberg - 12/05/2017 - removed unnecessary argument
                "serviceTarget" in linux-only call to servicehelper; removed
                assignment of unused local variable serviceTarget to self.servicename
                since servicename is not assigned in the linux code logic path (which
                was resulting in variable referenced before assignment error)

        '''

        try:

            # defaults
            compliant = True
            self.detailedresults = ''
            self.rulesuccess = True

            # if system is a mac, run reportmac
            if self.ismac:
                compliant = self.reportmac()

            # if not mac os x, then run this portion
            else:
                self.editor = None
                # set up package helper object only if not mac os x
                self.pkghelper = Pkghelper(self.logger, self.environ)

                # if the disableavahi CI is set, we want to make sure it is
                # completely disabled
                if self.DisableAvahi.getcurrvalue():
                    self.package = "avahi-daemon"
                    # if avahi-daemon is still running, it is not disabled
                    if self.sh.auditService('avahi-daemon'):
                        compliant = False
                        self.detailedresults += 'DisableAvahi has been ' + \
                            'set to True, but avahi-daemon service is ' + \
                            'currently running.\n'
                    self.numdependencies = 0
                    if self.pkghelper.determineMgr() == 'yum' or \
                       self.pkghelper.determineMgr() == 'dnf':
                        self.package = "avahi"
                        # The following KVEditor for /etc/sysconfig/network is
                        # used to meet the zeroconf requirement in
                        # CCE-RHEL7-CCE-TBD 2.5.2
                        path = "/etc/sysconfig/network"
                        self.path = path
                        if os.path.exists(path):
                            tmppath = path + ".tmp"
                            data = {"NOZEROCONF": "yes"}
                            self.editor = KVEditorStonix(
                                self.statechglogger, self.logger, "conf", path,
                                tmppath, data, "present", "closedeq")
                            if not self.editor.report():
                                self.compliant = False
                                self.detailedresults += path + " does not " + \
                                    "have the correct settings.\n"
                        else:
                            self.compliant = False
                            self.detailedresults += path + " does not exist.\n"

                        self.numdependencies = \
                            self.parseNumDependencies(self.package)
                        if self.numdependencies <= 3:
                            if self.pkghelper.check(self.package):
                                compliant = False
                                self.detailedresults += 'DisableAvahi is ' + \
                                    'set to True, but Avahi is currently ' + \
                                    'installed.\n'
                        else:
                            self.detailedresults += 'Avahi has too many ' + \
                                'dependent packages. Will not attempt to ' + \
                                'remove it.\n'

                    elif self.pkghelper.determineMgr() == "zypper":
                        self.package = "avahi"
                    elif self.pkghelper.check(self.package):
                        compliant = False
                        self.detailedresults += 'DisableAvahi is ' + \
                            'set to True, but Avahi is currently ' + \
                            'installed.\n'

                # otherwise if the securemdns CI is set, we want to make sure
                # it is securely configured
                if self.SecureMDNS.getcurrvalue():

                    # if the config file is found, proceed
                    if os.path.exists('/etc/avahi/avahi-daemon.conf'):

                        kvtype = "tagconf"
                        intent = "present"
                        filepath = '/etc/avahi/avahi-daemon.conf'
                        tmpfilepath = '/etc/avahi/avahi-daemon.conf.stonixtmp'
                        conftype = "closedeq"
                        self.avahiconfeditor = KVEditorStonix(
                            self.statechglogger, self.logger, kvtype, filepath,
                            tmpfilepath, self.confoptions, intent, conftype)
                        self.avahiconfeditor.report()
                        if self.avahiconfeditor.fixables:
                            compliant = False
                            self.detailedresults += "\nThe following configuration options are missing or incorrect in " + str(
                                filepath) + ":\n" + "\n".join(
                                    self.avahiconfeditor.fixables)

                    # if config file not found, check if avahi is installed
                    else:

                        # if not installed, we can't configure anything
                        if not self.pkghelper.check('avahi'):
                            self.detailedresults += 'Avahi Daemon not ' + \
                                'installed. Cannot configure it.\n'
                            compliant = True
                            self.logger.log(LogPriority.DEBUG,
                                            self.detailedresults)

                        # if it is installed, then the config file is missing
                        else:
                            compliant = False
                            self.detailedresults += 'Avahi is installed ' + \
                                'but could not find config file in ' + \
                                'expected location.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            self.detailedresults)

            self.compliant = compliant

        except (IOError):
            self.detailedresults += '\n' + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def reportmac(self):
        '''check for configuration items needed for mac os x


        :returns: bool
        @author: Breen Malmberg
        @change: dwalker - implemented kveditor defaults

        '''
        try:
            self.detailedresults = ""
            # See if parameter is set
            self.ch.executeCommand(self.pbr)
            resultOutput = self.ch.getOutput()
            if len(resultOutput) >= 1:
                if (resultOutput[0] == ""):
                    commandsuccess = False
                    self.detailedresults += "Parameter: " + str(self.parameter) + \
                        " for service " + self.servicename + " is not set.\n"
                else:
                    commandsuccess = True
                    debug = "Parameter: " + str(self.parameter) + \
                        " for service " + self.servicename + \
                        " is set correctly."
                    self.logger.log(LogPriority.DEBUG, debug)
            else:
                commandsuccess = False
                self.detailedresults += "Parameter: " + str(self.parameter) + \
                    " for service " + self.servicename + " is not set.\n"
            # see if service is running
            if not re.match("^10.11", self.environ.getosver()):
                servicesuccess = self.sh.auditService(
                    self.service, serviceTarget=self.servicename)
            else:
                servicesuccess = self.sh.auditService(
                    self.service, serviceTarget=self.servicename)
            if servicesuccess:
                debug = "Service: " + str(self.service) + ", " + \
                    self.servicename + " audit successful."
                self.logger.log(LogPriority.DEBUG, debug)
            else:
                self.detailedresults += "Service: " + str(self.service) + \
                    ", " + self.servicename + " audit failed.\n"

            if servicesuccess and commandsuccess:
                return True
            else:
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            raise
        return self.compliant

    def fix(self):
        '''The fix method will apply the required settings to the system.
        self.rulesuccess will be updated if the rule does not succeed.
        
        @author: Breen Malmberg
        @change: dwalker - added statechglogger findrulechanges and deleteentry
        @changed: Breen Malmberg - 12/05/2017 - removed unnecessary servicetarget


        '''

        try:

            self.rulesuccess = True
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            self.detailedresults = ""

            # if this system is a mac, run fixmac()
            if self.ismac:
                self.rulesuccess = self.fixmac()

            # if not mac os x, run this portion
            else:
                # if DisableAvahi CI is enabled, disable the avahi service
                # and remove the package
                if self.DisableAvahi.getcurrvalue():
                    avahi = self.package
                    avahid = 'avahi-daemon'
                    if self.sh.auditService(avahid):
                        debug = "Disabling " + avahid + " service"
                        self.logger.log(LogPriority.DEBUG, debug)
                        self.sh.disableService(avahid)
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "servicehelper",
                            "servicename": avahid,
                            "startstate": "enabled",
                            "endstate": "disabled"
                        }
                        self.statechglogger.recordchgevent(myid, event)
                    if self.environ.getosfamily() == 'linux' and \
                       self.pkghelper.check(avahi):
                        if self.numdependencies <= 3:
                            self.pkghelper.remove(avahi)
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                "eventtype": "pkghelper",
                                "pkgname": avahi,
                                "startstate": "installed",
                                "endstate": "removed"
                            }
                            self.statechglogger.recordchgevent(myid, event)
                        else:
                            debug += 'Avahi package has too many dependent ' \
                                + 'packages. Will not attempt to remove.\n'
                            self.logger.log(LogPriority.DEBUG, debug)

                    if self.pkghelper.determineMgr() == 'yum' or \
                       self.pkghelper.determineMgr() == 'dnf':
                        path = self.path
                        if not os.path.exists(path):
                            if createFile(path, self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                event = {
                                    "eventtype": "creation",
                                    "filepath": path
                                }
                                self.statechglogger.recordchgevent(myid, event)
                            else:
                                self.rulesuccess = False
                                self.detailedresults += "Failed to create " + \
                                    "file: " + path + ".\n"
                        if self.editor is None:
                            tmppath = path + ".tmp"
                            data = {"NOZEROCONF": "yes"}
                            self.editor = KVEditorStonix(
                                self.statechglogger, self.logger, "conf", path,
                                tmppath, data, "present", "closedeq")
                        if not self.editor.report():
                            if self.editor.fix():
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                self.editor.setEventID(myid)
                                if not self.editor.commit():
                                    self.rulesuccess = False
                                    self.detailedresults += "Could not " + \
                                        "commit changes to " + path + ".\n"
                            else:
                                self.rulesuccess = False
                                self.detailedresults += "Could not fix " + \
                                    "file " + path + ".\n"

                # if SecureMDNS CI is enabled, configure avahi-daemon.conf
                if self.SecureMDNS.getcurrvalue():
                    # if config file is present, proceed
                    avahiconf = '/etc/avahi/avahi-daemon.conf'
                    if os.path.exists(avahiconf):
                        if self.avahiconfeditor.fixables:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.avahiconfeditor.setEventID(myid)
                            if not self.avahiconfeditor.fix():
                                self.rulesuccess = False
                                debug = "KVEditor fix for " + avahiconf + \
                                    "failed"
                                self.logger.log(LogPriority.DEBUG, debug)
                            elif not self.avahiconfeditor.commit():
                                self.rulesuccess = False
                                debug = "KVEditor commit for " + avahiconf + \
                                    "failed"
                                self.logger.log(LogPriority.DEBUG, debug)

                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        setPerms(avahiconf, [0, 0, 0o644], self.logger,
                                 self.statechglogger, myid)
                        resetsecon(avahiconf)

                    # if config file is not present and avahi not installed,
                    # then we can't configure it
                    else:
                        if not self.pkghelper.check(avahi):
                            debug = 'Avahi Daemon not installed. ' + \
                                'Cannot configure it.'
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.detailedresults += 'Avahi daemon ' + \
                                'installed, but could not locate the ' + \
                                'configuration file for it.\n'
                            self.rulesuccess = False

        except IOError:
            self.detailedresults += '\n' + traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def fixmac(self):
        '''apply fixes needed for mac os x
        
        @author: Breen Malmberg
        @change: dwalker - implemented kveditor instead of direct editing


        '''
        try:
            self.detailedresults = ""
            success = True
            # See if parameter is set
            self.ch.executeCommand(self.pbr)
            resultOutput = self.ch.getOutput()
            if len(resultOutput) >= 1:
                if (resultOutput[0] == ""):
                    fixit = True
                else:
                    fixit = False
            else:
                fixit = True
            # Add parameter
            if fixit:
                # Due to weaknesses in using PlistBuddy and defaults to delete
                # from plists, as well as shortcomings in STONIX's state change
                # logging, we will record this change as a file deletion.
                # If the rule's undo is run on OS X, it will restore the
                # previous version of this file.
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if self.hasSIP:
                    self.statechglogger.recordfiledelete(
                        self.preferences, myid)
                else:
                    self.statechglogger.recordfiledelete(self.service, myid)
                self.ch.executeCommand(self.pbf)
                resultOutput = self.ch.getOutput()
                errorcode = self.ch.getReturnCode()
                if errorcode == 0:
                    debug = self.parameter + " was set successfully!"
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    self.detailedresults += self.parameter + \
                        " was not set successfully!\n"
                    self.statechglogger.deleteentry(myid)
                    success = False
            else:
                debug = self.parameter + " was already set!"
                self.logger.log(LogPriority.DEBUG, debug)
            # Reload Service
            if success:
                success = self.sh.reloadService(self.service,
                                                serviceTarget=self.servicename)
                if success:
                    debug = "Service: " + str(self.service) + ", " + \
                        self.servicename + " was reloaded successfully."
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    debug = "Service: " + str(self.service) + ", " + \
                        self.servicename + " reload failed!"
                    self.logger.log(LogPriority.DEBUG, debug)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

    def parseNumDependencies(self, pkgname):
        '''parse output of yum command to determine number of dependent packages
        to the given pkgname

        :param pkgname: 
        :returns: int
        @author: Breen Malmberg

        '''
        numdeps = 0
        flag = 0

        try:

            if self.pkghelper.determineMgr() == 'zypper':
                command = ['zypper', 'info', '--requires', pkgname]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if flag:
                        numdeps += 1
                    if re.search('Requires:', line):
                        flag = 1

            elif self.pkghelper.determineMgr() == 'yum':
                command = ['yum', '--assumeno', 'remove', pkgname]
                self.ch.wait = False
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Dependent packages\)', line):
                        sline = line.split('(+')
                        if len(sline) < 2:
                            return numdeps
                        cline = [
                            int(s) for s in sline[1].split() if s.isdigit()
                        ]
                        numdeps = int(cline[0])

            elif self.pkghelper.determineMgr() == 'dnf':
                command = ['dnf', '--assumeno', 'remove', pkgname]
                self.ch.wait = False
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Dependent packages\)', line):
                        sline = line.split('(+')
                        if len(sline) < 2:
                            return numdeps
                        cline = [
                            int(s) for s in sline[1].split() if s.isdigit()
                        ]
                        numdeps = int(cline[0])

            elif self.pkghelper.determineMgr() == 'apt-get':
                command = ['apt-cache', 'depends', pkgname]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Depends:', line):
                        numdeps += 1
            else:
                self.detailedresults += 'Unable to detect package manager\n'
                return numdeps

        except (IOError, OSError):
            self.detailedresults += 'Specified package: ' + str(pkgname) + \
                ' not found.\n'
            return numdeps
        except Exception:
            raise
        return numdeps
Ejemplo n.º 28
0
class DisableIPV6(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 123
        self.rulename = "DisableIPV6"
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 2.5.3.1"]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # configuration item instantiation
        datatype = 'bool'
        key = 'DISABLEIPV6'
        instructions = "To disable this rule set the value of DISABLEIPV6 " + \
            "to False."
        default = False
        self.ci = self.initCi(datatype, key, instructions, default)

        self.iditerator = 0
        self.created = False
        self.created2 = False
        # self.editor1: sysctl file editor
        # self.editor2: network file editor
        # self.editor3: sshd file editor
        self.editor1, self.editor2, self.editor3 = "", "", ""
        self.sh = ServiceHelper(self.environ, self.logger)
        self.sethelptext()

    def report(self):
        try:
            self.detailedresults = ""
            if self.environ.getosfamily() == "linux":
                self.ph = Pkghelper(self.logger, self.environ)
                self.compliant = self.reportLinux()
            elif self.environ.getosfamily() == "freebsd":
                self.compliant = self.reportFree()
            elif self.environ.getostype() == "Mac OS X":
                self.compliant = self.reportMac()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            self.detailedresults = ""

            # clear out event history so only the latest fix is recorded
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            if self.environ.getosfamily() == "linux":
                self.rulesuccess = self.fixLinux()
            elif self.environ.getosfamily() == "freebsd":
                self.rulesuccess = self.fixFree()
            elif self.environ.getosfamily() == "darwin":
                self.rulesuccess = self.fixMac()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def reportMac(self):
        self.ifaces = []
        compliant = True
        self.cmdhelper = CommandHelper(self.logger)
        cmd = ["/usr/sbin/networksetup", "-listallnetworkservices"]
        if not self.cmdhelper.executeCommand(cmd):
            self.detailedresults += "Unable to run " + \
                "networksetup -listallnetworkservices command\n"
            return False
        output = self.cmdhelper.getOutput()
        for item in output:
            item = item.strip()
            cmd = ["/usr/sbin/networksetup", "-getinfo", item]
            if not self.cmdhelper.executeCommand(cmd):
                self.detailedresults += "Unable to run " + \
                    "networksetup -getinfo command\n"
                return False
            output2 = self.cmdhelper.getOutput()
            for item2 in output2:
                if re.search("^IPv6:", item2):
                    check = item2.split(":")
                    if check[1].strip() != "Off":
                        self.detailedresults += "IPV6 is not turned off " + \
                            "for " + item + " interface\n"
                        self.ifaces.append(item)
                        compliant = False
        return compliant

    def fixMac(self):
        if self.ifaces:
            for item in self.ifaces:
                cmd = ["/usr/sbin/networksetup", "setv6off", item]
                if not self.cmdhelper.executeCommand(cmd):
                    self.rulesuccess = False
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        'eventtype':
                        'comm',
                        'command':
                        ['/usr/sbin/networksetup', 'setv6automatic', item]
                    }
                    self.statechglogger.recordchgevent(myid, event)
        return self.rulesuccess

    def reportFree(self):
        compliant = True
        self.editor1, self.editor2 = "", ""

        directives1 = {
            "ipv6_network_interfaces": "none",
            "ipv6_activate_all_interfaces": "NO",
            "ip6addrctl_enable": "NO",
            "ip6addrctl_policy": "NO"
        }
        directives2 = {
            "net.ipv6.conf.all.disable_ipv6": "1",
            "net.ipv6.conf.default.disable_ipv6": "1",
            "net.ipv6.conf.lo.disable_ipv6": "1"
        }

        path1 = "/etc/rc.conf"
        path2 = "/etc/sysctl.conf"
        tmpfile1 = "/etc/rc.conf.tmp"
        tmpfile2 = "/etc/sysctl.conf.tmp"

        # try and create /etc/rc.conf if doesn't exist
        if not os.path.exists(path1):
            if not createFile(path1, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path1 + ", so this file will not be configured, " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 420], self.logger):
                compliant = False
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path1, tmpfile1, directives1,
                                          "present", "closedeq")
            if not self.editor1.report():
                compliant = False

        # try and create /etc/sysctl.conf if doesn't exist
        if not os.path.exists(path2):
            if not createFile(path2, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path2 + " so this file will not be configured " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path2):
            if not checkPerms(path2, [0, 0, 384], self.logger):
                compliant = False
            self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path2, tmpfile2, directives2,
                                          "present", "closedeq")
            if not self.editor2.report():
                compliant = False
        else:
            compliant = False

        cmdhelper = CommandHelper(self.logger)
        cmd = ["/sbin/ifconfig", "-a"]
        if not cmdhelper.executeCommand(cmd):
            return False
        output = cmdhelper.getOutput()
        for line in output:
            line = line.strip()
            if re.search("^nd6", line):
                if not re.search("(.)*IFDISABLED(.)*", line):
                    compliant = False
        return compliant

    def fixFree(self):
        # debug messages are used for developers, self.detailedresults
        # are used for the users information
        path1 = "/etc/rc.conf"
        path2 = "/etc/sysctl.conf"
        success = True
        debug = ""
        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 420], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(self.path, [0, 0, 420], self.logger,
                                self.statechglogger, myid):
                    success = False
            if self.editor1:
                if self.editor1.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        debug += "Kveditor unable to correct file: " + \
                            path1 + "\n"
                        self.detailedresults += "Unable to correct " + path1 + \
                            "\n"
                        success = False
                    elif not self.editor1.commit():
                        self.detailedresults += "Unable to correct " + path1 + \
                            "\n"
                        debug += "commit for kveditor1 was not successful\n"
                        success = False
            else:
                debug += "Editor2 was never created so path didn't exist \
and/or wasn't able to be created\n"

                success = False
        else:
            self.detailedresults += path1 + " doesn't exist!\n"
            debug += path1 + " doesn't exist, unble to fix file\n"
            success = False

        if os.path.exists(path2):
            # check permissions on /etc/sysctl.conf
            if not checkPerms(path2, [0, 0, 384], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)

                # set permissions if wrong
                if not setPerms(self.path, [0, 0, 384, self.logger],
                                self.statechglogger, myid):
                    success = False
            # check if editor is present
            if self.editor2:
                if self.editor2.fixables():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        debug += "Kveditor unable to correct file: " + \
                            path2 + "\n"
                        self.detailedresults += "Unable to correct " + path2 + \
                            "\n"
                        success = False
                    elif not self.editor2.commit():
                        self.detailedresults += "Unable to correct " + path2 + \
                            "\n"
                        debug += "commit for kveditor2 was not successful\n"
                        success = False
            else:
                debug += "Editor2 was never created so path didn't exist \
and/or wasn't able to be created\n"

                success = False
        else:
            self.detailedresults += path2 + " doesn't exist!\n"
            debug += path2 + " doesn't exist, unble to fix file\n"
            success = False

        # restart the network
        ch = CommandHelper(self.logger)
        cmd = ["/etc/rc.d/netif", "restart"]
        if not ch.executeCommand(cmd):
            self.detaileresults += "Unable to restart network\n"
            success = False
        return success

    def reportLinux(self):
        self.ch = CommandHelper(self.logger)
        compliant = True
        netwrkfile = ""
        ifacefile = ""
        self.modprobefiles = []
        #self.modprobeOK = False
        self.modprobeOK = True
        sysctl = "/etc/sysctl.conf"
        self.interface = {"IPV6INIT": "no", "NETWORKING_IPV6": "no"}
        self.sysctls = {
            "net.ipv6.conf.all.disable_ipv6": "1",
            "net.ipv6.conf.default.disable_ipv6": "1"
        }
        self.modprobes = {
            "options": ["ipv6 disable=1"],
            "install": ["ipv6 /bin/true"]
        }

        # stig portion, check netconfig file for correct contents
        if self.ph.manager == "apt-get":
            nfspkg = "nfs-common"
        else:
            nfspkg = "nfs-utils.x86_64"
        if self.ph.check(nfspkg):
            if os.path.exists("/etc/netconfig"):
                item1 = "udp6 tpi_clts v inet6 udp - -"
                item2 = "tcp6 tpi_cots_ord v inet6 tcp - -"
                contents = readFile("/etc/netconfig", self.logger)
                for line in contents:
                    line = re.sub("\s+", " ", line.strip())
                    if re.search(item1, line) or re.search(item2, line):
                        self.detailedresults += "/etc/netconfig file contains " + \
                            "lines we don't want present\n"
                        compliant = False

        # "ifconfig" has been deprecated on Debian9 and some otherd distros
        # so use "ip addr" instead
        # Here we check if the system is giving out ipv6 ip addresses
        if os.path.exists("/sbin/ifconfig"):
            cmd = ["/sbin/ifconfig"]
        else:
            cmd = ["/sbin/ip", "addr"]

        if not self.ch.executeCommand(cmd):
            compliant = False
        else:
            output = self.ch.getOutput()
            for line in output:
                if re.search("^inet6", line.strip()):
                    self.detailedresults += "inet6 exists in the " + \
                        "ifconfig output\n"
                    compliant = False
                    break

        # check for ipv6 address in hostname file
        if os.path.exists("/etc/hosts"):
            contents = readFile("/etc/hosts", self.logger)
            for line in contents:
                if re.search("^#", line) or re.match("^\s*$", line):
                    continue
                if re.search(":", line):
                    compliant = False

        # check compliancy of /etc/sysctl.conf file
        if not os.path.exists(sysctl):
            self.detailedresults += "File " + sysctl + " does not exist\n"
            compliant = False
        else:
            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile,
                                          self.sysctls, "present", "openeq")
            if not self.editor1.report():
                self.detailedresults += "/etc/sysctl file doesn't contain \
                    the correct contents\n"

                compliant = False
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions for " + sysctl + \
                    "are incorrect\n"
                compliant = False

        # in addition to checking /etc/sysctl.conf contents we need to
        # also check sysctl compliancy using the sysctl command
        for key in self.sysctls:
            self.ch.executeCommand("/sbin/sysctl " + key)
            retcode = self.ch.getReturnCode()
            output = self.ch.getOutputString()
            errmsg = output + self.ch.getErrorString()
            if retcode != 0:
                if re.search("unknown key", errmsg):
                    continue
                else:
                    self.detailedresults += "Failed to get value " + key + " using sysctl command\n"
                    errmsg = self.ch.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errmsg)
                    compliant = False
            else:
                actualoutput = output.strip()
                expectedoutput = key + " = " + self.sysctls[key]
                if actualoutput != expectedoutput:
                    compliant = False
                    self.detailedresults += "\nsysctl output has " + \
                                            "incorrect value: expected " + \
                                            expectedoutput + ", found " + \
                                            actualoutput + "\n"
        # check files inside modprobe.d directory for correct contents
        if os.path.exists("/etc/modprobe.d/"):
            modprobefiles = glob.glob("/etc/modprobe.d/*")
            for modfile in modprobefiles:
                tmpfile = ""
                modprobekveditor = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", modfile,
                                                  tmpfile, self.modprobes,
                                                  "present", "space")
                if modprobekveditor.report():
                    self.modprobeOK = True
                    break
            if not self.modprobeOK:
                self.detailedresults += "Didn't find desired contents inside files " + \
                    "within /etc/modprobe.d/"
                compliant = False
        else:
            # system isn't using loadable kernel modules, not an issue
            self.modprobeOK = True

        # Check for existence of interface and network files to be configured
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            if not os.path.exists(ifacefile):
                ifacefile = ""
            netwrkfile = "/etc/sysconfig/network"
            if not os.path.exists(netwrkfile):
                netwrkfile = ""
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
            if not os.path.exists(ifacefile):
                ifacefile = ""

        # go through interface directory and check each interface file
        # for correct contents
        if ifacefile:
            dirs = glob.glob(ifacefile + '*')
            for loc in dirs:
                contents = []
                if re.search('^' + ifacefile + 'ifcfg', loc):
                    if not checkPerms(loc, [0, 0, 0o644], self.logger):
                        compliant = False
                    contents = readFile(loc, self.logger)
                    if contents:
                        for key in self.interface:
                            found = False
                            for line in contents:
                                if re.search("^#", line) or re.match(
                                        "^\s*$", line):
                                    continue
                                if re.search("^" + key, line):
                                    if re.search("=", line):
                                        temp = line.split("=")
                                        if temp[1].strip(
                                        ) == self.interface[key]:
                                            found = True
                                            continue
                                        else:
                                            found = False
                                            break
                                    else:
                                        compliant = False
                                        self.detailedresults += loc + \
                                            " file in bad format\n"
                            if not found:
                                self.detailedresults += "contents of " + \
                                    loc + " file is wrong\n"
                                compliant = False
                                break
                            else:
                                continue
                    else:
                        compliant = False

        # check network file for correct contents
        if netwrkfile:
            if os.path.exists(netwrkfile):
                if not checkPerms(netwrkfile, [0, 0, 0o644], self.logger):
                    compliant = False
                tmpfile = netwrkfile + ".tmp"
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", netwrkfile, tmpfile,
                                              self.interface, "present",
                                              "closedeq")
                if not self.editor2.report():
                    self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"

                    compliant = False
            else:
                self.detailedresults += netwrkfile + " doesn't exist\n"
                compliant = False

        # This subpart is only for apt-get based systems
        # sshd needs the inet directive for ipv6 disablement
        if self.ph.manager == "apt-get":
            data = {"AddressFamily": "inet"}
            kvtype = "conf"
            path = "/etc/ssh/sshd_config"
            tmpPath = path + ".tmp"
            intent = "present"
            configtype = "space"
            self.editor3 = KVEditorStonix(self.statechglogger, self.logger,
                                          kvtype, path, tmpPath, data, intent,
                                          configtype)
            if not self.editor3.report():
                self.detailedresults += "/etc/ssh/ssdh_config doesn't \
contain the correct contents\n"

                compliant = False
        return compliant

    def fixLinux(self):
        '''
        @change: dkennel removed extraneous arg from setperms call on 864
        '''
        universal = "#The following lines were added by stonix\n"
        debug = ""
        success = True
        ifacefile = ""
        netwrkfile = ""
        sysctl = "/etc/sysctl.conf"
        blacklistfile = "/etc/modprobe.d/stonix-blacklist.conf"

        # STIG portion, correct netconfig file
        if self.ph.manager == "apt-get":
            nfspkg = "nfs-common"
        else:
            nfspkg = "nfs-utils.x86_64"
        # if package not installed, no need to configure it
        if self.ph.check(nfspkg):
            if os.path.exists("/etc/netconfig"):
                filestring = ""
                # we want to make sure the following two lines don't
                # appear in the netconfig file
                item1 = "udp6 tpi_clts v inet6 udp - -"
                item2 = "tcp6 tpi_cots_ord v inet6 tcp - -"
                contents = readFile("/etc/netconfig", self.logger)
                for line in contents:
                    templine = re.sub("\s+", " ", line.strip())
                    # if we find the lines, skip them thus leaving them out of
                    # of the rewrite
                    if re.search(item1, templine) or re.search(
                            item2, templine):
                        continue
                    else:
                        filestring += line
                tmpfile = "/etc/netconfig.tmp"
                if not writeFile(tmpfile, filestring, self.logger):
                    success = False
                else:
                    # record event, rename file, set perms
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": "/etc/netconfig"}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(
                        "/etc/netconfig", tmpfile, myid)
                    os.rename(tmpfile, "/etc/netconfig")
                    os.chown("/etc/netconfig", 0, 0)
                    os.chmod("/etc/netconfig", 420)
                    resetsecon("/etc/netconfig")

        # remove any ipv6 addresses from /etc/hosts file
        if os.path.exists("/etc/hosts"):
            contents = readFile("/etc/hosts", self.logger)
            tempstring = ""
            tmpfile = "/etc/hosts.tmp"
            for line in contents:
                if re.search("^#", line) or re.match("^\s*$", line):
                    tempstring += line
                    continue
                elif re.search(":", line):
                    tempstring += "#" + line
                else:
                    tempstring += line
            if writeFile(tmpfile, tempstring, self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": "/etc/hosts"}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange("/etc/hosts", tmpfile,
                                                     myid)
                os.rename(tmpfile, "/etc/hosts")
                os.chown("/etc/hosts", 0, 0)
                os.chmod("/etc/hosts", 420)
                resetsecon("/etc/hosts")
            else:
                success = False
                debug = "Unable to write to file /etc/hosts\n"
                self.logger.log(LogPriority.DEBUG, debug)

        # fix sysctl / tuning kernel parameters
        # manually write key value pairs to /etc/sysctl.conf
        created = False
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                success = False
                debug = "Unable to create " + sysctl + "\n"
                self.logger.log(LogPriority.DEBUG, debug)
        if os.path.exists(sysctl):
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False

            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile,
                                          self.sysctls, "present", "openeq")
            if not self.editor1.report():
                if self.editor1.fixables:
                    # If we did not create the file, set an event ID for the
                    # KVEditor's undo event
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor1.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                        if not setPerms(self.path, [0, 0, 0o644], self.logger):
                            self.detailedresults += "Could not set permissions on " + \
                                                    self.path + "\n"
                            success = False
                    resetsecon(sysctl)

        # here we also check the output of the sysctl command for each key
        # to cover all bases
        for key in self.sysctls:
            if self.ch.executeCommand("/sbin/sysctl " + key):
                output = self.ch.getOutputString().strip()
                errmsg = output + self.ch.getErrorString()
                if re.search("unknown key", errmsg):
                    continue
                if not re.search(self.sysctls[key] + "$", output):
                    undovalue = output[-1]
                    self.ch.executeCommand("/sbin/sysctl -q -e -w " + key +
                                           "=" + self.sysctls[key])
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        success = False
                        self.detailedresults += "Failed to set " + key + " = " + self.sysctls[
                            key] + "\n"
                        errmsg = self.ch.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errmsg)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        command = "/sbin/sysctl -q -e -w " + key + "=" + undovalue
                        event = {
                            "eventtype": "commandstring",
                            "command": command
                        }
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Unable to get value for " + key + "\n"
                success = False
        # at the end do a print and ignore any key errors to ensure
        # the new values are read into the kernel
        self.ch.executeCommand("/sbin/sysctl -q -e -p")
        retcode2 = self.ch.getReturnCode()
        if retcode2 != 0:
            success = False
            self.detailedresults += "Failed to load new sysctl configuration from config file\n"
            errmsg2 = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg2)

        # We never found the correct contents in any of the modprobe.d files
        # so we're going to created the stonix-blacklist file
        # this file is used in other rules
        if not self.modprobeOK:
            created = False
            tmpfile = blacklistfile + ".tmp"
            modprobekveditor = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", blacklistfile, tmpfile,
                                              self.modprobes, "notpresent",
                                              "space")
            if not os.path.exists(blacklistfile):
                # create the file and record the event as file creation
                if createFile(blacklistfile, self.logger):
                    created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "creation",
                        "filepath": blacklistfile
                    }
                    self.statechglogger.recordchgevent(myid, event)
            if os.path.exists(blacklistfile):
                if not modprobekveditor.report():
                    if not modprobekveditor.fix():
                        success = False
                        self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"
                    else:
                        # if the file was created, then we already recorded an event
                        # for that, so this step would get skipped
                        if not created:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            modprobekveditor.setEventID(myid)
                        if not modprobekveditor.commit():
                            success = False
                            self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"

        # fix ifcfg (interface) files
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            netwrkfile = "/etc/sysconfig/network"
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
        if ifacefile:
            if os.path.exists(ifacefile):
                dirs = glob.glob(ifacefile + "*")
                if dirs:
                    for loc in dirs:
                        interface = {"IPV6INIT": "no", "NETWORKING_IPV6": "no"}
                        interface2 = {
                            "IPV6INIT": "no",
                            "NETWORKING_IPV6": "no"
                        }
                        found = False
                        tempstring = ""
                        if re.search('^' + ifacefile + 'ifcfg', loc):
                            filename = loc
                            tmpfile = filename + ".tmp"
                            contents = readFile(filename, self.logger)
                            if not checkPerms(filename, [0, 0, 420],
                                              self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                if not setPerms(filename, [0, 0, 420],
                                                self.logger,
                                                self.statechglogger, myid):
                                    debug = "Unable to set permissions on " + \
                                        filename + "\n"
                                    self.logger.log(LogPriority.DEBUG, debug)
                                    success = False
                            for key in interface:
                                found = False
                                for line in contents:
                                    if re.search("^#", line) or \
                                       re.match("^\s*$", line):
                                        continue
                                    if re.search("^" + key, line):
                                        if re.search("=", line):
                                            temp = line.split("=")
                                            if temp[1].strip(
                                            ) == interface[key]:
                                                if found:
                                                    continue
                                                found = True
                                            else:
                                                contents.remove(line)
                                if found:
                                    del interface2[key]
                            for line in contents:
                                tempstring += line
                            tempstring += universal
                            for key in interface2:
                                tempstring += key + "=" + interface2[key] + \
                                    "\n"
                            if not writeFile(tmpfile, tempstring, self.logger):
                                success = False
                                debug = "Unable to write to file " + loc + "\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {'eventtype': 'conf', 'filepath': filename}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(
                                filename, tmpfile, myid)
                            os.rename(tmpfile, filename)
                            os.chown(filename, 0, 0)
                            os.chmod(filename, 420)
                            resetsecon(filename)
            elif not os.path.exists(ifacefile) and ifacefile != "":
                # will not attempt to create the interface files
                debug = "interface directory which holds interface \
                files, doesn't exist, stonix will not attempt to make this \
                directory or the files contained therein"

                success = False
                self.logger.log(LogPriority.DEBUG, debug)

        # fix network file if it exists
        if netwrkfile:
            if not os.path.exists(netwrkfile):
                if not createFile(netwrkfile, self.logger):
                    debug = "Unable to create " + netwrkfile + "file\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
                else:
                    if not checkPerms(netwrkfile, [0, 0, 420], self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(netwrkfile, [0, 0, 420], self.logger,
                                        self.statechglogger, myid):
                            debug = "Unable to set permissions on " + \
                                    netwrkfile + "\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                    tmpfile = netwrkfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf",
                                                  netwrkfile, tmpfile,
                                                  self.interface, "present",
                                                  "closedeq")
                    if not self.editor2.report():
                        self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"

            if self.editor2:
                if self.editor2.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor2.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    os.chown(netwrkfile, 0, 0)
                    os.chmod(netwrkfile, 420)
                    resetsecon(netwrkfile)

        # fix sshd_config file for apt-get systems if ssh is installed
        if self.ph.manager == "apt-get":
            if not os.path.exists("/etc/ssh/sshd_config"):
                msg = "/etc/ssh/ssd_config doesn\'t exist.  This could mean ssh \
    is not installed or the file has been inadvertantly deleted.  Due to the \
    complexity of this file stonix will not attempt to create this file"

                self.logger.log(LogPriority.DEBUG, msg)
                success = False
            else:
                if not checkPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                  self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                    self.logger, self.statechglogger, myid):
                        success = False
                        debug = "Unable to set permissions on " + \
                            "/etc/ssh/sshd_config\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                if self.editor3:
                    if self.editor3.fixables:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor3.setEventID(myid)
                        if not self.editor3.fix():
                            success = False
                            debug = "Unable to complete kveditor fix method" + \
                                "for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        elif not self.editor3.commit():
                            success = False
                            debug = "Unable to complete kveditor commit " + \
                                "method for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        os.chown("/etc/ssh/sshd_config", 0, 0)
                        os.chmod("/etc/ssh/sshd_config", 420)
                        resetsecon("/etc/ssh/sshd_config")
        return success
Ejemplo n.º 29
0
class MinimizeServices(Rule):
    '''The MinimizeServices class sets the system so that only approved services
    are running at any given time.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 12
        self.rulename = 'MinimizeServices'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {'type': 'black', 'family': ['darwin']}
        self.servicehelper = ServiceHelper(self.environ, self.logger)
        self.guidance = [
            'NSA 2.1.2.2', 'NSA 2.2.2.3', 'NSA 2.4.3', 'NSA 3.1', 'CCE-3416-5',
            'CCE-4218-4', 'CCE-4072-5', 'CCE-4254-9', 'CCE-3668-1',
            'CCE-4129-3', 'CCE-3679-8', 'CCE-4292-9', 'CCE-4234-1',
            'CCE-4252-3', 'CCE-3390-2', 'CCE-3974-3', 'CCE-4141-8',
            'CCE-3537-8', 'CCE-3705-1', 'CCE-4273-9', 'CCE-3412-4',
            'CCE-4229-1', 'CCE-4123-6', 'CCE-4286-1', 'CCE-3425-6',
            'CCE-4211-9', 'CCE-3854-7', 'CCE-4356-2', 'CCE-4369-5',
            'CCE-4100-4', 'CCE-3455-3', 'CCE-4421-4', 'CCE-4302-6',
            'CCE-3822-4', 'CCE-4364-6', 'CCE-4355-4', 'CCE-4377-8',
            'CCE-4289-5', 'CCE-4298-6', 'CCE-4051-9', 'CCE-4324-0',
            'CCE-4406-5', 'CCE-4268-9', 'CCE-4303-4', 'CCE-4365-3',
            'CCE-3755-6', 'CCE-4425-5', 'CCE-4191-3', 'CCE-4336-4',
            'CCE-4376-0', 'CCE-4416-4', 'CCE-3501-4', 'CCE-4396-8',
            'CCE-3535-2', 'CCE-3568-3', 'CCE-4533-6', 'CCE-4550-0',
            'CCE-4473-5', 'CCE-4491-7', 'CCE-3578-2', 'CCE-3919-8',
            'CCE-4338-0', 'CCE-3847-1', 'CCE-4551-8', 'CCE-4556-7',
            'CCE-3765-5', 'CCE-4508-8', 'CCE-4327-3', 'CCE-4468-5',
            'CCE-4512-0', 'CCE-4375-2', 'CCE-4393-5', 'CCE-3662-4',
            'CCE-4442-0', 'CCE-4596-3', 'CCE-4486-7', 'CCE-4362-0',
            'CCE-3622-8', 'CCE-4299-4', 'CCE-4592-2', 'CCE-4614-4',
            'CCE-4279-6', 'CCE-4557-5', 'CCE-4588-0', 'CCE-4354-7',
            'CCE-4240-8', 'CCE-4517-9', 'CCE-4284-6', 'CCE-4429-7',
            'CCE-4306-7', 'CCE-4499-0', 'CCE-4266-3', 'CCE-4411-5',
            'CCE-4305-9', 'CCE-4477-6', 'CCE-3650-9', 'CCE-4571-6',
            'CCE-3950-3', 'CCE-4470-1', 'CCE-4598-9', 'CCE-4620-1',
            'CCE-4333-1', 'CCE-3857-0', 'CCE-4359-6', 'CCE-4615-1',
            'CCE-4007-1', 'CCE-3901-6', 'CCE-4553-4', 'CCE-4584-9',
            'CCE-4611-0', 'CCE-3655-8', 'CCE-4541-9', 'CCE-4483-4',
            'CCE-3663-2', 'CCE-4037-8'
        ]
        self.bsddefault = [
            'background_fsck', 'cleanvar', 'cron', 'devd', 'dmesg', 'gptboot',
            'hostid', 'ip6addrctl', 'mixer', 'newsyslog', 'ntpd',
            'sendmail_submit', 'sendmail_submit', 'sendmail_msp_queue', 'sshd',
            'syslogd', 'virecover'
        ]
        self.linuxdefault = [
            'acpi-support', 'acpid', 'alsa-restore', 'alsa-store', 'alsasound',
            'anacron', 'apmd', 'atd', 'audit', 'auditd', 'autofs',
            'blk-availability', 'bootlogd', 'cpuspeed', 'cron', 'crond',
            'cups', 'dbus', 'earlysyslog', 'exim4', 'fbset', 'firstboot',
            'gdm3', 'grub-common', 'haldaemon', 'haveged', 'iptables',
            'ip6tables', 'irqbalance', 'kbd', 'libnss-ldap', 'lm_sensors',
            'lvm2-monitor', 'mcstrans', 'mdmonitor', 'messagebus', 'minissdpd',
            'motd', 'netfs', 'network', 'network-manager', 'NetworkManager',
            'network-remotefs', 'nfs-common', 'nfslock', 'nscd', 'nslcd',
            'ntpd', 'oddjobd', 'osad', 'ondemand', 'portmap', 'postfix',
            'psacct', 'pulseaudio', 'purge-kernels', 'random', 'rasagent',
            'rc.local', 'rdma', 'restorecond', 'rhnsd', 'rmnologin', 'rpcgssd',
            'rpcidmapd', 'rpcbind', 'rsyslog', 'rsyslogd', 'sendmail',
            'setroubleshoot', 'smartd', 'splash', 'splash-early', 'sshd',
            'sssd', 'syslog', 'sysstat', 'udev-post', 'xfs'
        ]
        self.specials = [
            'apparmor', 'urandom', 'x11-common', 'sendsigs', 'unmountnfs.sh',
            'networking', 'umountfs', 'umountroot', 'reboot', 'halt',
            'killprocs', 'single', 'unattended-upgrades', 'devfs', 'dmesg',
            'bootmisc', 'fsck', 'hostname', 'hwclock', 'keymaps', 'localmount',
            'modules', 'mount-ro', 'mtab', 'net.lo', 'procfs', 'root',
            'savecache', 'swap', 'sysctl', 'termencoding', 'udev'
        ]
        self.soldefault = [
            'lrc:/etc/rc2_d/S10lu', 'lrc:/etc/rc2_d/S20sysetup',
            'lrc:/etc/rc2_d/S40llc2', 'lrc:/etc/rc2_d/S42ncakmod',
            'lrc:/etc/rc2_d/S47pppd', 'lrc:/etc/rc2_d/S70uucp',
            'lrc:/etc/rc2_d/S72autoinstall',
            'lrc:/etc/rc2_d/S73cachefs_daemon',
            'lrc:/etc/rc2_d/S81dodatadm_udaplt', 'lrc:/etc/rc2_d/S89PRESERVE',
            'lrc:/etc/rc2_d/S89bdconfig', 'lrc:/etc/rc2_d/S91ifbinit',
            'lrc:/etc/rc2_d/S91jfbinit', 'lrc:/etc/rc2_d/S94ncalogd',
            'lrc:/etc/rc2_d/S98deallocate', 'lrc:/etc/rc3_d/S16boot_server',
            'lrc:/etc/rc3_d/S50apache', 'lrc:/etc/rc3_d/S52imq',
            'lrc:/etc/rc3_d/S80mipagent', 'lrc:/etc/rc3_d/S84appserv',
            'svc:/system/svc/restarter:default',
            'svc:/system/installupdates:default', 'svc:/network/pfil:default',
            'svc:/network/tnctl:default', 'svc:/network/loopback:default',
            'svc:/system/filesystem/root:default',
            'svc:/system/scheduler:default', 'svc:/network/physical:default',
            'svc:/system/identity:node', 'svc:/system/boot-archive:default',
            'svc:/system/filesystem/usr:default', 'svc:/system/keymap:default',
            'svc:/system/device/local:default',
            'svc:/system/filesystem/minimal:default',
            'svc:/system/resource-mgmt:default',
            'svc:/system/name-service-cache:default',
            'svc:/system/rmtmpfiles:default', 'svc:/system/identity:domain',
            'svc:/system/coreadm:default', 'svc:/system/cryptosvc:default',
            'svc:/system/sysevent:default',
            'svc:/system/device/fc-fabric:default',
            'svc:/milestone/devices:default', 'svc:/system/sar:default',
            'svc:/system/picl:default', 'svc:/network/ipsec/ipsecalgs:default',
            'svc:/network/ipsec/policy:default',
            'svc:/milestone/network:default', 'svc:/network/initial:default',
            'svc:/network/service:default', 'svc:/network/dns/client:default',
            'svc:/milestone/name-services:default',
            'svc:/system/pkgserv:default',
            'svc:/network/iscsi/initiator:default',
            'svc:/system/manifest-import:default',
            'svc:/system/patchchk:default',
            'svc:/milestone/single-user:default',
            'svc:/system/filesystem/local:default',
            'svc:/network/shares/group:default', 'svc:/system/cron:default',
            'svc:/system/sysidtool:net',
            'svc:/system/boot-archive-update:default',
            'svc:/network/routing-setup:default',
            'svc:/network/rpc/bind:default', 'svc:/system/sysidtool:system',
            'svc:/milestone/sysconfig:default', 'svc:/system/sac:default',
            'svc:/system/filesystem/autofs:default',
            'svc:/network/inetd:default',
            'svc:/application/font/fc-cache:default',
            'svc:/system/utmp:default', 'svc:/system/console-login:default',
            'svc:/system/dumpadm:default', 'svc:/system/system-log:default',
            'svc:/network/ssh:default', 'svc:/network/sendmail-client:default',
            'svc:/network/smtp:sendmail',
            'svc:/application/management/wbem:default',
            'svc:/system/postrun:default', 'svc:/network/rpc/gss:default',
            'svc:/application/font/stfsloader:default',
            'svc:/network/rpc/cde-calendar-manager:default',
            'svc:/network/rpc/cde-ttdbserver:tcp',
            'svc:/network/rpc/smserver:default',
            'svc:/network/security/ktkt_warn:default',
            'svc:/network/rpc-100235_1/rpc_ticotsord:default',
            'svc:/system/filesystem/volfs:default',
            'svc:/milestone/multi-user:default',
            'svc:/system/boot-config:default', 'svc:/system/fmd:default',
            'svc:/milestone/multi-user-server:default',
            'svc:/system/zones:default', 'svc:/system/fpsd:default',
            'svc:/system/basicreg:default', 'svc:/application/stosreg:default',
            'svc:/application/graphical-login/cde-login:default',
            'svc:/application/cde-printinfo:default',
            'svc:/application/autoreg:default',
            'svc:/system/webconsole:console', 'svc:/network/cswpuppetd:default'
        ]
        self.systemddefault = [
            'accounts-daemon.service', 'after-local.service',
            'alsa-restore.service', 'alsasound', 'alsa-store.service',
            'alsa-state.service', 'alsa-state.service', 'anacron.service',
            '[email protected]', 'arp-ethers.service', 'atd.service',
            'auditd.service', 'auth-rpcgss-module.service', 'brandbot.service',
            'chronyd.service', 'chronyd', 'chrony.service', 'chrony',
            'colord.service', 'cron', 'cron.service', 'crond.service', 'cups',
            'cups.service', 'debian-fixup.service', 'dbus.service', 'dbus',
            'dbus-daemon.service', 'dbus-broker.service',
            'display-manager.service', 'dm-event.service',
            'dmraid-activation.service', 'dnf-makecache.service',
            'dracut-shutdown.service', 'emergency.service', 'encase.service',
            'fedora-autorelabel-mark.service', 'fedora-autorelabel.service',
            'fedora-configure.service', 'fedora-import-state.service',
            'fedora-loadmodules.service', 'fedora-readonly.service',
            'fedora-storage-init-late.service', 'fedora-storage-init.service',
            'fedora-wait-storage.service', 'firewalld.service', 'gdm.service',
            'gdm3.service', 'gdm3', '*****@*****.**', '*****@*****.**',
            '*****@*****.**', '*****@*****.**', '[email protected]',
            'getty-static.service', 'halt.service', 'localfs.service',
            'ip6tables.service', 'iptables.service', 'irqbalance.service',
            'iprdump.service', 'iprinit.service', 'iprupdate.service',
            'iscsi-shutdown.service', 'kmod', 'kmod-static-nodes.service',
            'ksm.service', 'ksmtuned.service', 'ldconfig.service',
            'lightdm.service', 'lightdm', 'lvm2-activation.service',
            'lvm2-activation-early.service', 'lvm2-monitor.service', 'mcelog',
            'mcelog.service', 'mdmonitor-takeover.service',
            'mdmonitor.service', 'named-setup-rndc.service',
            'netconsole.service', 'network.service', 'networking.service',
            'networking', 'network-manager', 'network-manager.service',
            'NetworkManager.service', 'NetworkManager', 'network',
            'netcf-transaction.service', 'nfs-blkmap.service',
            'nfs-config.service', 'nfs-idmapd.service', 'nfs-mountd.service',
            'nfs-utils.service', 'nessusagent.service', 'nfs-lock.service',
            'nslcd.service', 'ntpd.service', 'ntpdate.service', 'ntp.service',
            'ntp', 'ntpd', 'ondemand.service', 'osad.service', 'pcscd',
            'pcscd.service', 'postfix', 'polkit.service',
            'purge-kernels.service', 'plymouth-quit-wait.service',
            'plymouth-quit.service', 'plymouth-read-write.service',
            'plymouth-start.service', 'polkit.service', 'postfix.service',
            '*****@*****.**', 'poweroff.service', 'prefdm.service',
            'procps.service', 'procps', 'psacct.service', 'psacct',
            'rc-local.service', 'rc.local', 'reboot.service',
            'remount-rootfs.service', 'rescue.service', 'resolvconf.service',
            'resolvconf', 'rhel-autorelabel', 'rhel-configure.service',
            'rhel-import-state.service', 'rhel-loadmodules.service',
            'rhel-readonly.service', 'rhnsd.service',
            'rhel-autorelabel-mark.service', 'rhel-autorelabel.service',
            'rhsmcertd.service', 'rsyslog.service', 'rsyslog', 'rsyslogd',
            'rtkit-daemon.service', 'rhnsd', 'rngd.service', 'rpcbind.service',
            'rpc-gssd.service', 'rpc-statd-notify.service',
            'rpc-statd.service', 'rpc-svcgssd.service', 'sddm.service', 'sddm',
            'sendmail.service', 'sm-client.service', 'spice-vdagentd.service',
            'ssh', 'ssh.service', 'sshd', 'sshd.service',
            'sshd-keygen.service', 'stonix-mute-mic.service',
            'stonix-mute-mic', 'SuSEfirewall2.service',
            'SuSEfirewall2_init.service', 'syslog.service',
            'systemd-firstboot.service', 'systemd-fsck-root.service',
            'systemd-hostnamed.service', 'systemd-hwdb-update.service',
            'systemd-journal-catalog-update.service',
            'systemd-machine-id-commit.service',
            'systemd-setup-dgram-qlen.service', 'systemd-sysusers.service',
            'systemd-update-done.service', 'systemd-journal-flush.service',
            'systemd-random-seed.service',
            'systemd-tmpfiles-setup-dev.service',
            'systemd-udev-root-symlink.service', 'systemd-udev-settle.service',
            'systemd-udev-trigger.service', 'systemd-udev.service',
            'systemd-udevd.service', 'systemd-update-utmp.service',
            'sssd.service', 'system-setup-keyboard.service', 'smartd.service',
            'sysstat.service', 'systemd-fsck-root.service',
            'systemd-journal-flush.service', 'systemd-random-seed.service',
            'systemd-reboot.service', 'systemd-tmpfiles-setup-dev.service',
            'systemd-udev-settle.service', 'systemd-udev-trigger.service',
            'systemd-udevd.service', 'systemd-update-utmp.service',
            'systemd-ask-password-console.service',
            'systemd-ask-password-plymouth.service',
            'systemd-ask-password-wall.service', 'systemd-binfmt.service',
            'systemd-initctl.service', 'systemd-journald.service',
            'systemd-logind.service', 'systemd-modules-load.service',
            'systemd-networkd-wait-online.service', 'systemd-networkd.service',
            'systemd-random-seed-load.service',
            'systemd-random-seed-save.service',
            'systemd-readahead-collect.service',
            'systemd-readahead-done.service',
            'systemd-readahead-replay.service',
            'systemd-remount-api-vfs.service', 'systemd-remount-fs.service',
            'systemd-shutdownd.service', 'systemd-sysctl.service',
            'systemd-tmpfiles-clean.service', 'systemd-tmpfiles-setup.service',
            'systemd-update-utmp-runlevel.service',
            'systemd-update-utmp-shutdown.service',
            'systemd-user-sessions.service', 'systemd-vconsole-setup.service',
            'systemd-resolved.service', 'systemd-resolved',
            'dbus-org.freedesktop.resolve1.service'
            'tcsd.service', 'tuned.service', 'udev', 'udev.service',
            'udev-settle.service', 'udev-trigger.service', 'udev.service',
            'udev-finish.service', 'udev-finish', 'udisks2.service',
            'ufw.service', 'upower.service', 'unbound-anchor.service',
            'urandom.service', 'urandom', 'virtlockd.service',
            'wicked.service', 'xdm', 'xdm.service',
            'YaST2-Second-Stage.service', 'ypbind.service',
            'sysstat-collect.service', 'sysstat-summary.service'
        ]

        datatype = 'bool'
        key = 'MINIMIZESVCS'
        instructions = "To prevent STONIX from disabling all non-white-listed services, set the value of MINIMIZESVCS to False."
        default = True
        self.minimizeci = self.initCi(datatype, key, instructions, default)

        datatype2 = 'list'
        key2 = 'SERVICEENABLE'
        instructions2 = """This list contains services that are permitted to \
run on this platform. If you need to run a service not currently in this \
list, add the service to the list and STONIX will not disable it. List \
elements should be space separated."""

        self.serviceTarget = ""

        self.logger.log(LogPriority.DEBUG, "Starting platform detection")

        self.environ.setsystemtype()
        systemtype = self.environ.getsystemtype()
        self.ch = CommandHelper(self.logger)

        if systemtype == "systemd":
            self.logger.log(LogPriority.DEBUG, [
                'MinimizeServices.__init__',
                "systemctl found. Using systemd white list"
            ])
            self.svcslistci = self.initCi(datatype2, key2, instructions2,
                                          self.systemddefault)

        elif self.environ.getosfamily() == 'linux':
            self.logger.log(LogPriority.DEBUG, [
                'MinimizeServices.__init__',
                "Linux OS found. Using Linux default white list"
            ])
            self.svcslistci = self.initCi(datatype2, key2, instructions2,
                                          self.linuxdefault)

        elif self.environ.getosfamily() == 'solaris':
            self.logger.log(LogPriority.DEBUG, [
                'MinimizeServices.__init__',
                "Solaris OS found. Using Solaris list"
            ])
            self.svcslistci = self.initCi(datatype2, key2, instructions2,
                                          self.soldefault)

        elif self.environ.getosfamily() == 'freebsd':
            self.logger.log(LogPriority.DEBUG, [
                'MinimizeServices.__init__',
                "FreeBSD OS found. Using BSD white list"
            ])
            self.svcslistci = self.initCi(datatype2, key2, instructions2,
                                          self.bsddefault)

        else:
            self.logger.log(LogPriority.DEBUG, [
                'MinimizeServices.__init__',
                "Detection fell through. Return from ENV:" +
                self.environ.getosfamily()
            ])
            self.svcslistci = self.initCi(datatype2, key2, instructions2,
                                          self.linuxdefault)

        #what's in stonix.conf
        x = self.svcslistci.getcurrvalue()

        #self.systemddefault - x
        y = [li for li in self.systemddefault if li not in x]

        if systemtype == "systemd":
            #the set of what's in stonix.conf added to what's in self.systemddefault that isn't in stonix.conf
            self.svcslistci.updatecurrvalue(x + y)

    def whiteListAliases(self):
        """
        add all alias names of white listed services to white list

        :return: void
        """

        dirs = [
            "/usr/share/dbus-1/services/*",
            "/usr/share/dbus-1/system-services/*", "/etc/systemd/system/*",
            "/usr/lib/systemd/system/*"
        ]
        grep_alias_cmd = "/bin/grep -rih Alias= "
        aliaslines = []
        services = self.svcslistci.getcurrvalue()

        for d in dirs:
            self.ch.executeCommand(grep_alias_cmd + d)
            output = self.ch.getOutput()
            retcode = self.ch.getReturnCode()
            if retcode == 0:
                aliaslines += output

            for al in aliaslines:
                try:
                    aliases = al.split("=")
                    aliases = aliases[1].split()
                    for i in range(len(aliases)):
                        if ".target" in aliases[i]:
                            continue
                        else:
                            services.append(aliases[i])
                except IndexError:
                    continue

        services = list(set(services))

        self.svcslistci.updatecurrvalue(services)

    def report(self):
        '''Report on whether any services not in the baseline or configured set
        are running.

        :return: self.compliant
        :rtype: bool
        '''

        self.compliant = True
        self.detailedresults = ""
        unauthorizedservices = []
        authorizedservices = []
        if self.environ.getsystemtype() == "systemd":
            self.whiteListAliases()

        try:

            servicelist = self.servicehelper.listServices()
            allowedlist = self.svcslistci.getcurrvalue()
            corelist = self.svcslistci.getdefvalue()

            self.logger.log(LogPriority.DEBUG,
                            "AllowedList: " + str(allowedlist))

            for service in servicelist:
                if service in allowedlist or re.search('user@[0-9]*.service',
                                                       service):
                    if service in allowedlist:
                        authorizedservices.append(str(service))
                        # [email protected] are user managers started by PAM
                        # on Linux. They are GRAS (generally regarded as safe)
                    if service not in corelist and \
                    not re.search('user@[0-9]*.service', service):
                        self.logger.log(LogPriority.DEBUG,
                                        'Non-core service running: ' + service)
                else:
                    if self.servicehelper.auditService(
                            service, serviceTarget=self.serviceTarget):
                        self.compliant = False
                        unauthorizedservices.append(str(service))

            self.logger.log(
                LogPriority.DEBUG,
                "The following white-listed services were found and will NOT be disabled:\n+ "
                + "\n+ ".join(authorizedservices) + "\n")

            if self.compliant:
                self.detailedresults += 'No unauthorized services were detected'
                self.targetstate = 'configured'
            else:
                self.detailedresults += "Unauthorized running services detected:\n- " + "\n- ".join(
                    unauthorizedservices) + "\n"
            self.targetstate = 'notconfigured'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        '''Disable all services not in the 'enable services' list.

        :return: self.rulesuccess
        :rtype: bool
        '''

        self.detailedresults = ""
        self.rulesuccess = True
        changes = []

        if self.minimizeci.getcurrvalue():
            try:
                servicelist = self.servicehelper.listServices()
                allowedlist = self.svcslistci.getcurrvalue()

                # make sure you're not dealing with any return garbage
                # in the comparisons below
                allowedlist = [i.rstrip() for i in allowedlist]
                servicelist = [i.rstrip() for i in servicelist]

                for service in servicelist:
                    if service in allowedlist:
                        continue
                    elif re.search('user@[0-9]*.service', service):
                        # these are systemd usermanagers started by PAM
                        # they are OK.
                        continue
                    else:
                        running = self.servicehelper.auditService(
                            service, serviceTarget=self.serviceTarget)
                        if running and service not in self.specials:
                            changes.append(service)
                            self.servicehelper.disableService(
                                service, serviceTarget=self.serviceTarget)

                mytype = 'command'
                mystart = []
                myend = changes
                myid = '0013001'
                event = {
                    'eventtype': mytype,
                    'startstate': mystart,
                    'endstate': myend
                }
                self.statechglogger.recordchgevent(myid, event)

            except (KeyboardInterrupt, SystemExit):
                raise
            except Exception:
                self.rulesuccess = False
                self.detailedresults += traceback.format_exc()
                self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def undo(self):
        '''Attempt to reset the service state for all the services that we changed
        back to their original states.
        
        @author: Dave Kennel


        '''
        self.targetstate = 'notconfigured'
        try:
            event1 = self.statechglogger.getchgevent('0013001')
            for service in event1['endstate']:
                self.servicehelper.enableService(
                    service, serviceTarget=self.serviceTarget)
        except (IndexError, KeyError):
            self.logger.log(
                LogPriority.DEBUG,
                ['MinimizeServices.undo', "EventID 0013001 not found"])
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.detailedresults = 'MinimizeServices: '
            self.detailedresults = self.detailedresults + \
                traceback.format_exc()
            self.rulesuccess = False
            self.logger.log(LogPriority.ERROR,
                            ['MinimizeServices.fix', self.detailedresults])
            return False
        self.report()
        if self.currstate == self.targetstate:
            self.detailedresults = 'MinimizeServices: Changes ' + \
                'successfully reverted'
        return True
Ejemplo n.º 30
0
class DisableInactiveAccounts(Rule):
    '''This rule will set the global policy for inactive accounts so that any
    account not accessed/used within 35 days will be automatically disabled.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 4
        self.rulename = 'DisableInactiveAccounts'
        self.compliant = True
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.sethelptext()
        self.guidance = ['CNSSI 1253', 'DISA STIG']

        datatype = 'bool'
        key = 'DISABLEINACTIVEACCOUNTS'
        instructions = 'To disable this rule, set the value of ' + \
            'DisableInactiveAccounts to False.'
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'fisma': 'high'
        }

        self.initobjs()

    def initobjs(self):
        '''initialize objects for use by this class


        :returns: void
        @author: Breen Malmberg

        '''

        self.cmdhelper = CommandHelper(self.logger)

    def getEnabledAccounts(self):
        '''return a list of all currently enabled accounts


        :returns: enabledaccounts

        :rtype: list
@author: Breen Malmberg

        '''

        allaccounts = []
        enabledaccounts = []
        getallaccounts = "/usr/bin/dscl . -list /Users"
        getenabled = "/usr/bin/pwpolicy -u {username} --get-effective-policy"

        try:

            self.cmdhelper.executeCommand(getallaccounts)
            allaccounts = self.cmdhelper.getOutput()
            if allaccounts:
                for acc in allaccounts:
                    self.cmdhelper.executeCommand(
                        getenabled.replace("{username}", acc))
                    outputstr = self.cmdhelper.getOutputString()
                    if re.search("isDisabled=false", outputstr, re.IGNORECASE):
                        enabledaccounts.append(acc)

        except Exception:
            raise

        return enabledaccounts

    def report(self):
        '''get a list of users
        determine each user's password last set time
        determine if each user is inactive


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        # UPDATE THIS SECTION IF YOU CHANGE THE CONSTANTS BEING USED IN THE RULE
        constlist = [EXCLUDEACCOUNTS]
        if not self.checkConsts(constlist):
            self.compliant = False
            self.detailedresults = "\nPlease ensure that the constant: EXCLUDEACCOUNTS, in localize.py, is defined and is not None. This rule will not function without it."
            self.formatDetailedResults("report", self.compliant,
                                       self.detailedresults)
            return self.compliant

        self.compliant = True
        self.detailedresults = ''

        # do not check any accounts with these regex terms found in them
        # these are accounts which would not have the passwordlastsettime key
        # in their accountpolicydata, and accounts which we do not want to
        # disable
        accexcludere = ['_', 'nobody', 'daemon', 'root']
        for account in EXCLUDEACCOUNTS:
            accexcludere.append(account)
        self.inactiveaccounts = []

        try:

            userlist = self.getEnabledAccounts()

            if self.cmdhelper.getReturnCode() != 0:
                self.rulesuccess = False
                self.compliant = False
                self.detailedresults += '\nThere was a problem retrieving ' + \
                    'the list of users on this system.'

            userlistnew = []
            for user in userlist:
                removeuser = False
                for element in accexcludere:
                    if re.search(element, user):
                        removeuser = True
                if not removeuser:
                    userlistnew.append(user)

            for user in userlistnew:
                inactivedays = self.getinactivedays(user.strip())
                if int(inactivedays) > 35:
                    self.compliant = False
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + \
                        '" has been inactive for more than 35 days.'
                    self.inactiveaccounts.append(user.strip())
                elif int(inactivedays) > 0 and int(inactivedays) <= 35:
                    daysleft = 35 - int(inactivedays)
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + '" has been inactive for ' + \
                        str(inactivedays) + ' days. You have ' + \
                        str(daysleft) + \
                        ' days left before this account will be disabled.'
                    self.logger.log(
                        LogPriority.DEBUG, '\nThe user account "' +
                        user.strip() + '" has been inactive for ' +
                        str(inactivedays) + ' days. You have ' +
                        str(daysleft) + ' days left before this ' +
                        'account will be disabled.')
                else:
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + '" is not inactive. No problems.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def getinactivedays(self, user):
        '''Get and return the number of days a given user account has been
        inactive

        :param user: string the name of the account to check
        @author: Breen Malmberg
        :returns: inactivedays
        :rtype: int

        '''

        inactivedays = 0
        date_format = "%a %b %d %H:%M:%S %Y"

        try:

            if not user:
                self.logger.log(
                    LogPriority.DEBUG, "The given value for " +
                    "parameter user was None, or blank!")
                return inactivedays

            if not isinstance(user, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "The given value for parameter user was not " +
                    "of the correct type (int)!")
                return inactivedays

            self.cmdhelper.executeCommand('/usr/bin/dscl . readpl /Users/' +
                                          user + ' accountPolicyData ' +
                                          'passwordLastSetTime')
            epochchangetimestr = self.cmdhelper.getOutputString()
            retcode = self.cmdhelper.getReturnCode()
            outstr = self.cmdhelper.getOutputString()
            if retcode != 0:
                if retcode == 181:  # this is the mac os x error code when a plist path does not exist
                    if re.search("No such plist path: passwordLastSetTime",
                                 outstr, re.IGNORECASE):
                        self.detailedresults += "The local account: " + str(
                            user
                        ) + " has never had a password set for it! We will now disable this local account on this machine."
                        self.logger.log(
                            LogPriority.DEBUG,
                            "The local user account: " + str(user) +
                            " had no password for it. STONIX will disable it now."
                        )
                        inactivedays = 9999  # this will ensure it gets added to the list of accounts to disable
                    return inactivedays
                else:
                    self.detailedresults += '\nThere was an issue reading ' + \
                        user + '\'s accountPolicyData passwordLastSetTime'
                    self.compliant = False
                    return inactivedays

            epochchangetimelist = epochchangetimestr.split(':')
            epochchangetimestropr = epochchangetimelist[1].strip()
            epochchangetime = Decimal(epochchangetimestropr)
            pwchangedate = time.ctime(epochchangetime)
            a = datetime.strptime(pwchangedate, date_format)
            now = time.ctime()
            b = datetime.strptime(now, date_format)
            diff = b - a
            if int(diff.days) > 180:
                inactivedays = int(diff.days) - 180

        except Exception:
            raise
        return inactivedays

    def fix(self):
        '''check if ci is enabled
        if it is, run fix actions for this rule
        if not, report that it is disabled


        :returns: fixsuccess

        :rtype: bool
@author: Breen Malmberg

        '''

        # UPDATE THIS SECTION IF YOU CHANGE THE CONSTANTS BEING USED IN THE RULE
        constlist = [EXCLUDEACCOUNTS]
        if not self.checkConsts(constlist):
            success = False
            self.formatDetailedResults("fix", success, self.detailedresults)
            return success

        # defaults
        fixsuccess = True
        self.detailedresults = ''
        self.iditerator = 0
        disabledaccounts = []

        try:

            if self.ci.getcurrvalue():

                if self.inactiveaccounts:
                    for user in self.inactiveaccounts:
                        self.cmdhelper.executeCommand(
                            '/usr/bin/pwpolicy -disableuser -u ' + user)
                        errout = self.cmdhelper.getErrorString()
                        rc = self.cmdhelper.getReturnCode()
                        if rc != 0:
                            self.detailedresults += '\nThere was an issue trying to disable user account: ' + user
                            self.logger.log(LogPriority.DEBUG, errout)
                            fixsuccess = False
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                'eventtype':
                                'commandstring',
                                'command':
                                '/usr/bin/pwpolicy -enableuser -u ' + user
                            }
                            self.statechglogger.recordchgevent(myid, event)
                            disabledaccounts.append(user)
                            self.logger.log(
                                LogPriority.DEBUG, "Disabling user account: " +
                                str(user) + " ...")
                    if disabledaccounts:
                        self.detailedresults += "\nDisabled the following accounts: " + "\n- ".join(
                            disabledaccounts)
                else:
                    self.detailedresults += '\nNo inactive accounts detected. No accounts were disabled.'

            else:
                self.detailedresults += '\nThe CI for this rule was not enabled. Nothing was done.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", fixsuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return fixsuccess
Ejemplo n.º 31
0
class SHchkconfig(ServiceHelperTemplate):
    """
    SHchkconfig is the Service Helper for systems using the chkconfig command to
    configure services. (RHEL up to 6, SUSE, Centos up to 6, etc)

    @author: David Kennel
    """

    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHchkconfig, self).__init__(environment, logdispatcher)
        self.environ = environment
        self.logger = logdispatcher
        self.initobjs()
        self.localize()

    def initobjs(self):
        """
        initialize class objects

        @return:
        """

        self.ch = CommandHelper(self.logger)

    def localize(self):
        """
        set base command paths (chkconfig and service) based on OS

        @return:
        """

        self.svc = ""
        self.chk = ""

        chk_paths = ["/sbin/chkconfig", "/usr/sbin/chkconfig"]
        for cp in chk_paths:
            if os.path.exists(cp):
                self.chk = cp
                break
        service_paths = ["/sbin/service", "/usr/sbin/service"]
        for sp in service_paths:
            if os.path.exists(sp):
                self.svc = sp
                break

        if not self.svc:
            raise IOError("Could not locate the service utility on this system")
        if not self.chk:
            raise IOError("Could not locate the chkconfig utility on this system")

    def startService(self, service, **kwargs):
        """
        start a given service

        @param service: string; name of service
        @param kwargs: 
        @return: success
        @rtype: bool
        @author: Breen Malmberg
        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " start")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if not self.isRunning(service):
            success = False

        return success

    def stopService(self, service, **kwargs):
        """
        stop a given service

        @param service: 
        @param kwargs: 
        @return: success
        @rtype: bool
        @author: Breen Malmberg
        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " stop")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if self.isRunning(service):
            success = False

        return success

    def disableService(self, service, **kwargs):
        """
        Disables the specified service and stops it if
        it is running

        @param service: string; Name of the service to be disabled
        @return: bool
        @author: David Kennel
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        disabled = True

        self.ch.executeCommand(self.chk + " " + service + " off")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            disabled = False

        if self.auditService(service):
            disabled = False

        if not self.stopService(service):
            disabled = False

        return disabled

    def enableService(self, service, **kwargs):
        """
        Enables a service and starts it if it is not running as long as we are
        not in install mode

        @param service: string; Name of the service to be enabled
        @return: enabled
        @rtype: bool
        @author: David Kennel
        @change: Breen Malmberg - 04/10/2019 - 
        """

        enabled = True

        self.ch.executeCommand(self.chk + " " + service + " on")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False

        if not self.auditService(service):
            enabled = False

        if not self.startService(service):
            enabled = False

        return enabled

    def auditService(self, service, **kwargs):
        """
        Checks the status of a service and returns a bool indicating whether or
        not the service is enabled

        @param service: string; Name of the service to audit
        @return: enabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        enabled = True

        if not self.audit_chkconfig_service(service):
            enabled = False

        return enabled

    def audit_chkconfig_service(self, service):
        """
        uses the chkconfig command to check if a given
        service is enabled or not

        @return: enabled
        @rtype: bool
        @author: Breen Malmberg
        """

        enabled = True

        self.ch.executeCommand(self.chk + " --list " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False
            self.logger.log(LogPriority.DEBUG, "Failed to get status of service: " + service)
            return enabled

        output = self.ch.getOutputString()
        if not re.search(":on", output):
            enabled = False

        return enabled

    def isRunning(self, service, **kwargs):
        """
        Check to see if a service is currently running.

        @param service: string; Name of the service to check
        @return: running
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        running = True
        # see: http://refspecs.linuxbase.org/LSB_3.1.0/LSB-generic/LSB-generic/iniscrptact.html
        success_codes = [0, 1, 2, 3]

        self.ch.executeCommand(self.svc + " " + service + " status")
        retcode = self.ch.getReturnCode()
        if retcode not in success_codes:
            running = False
            self.logger.log(LogPriority.DEBUG, "Command error while getting run status of service: " + service)
            return running

        outputlines = self.ch.getOutput()
        # need to parse for either sysv or systemd output
        if not self.parse_running(outputlines):
            running = False

        return running

    def parse_running(self, outputlines):
        """
        check whether given service is running, with the
        service command
        this is the older (classic) systemV case

        @param outputlines: list; list of strings to search
        @return: running
        @rtype: bool
        @author: Breen Malmberg
        """

        running = True
        systemctl_locations = ["/usr/bin/systemctl", "/bin/systemctl"]
        if any(os.path.exists(sl) for sl in systemctl_locations):
            searchterms = ["Active:\s+inactive", "Active:\s+unknown"]
        else:
            searchterms = ["is stopped", "hook is not installed", "is not running"]

        for line in outputlines:
            if any(re.search(st, line) for st in searchterms):
                running = False
                break

        return running

    def reloadService(self, service, **kwargs):
        """
        Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        @param service: string; Name of service to be reloaded
        @return: reloaded
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        reloaded = True

        # force-reload: cause the configuration to be reloaded if the service supports this,
        # otherwise restart the service if it is running
        self.ch.executeCommand(self.svc + " " + service + " force-reload")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            reloaded = False
            self.logger.log(LogPriority.DEBUG, "Failed to reload service: " + service)

        return reloaded

    def listServices(self, **kwargs):
        """
        Return a list containing strings that are service names.

        @return: service_list
        @rtype: list
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        service_list = []

        self.ch.executeCommand(self.chk + " --list")
        outputlines = self.ch.getOutput()
        for line in outputlines:
            try:
                service_list.append(line.split()[0])
            except IndexError:
                pass

        return service_list

    def getStartCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - start command
        @author: dwalker
        '''
        return self.svc + " " + service + " start"

    def getStopCommand(self, service):
        '''
        retrieve the stop command.  Mostly used by event recording
        @return: string - stop command
        @author: dwalker
        '''
        return self.svc + " " + service + " stop"

    def getEnableCommand(self, service):
        '''
        retrieve the enable command.  Mostly used by event recording
        @return: string - enable command
        @author: dwalker
        '''
        return self.chk + " " + service + " on"

    def getDisableCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - disable command
        @author: dwalker
        '''
        return self.chk + " " + service + " off"
Ejemplo n.º 32
0
class Freebsd(object):

    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    :version:
    :author:Derek T Walker 08-06-2012'''

    def __init__(self,logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/sbin/pkg_add -r -f "
        self.remove = "/usr/sbin/pkg_delete "
###############################################################################
    def installpackage(self, package):
        '''
         Install a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be installed, must be 
            recognizable to the underlying package manager.
        @return bool :
        @author'''
        try:
            installed = False
#             retval = call(self.install + package,stdout=None,shell=True)
            self.ch.executeCommand(self.install + package)
            if self.ch.getReturnCode() == 0:
#             if retval == 0:
                self.detailedresults += package + " pkg installed successfully"
                installed = True
            else:
                self.detailedresults += package + " pkg not able to install"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return installed
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################
    def removepackage(self, package):
        '''
         Remove a package. Return a bool indicating success or failure.

        @param string package : Name of the package to be removed, must be 
            recognizable to the underlying package manager.
        @return : bool 
        @author'''
        try:
            self.detailedresults = ""
            removed = False
            stringToMatch = package + "(.*)"
            self.ch.executeCommand(["/usr/sbin/pkg_info"])
            output = self.ch.getOutput()
#             temp = Popen(["/usr/sbin/pkg_info"],stdout=PIPE,shell=True)
#             details = temp.stdout.readlines()
#             temp.stdout.close()
            for cell in output:
                cell2 = cell.split(" ")
                if re.search(stringToMatch,cell2[0]):
                    retval = call(self.remove + cell2[0],stdout=None,shell=True)
                    if retval == 0:
                        self.detailedresults += package + " pkg removed \
successfully"
                        removed = True
                    else:
                        self.detailedresults += package + " pkg not able\
to be removed"
            if not self.detailedresults:
                self.detailedresults += package + " pkg not found to remove"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return removed
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################
    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if 
        the package is installed.

        @param string package : Name of the package whose installation status 
            is to be checked, must be recognizable to the underlying package 
            manager.
        @return : bool
        @author'''
        try:
            self.detailedresults = ""
            present = False
            stringToMatch = package +"(.*)"
            self.ch.executeCommand(["/usr/sbin/pkg_info"])
            output = self.ch.getOutput()
#             temp = Popen(["/usr/sbin/pkg_info"],stdout=PIPE,shell=False)
#             info = temp.stdout.readlines()
#             temp.stdout.close()
            for cell in output:
                cell2 = cell.split(" ")
                if re.search(stringToMatch,cell2[0]):
                    self.detailedresults += package + " pkg found"
                    present = True
            if not self.detailedresults:
                self.detailedresults += package + " pkg not found"
            self.logger.log(LogPriority.INFO, self.detailedresults)
            return present
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.INFO, self.detailedresults)
###############################################################################
    def getInstall(self):
        return self.install
###############################################################################
    def getRemove(self):
        return self.remove
Ejemplo n.º 33
0
class SHsystemctl(ServiceHelperTemplate):
    """
    SHsystemctl is the Service Helper for systems using the systemctl command to
    configure services. (Fedora and future RHEL and variants)
    """

    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHsystemctl, self).__init__(environment, logdispatcher)
        self.environment = environment
        self.logdispatcher = logdispatcher
        self.ch = CommandHelper(self.logdispatcher)

        self.localize()

    def localize(self):
        """

        @return:
        """

        systemctl_paths = ["/usr/bin/systemctl", "/bin/systemctl"]
        self.sysctl = ""

        for sp in systemctl_paths:
            if os.path.exists(sp):
                self.sysctl = sp
                break

        if not self.sysctl:
            raise IOError("Cannot find systemctl utility on this system!")

        # do not attempt to manipulate any service which has a status in this list
        self.handsoff = ["static", "transient", "generated", "masked", "masked-runtime"]

    def disableService(self, service, **kwargs):
        """
        Disables the service and terminates it if it is running.

        @param service: string; Name of the service to be disabled
        @return: disabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor;
                doc string edit; logging edit
        """

        disabled = True
    
        self.ch.executeCommand(self.sysctl + " disable " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            disabled = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        if not self.stopService(service):
            disabled = False

        return disabled

    def enableService(self, service, **kwargs):
        """
        Enables a service and starts it if it is not running as long as we are
        not in install mode

        @param service: string; Name of the service to be disabled
        @return: enabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor;
                doc string edit; logging edit
        """

        enabled = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            enabled = False
            return enabled

        self.ch.executeCommand(self.sysctl + " enable " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return enabled

    def auditService(self, service, **kwargs):
        """
        Checks the status of a service and returns a bool indicating whether or
        not the service is configured to run or not.

        @param service: string; Name of the service to audit
        @return: enabled
        @rtype: bool
        """

        enabled = False

        self.ch.executeCommand(self.sysctl + " is-enabled " + service)

        if self.ch.findInOutput("not a native service"):
            self.logdispatcher.log(LogPriority.DEBUG, "Attempted to audit a non-systemd service with systemctl commands")
            return enabled
        elif self.ch.findInOutput("enabled"):
            enabled = True

        return enabled

    def isRunning(self, service, **kwargs):
        """
        Check to see if a service is currently running. The enable service uses
        this so that we're not trying to start a service that is already
        running.

        @param service: string; name of service to check
        @return: running
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                debug logging edit
        """

        running = True
        inactive_keys = ["inactive", "unknown"]

        self.ch.executeCommand(self.sysctl + " is-active " + service)
        for k in inactive_keys:
            if self.ch.findInOutput(k):
                running = False

        return running

    def reloadService(self, service, **kwargs):
        """
        Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        @param service: string; Name of the service to reload
        @return: success
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                debug logging edit
        """

        success = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            success = False
            return success

        self.ch.executeCommand(self.sysctl + " reload-or-restart " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)
        if not self.isRunning(service):
            success = False

        return success

    def listServices(self, **kwargs):
        """
        Return a list containing strings that are service names.

        @return: service_list
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; debug logging
                edit; doc string edit
        """

        service_list = []

        # list all installed, service-type service units on the system
        self.ch.executeCommand(self.sysctl + " -a -t service --no-pager list-unit-files")
        output = self.ch.getOutput()

        for line in output:
            try:
                service_list.append(line.split()[0])
            except IndexError:
                pass
            except:
                raise

        if not service_list:
            errmsg = self.ch.getErrorString()
            if errmsg:
                self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return service_list

    def startService(self, service, **kwargs):
        """
        start given service

        @param service:
        @param kwargs:
        @return: started
        @rtype: bool
        @author: Breen Malmberg
        """

        started = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            started = False
            return started

        self.ch.executeCommand(self.sysctl + " start " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            started = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return started

    def stopService(self, service, **kwargs):
        """
        stop given service

        @param service:
        @param kwargs:
        @return: stopped
        @rtype: bool
        @author: Breen Malmberg
        """

        stopped = True

        if not self.isRunning(service):
            return stopped # nothing to do

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            stopped = False
            return stopped

        else:
            self.ch.executeCommand(self.sysctl + " stop " + service)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                stopped = False
                errmsg = self.ch.getErrorString()
                self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return stopped

    def getServiceStatus(self, service, **kwargs):
        """
        return is-enabled status output
        possible return values:

        enabled
        enabled-runtime
        linked
        linked-runtime
        masked
        masked-runtime
        static
        indirect
        disabled
        generated
        transient
        unknown (custom status defined in STONIX; not generated by systemctl)

        @param service:
        @param kwargs:
        @return: status
        @rtype: string
        @author: Breen Malmberg
        """

        status = ""
        known_statuses = ["enabled", "enabled-runtime", "linked", "linked-runtime",
                          "masked", "masked-runtime", "static", "indirect", "disabled",
                          "generated", "transient"]

        self.ch.executeCommand(self.sysctl + " is-enabled " + service)
        output = self.ch.getOutputString()

        try:
            if len(output.split()) == 1:
                status = str(output)
            else:
                status = str(output.split()[0])
        except IndexError:
            pass
        except:
            raise

        if status not in known_statuses:
            status = "unknown"
        elif not isinstance(status, basestring):
            status = "unknown"

        if status in self.handsoff:
            self.logdispatcher.log(LogPriority.DEBUG, "Status of service: " + service + " indicates it is either protected, required or immutable. Will not perform operation on this service!")

        return status

    def getStartCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - start command
        @author: dwalker
        '''
        return self.sysctl + " start " + service

    def getStopCommand(self, service):
        '''
        retrieve the stop command.  Mostly used by event recording
        @return: string - stop command
        @author: dwalker
        '''
        return self.sysctl + " stop " + service

    def getEnableCommand(self, service):
        '''
        retrieve the enable command.  Mostly used by event recording
        @return: string - enable command
        @author: dwalker
        '''
        return self.sysctl + " enable " + service

    def getDisableCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - disable command
        @author: dwalker
        '''
        return self.sysctl + " disable " + service
Ejemplo n.º 34
0
class DisableTouchID(Rule):
    '''classdocs'''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 102
        self.rulename = 'DisableTouchID'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.environ = environ
        self.sethelptext()
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        datatype = 'bool'
        key = 'DISABLETOUCHID'
        instructions = "To prevent this rule from running, set the value of DisableTouchID to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.guidance = ['(None)']

        self.initobjs()

    def initobjs(self):
        '''init objects used by this class
        
        @author: Breen Malmberg


        '''

        self.ch = CommandHelper(self.logger)
        self.fixed = False

    def report(self):
        '''check status of touch id


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        self.detailedresults = ""
        self.compliant = True
        checkstrings = {
            "System Touch ID configuration:": False,
            "Operation performed successfully": False
        }
        bioutil = "/usr/bin/bioutil"
        reportcmd = bioutil + " -r -s"
        touchidinstalled = False

        try:

            if not os.path.exists(bioutil):
                self.logger.log(LogPriority.DEBUG,
                                "The required bioutil utility was not found")
                self.compliant = False
                self.formatDetailedResults("report", self.compliant,
                                           self.detailedresults)
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return self.compliant

            self.ch.executeCommand(reportcmd)
            outlist = self.ch.getOutput()

            if not outlist:
                self.logger.log(LogPriority.DEBUG,
                                "bioutil command returned no output!")
                self.compliant = False
                self.formatDetailedResults("report", self.compliant,
                                           self.detailedresults)
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return self.compliant

            for line in outlist:
                if re.search("Touch ID functionality", line, re.IGNORECASE):
                    touchidinstalled = True

            if touchidinstalled:
                checkstrings = {
                    "System Touch ID configuration:": False,
                    "Touch ID functionality: 0": False,
                    "Touch ID for unlock: 0": False,
                    "Operation performed successfully": False
                }

            for line in outlist:
                for cs in checkstrings:
                    if re.search(cs, line, re.IGNORECASE):
                        checkstrings[cs] = True

            for cs in checkstrings:
                if not checkstrings[cs]:
                    self.compliant = False
                    self.detailedresults += "\nTouch ID is still enabled."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.compliant = False
            self.detailedresults = traceback.format_exc()
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        '''turn off touch id functionality


        :returns: self.rulesuccess

        :rtype: bool
@author: Breen Malmberg

        '''

        self.detailedresults = ""
        self.rulesuccess = True
        fixcmd1 = "/usr/bin/bioutil -w -s -u 0"
        fixcmd2 = "/usr/bin/bioutil -w -s -f 0"
        checkstring = "Operation performed successfully"

        try:

            if self.ci.getcurrvalue():

                self.ch.executeCommand(fixcmd1)
                outlist = self.ch.getOutput()

                if not outlist:
                    self.logger.log(LogPriority.DEBUG,
                                    "bioutil command returned no output!")
                    self.detailedresults += "\n"
                    self.rulesuccess = False
                    self.formatDetailedResults("fix", self.compliant,
                                               self.detailedresults)
                    self.logger.log(LogPriority.INFO, self.detailedresults)
                    return self.rulesuccess

                cmdsuccess = False
                for line in outlist:
                    if re.search(checkstring, line, re.IGNORECASE):
                        cmdsuccess = True

                if not cmdsuccess:
                    self.rulesuccess = False
                    self.detailedresults += "\nFailed to turn off Touch ID unlock"

                self.ch.executeCommand(fixcmd2)
                outlist = self.ch.getOutput()

                if not outlist:
                    self.logger.log(LogPriority.DEBUG,
                                    "bioutil command returned no output!")
                    self.rulesuccess = False
                    self.formatDetailedResults("fix", self.compliant,
                                               self.detailedresults)
                    self.logger.log(LogPriority.INFO, self.detailedresults)
                    return self.rulesuccess

                cmdsuccess = False
                for line in outlist:
                    if re.search(checkstring, line, re.IGNORECASE):
                        cmdsuccess = True

                if not cmdsuccess:
                    self.rulesuccess = False
                else:
                    self.fixed = True

            else:
                self.detailedresults += "\nCI was not enabled. Nothing was fixed."
                self.logger.log(
                    LogPriority.DEBUG,
                    "User ran rule without CI enabled. Nothing was fixed.")

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults = traceback.format_exc()
        self.formatDetailedResults("fix", self.compliant, self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def undo(self):
        '''reverse the fix actions which were applied


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

        self.detailedresults = ""
        success = True
        undocmd1 = "/usr/bin/bioutil -w -s -f 1"
        undocmd2 = "/usr/bin/bioutil -w -s -u 1"

        try:

            if self.fixed:

                self.ch.executeCommand(undocmd1)
                retval = self.ch.getReturnCode()
                if retval != 0:
                    success = False
                    self.detailedresults += "\nEncountered an error while trying to undo the fix actions."

                self.ch.executeCommand(undocmd2)
                retval = self.ch.getReturnCode()
                if retval != 0:
                    success = False
                    self.detailedresults += "\nEncountered an error while trying to undo the fix actions."

                if success:
                    self.fixed = False

            else:
                self.detailedresults += "\nSystem already in original state. Nothing to undo."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            success = False
            self.detailedresults = traceback.format_exc()
        self.formatDetailedResults("undo", success, self.detailedresults)
        self.logger.log(LogPriority.INFO, self.detailedresults)
        return success
Ejemplo n.º 35
0
class lanlMacInfo():
    '''
    lanlMacInfo gets information from the mac and LDAP to help set basi
    computer data for the mac this includes:
    ComputerName
    HostName
    LocalHostname
    asset_id (property number)
    endUserName (owner)
    @author: ekkehard
    '''

    def __init__(self, logger):
        '''
        initialize lanlMacInfo
        @author: ekkehard
        '''
        self.logdispatch = logger
# Make sure we have the full path for all commands
        self.ch = CommandHelper(logger)
        self.LANLAssetTagNVRAM = ""
        self.LANLAssetTagFilesystem = ""
        self.macAddressDictionary = {}
        self.dictionary = {}
        self.accuracyDictionary = {}
        self.dictionaryItem = None
        self.keys = None
        self.key = None
        self.keyIndexNumber = 0
        self.keysNumberOf = 0
        self.entries = -1
        self.computerName = ""
        self.computerNameDiskUtility = ""
        self.hostName = ""
        self.hostNameDiskUtility = ""
        self.localHostname = ""
        self.localHostnameDiskUtility = ""
        self.endUsername = ""
        self.assetTag = ""
# Make sure we have the full path for all commands
        self.ns = "/usr/sbin/networksetup"
        self.scutil = "/usr/sbin/scutil"
        self.jamf = "/usr/sbin/jamf"
        self.nvram = "/usr/sbin/nvram"
        self.ldap = "/usr/bin/ldapsearch"
        self.lanl_property_file = "/Library/Preferences/lanl_property_number.txt"
# Initialize accuracy modules
        self.updateAssetTagAccuracy(True, 0, "", True)
        self.updateEndUserNameAccuracy(True, 0, "", True)
        self.updateComputerNameAccuracy(True, 0, "", True)
# reset messages and initialize everyting
        self.messageReset()
        self.initializeLanlAssetTagNVRAM()
        self.initializeLanlAssetTagFilesystem()
        self.initializeDiskUtilityInfo()
        self.populateFromMac()
        self.determinAccuracy()
    
    def gotoFirstItemLDAP(self):
        '''
        go to the first entry in the LDAP dictionary
        @author: ekkehard
        @return: dictionary entry
        '''
        self.keyIndexNumber = 0
        self.keys = sorted(self.dictionary.keys())
        self.keysNumberOf = len(self.keys)
        if self.keysNumberOf > self.keyIndexNumber:
            self.key = self.keys[self.keyIndexNumber]
            self.dictinaryItem = self.dictionary[self.key]
        else:
            self.key = None
            self.dictinaryItem = None
        return self.dictinaryItem

    def gotoNextItemLDAP(self):
        '''
        go to the next entry in the LDAP dictionary
        @author: ekkehard
        @return: dictionary entry
        '''
        self.keyIndexNumber = self.keyIndexNumber + 1
        self.keys = sorted(self.dictionary.keys())
        self.keysNumberOf = len(self.keys)
        if (self.keysNumberOf - 1) < self.keyIndexNumber:
            self.keyIndexNumber = 0
            self.dictinaryItem = None
        else:
            self.key = self.keys[self.keyIndexNumber]
            self.dictinaryItem = self.dictionary[self.key]
        return self.dictinaryItem

    def getCurrentItemLDAP(self):
        '''
        get the current item in the LDAP dictionary
        @author: ekkehard
        @return: dictionary entry
        '''
        return self.dictinaryItem

    def getComputerInfoCompliance(self):
        '''
        see if all is set correctly
        @author: ekkehard
        @return: boolean - True of False
        '''
        success = True
        compliant = True
# Check computername
        messagestring = "ComputerName Confidence level of " + \
        str(self.getSuggestedComputerNameConfidenceOnly()) + "%"
        if not(self.getSuggestedComputerNameConfidenceOnly() == 100):
            compliant = False
            messagestring = messagestring + " is less than 100%"
        messagestring = messagestring + "; ComputerName (" + \
        self.getDiskUtilityComputerName() + ") and proposed ComputerName (" + \
        self.getSuggestedComputerName() + ")"
        if not(self.getDiskUtilityComputerName() == self.getSuggestedComputerName()):
            compliant = False
            messagestring = messagestring + " are not equal;"
        else:
            messagestring = messagestring + " are equal;"
        if not compliant:
            messagestring = "- Not compliant; " + messagestring
            if not(self.computerNameAccuracyLevelWhy == "" ):
                messagestring = messagestring + " - " + self.computerNameAccuracyLevelWhy + ";"
            success = False
        else:
            messagestring = "- compliant; " + messagestring
        self.messageAppend(messagestring)
# Check hostname
        compliant = True
        messagestring = "HostName confidence level of " + \
        str(self.getSuggestedComputerNameConfidenceOnly()) + "%"
        if not(self.getSuggestedComputerNameConfidenceOnly() == 100):
            compliant = False
            messagestring = messagestring + " is less than 100%"
        messagestring = messagestring + "; HostName (" + \
        self.getDiskUtilityHostName() + ") and proposed HostName (" + \
        self.getSuggestedHostName() + ")"
        if not(self.getDiskUtilityHostName() == self.getSuggestedHostName()):
            compliant = False
            messagestring = messagestring + " are not equal;"
        else:
            messagestring = messagestring + " are equal;"
        if not compliant:
            messagestring = "- Not compliant; " + messagestring
            if not(self.computerNameAccuracyLevelWhy == "" ):
                messagestring = messagestring + " - " + self.computerNameAccuracyLevelWhy + ";"
            success = False
        else:
            messagestring = "- compliant; " + messagestring
        self.messageAppend(messagestring)
# Check localhostname
        compliant = True
        messagestring = "LocalHostName confidence level of " + \
        str(self.getSuggestedComputerNameConfidenceOnly()) + "%"
        if not(self.getSuggestedComputerNameConfidenceOnly() == 100):
            compliant = False
            messagestring = messagestring + " is less than 100%"
        messagestring = messagestring + "; LocalHostName (" + \
        self.getDiskUtilityLocalHostName() + ") and proposed LocalHostName (" + \
        self.getSuggestedLocalHostName() + ")"
        if not(self.getDiskUtilityLocalHostName() == self.getSuggestedLocalHostName()):
            compliant = False
            messagestring = messagestring + " are not equal;"
        else:
            messagestring = messagestring + " are equal;"
        if not compliant:
            messagestring = "- Not compliant; " + messagestring
            if not(self.computerNameAccuracyLevelWhy == "" ):
                messagestring = messagestring + " - " + self.computerNameAccuracyLevelWhy + ";"
            success = False
        else:
            messagestring = "- compliant; " + messagestring
        self.messageAppend(messagestring)
        return success

    def getDiskUtilityComputerName(self):
        '''
        get the ComputerName determined by disk utility
        @author: ekkehard
        @return: string
        '''
        return self.computerNameDiskUtility

    def getDiskUtilityHostName(self):
        '''
        get the HostName determined by disk utility
        @author: ekkehard
        @return: string
        '''
        return self.hostNameDiskUtility

    def getDiskUtilityLocalHostName(self):
        '''
        get the LocalHostName determined by disk utility
        @author: ekkehard
        @return: string
        '''
        return self.localHostnameDiskUtility

    def getLANLAssetTagNVRAM(self):
        '''
        get the asset_id set in NVRAM determined
        @author: ekkehard
        @return: string
        '''
        return str(self.LANLAssetTagNVRAM)

    def getLANLAssetTagFilesystem(self):
        '''
        get the asset_id set in file system
        @author: ekkehard
        @return: string
        '''
        return str(self.LANLAssetTagFilesystem)

    def getSuggestedAssetTag(self):
        '''
        get the suggested asset_id
        @author: ekkehard
        @return: string
        '''
        return self.assetTag
    
    def getSuggestedAssetTagConfidence(self):
        '''
        get the suggested asset_id and asset_id confidence level
        @author: ekkehard
        @return: string
        '''
        displayValue = str(self.assetTag) + " (" + str(self.assetTagAccuracyLevel) + "%)"
        return displayValue
    
    def getSuggestedAssetTagConfidenceOnly(self):
        '''
        get the suggested asset_id confidence level
        @author: ekkehard
        @return: real
        '''
        return self.assetTagAccuracyLevel

    def getSuggestedComputerName(self):
        '''
        get the suggested ComputerName
        @author: ekkehard
        @return: string
        '''
        return self.computerName
    
    def getSuggestedComputerNameConfidence(self):
        '''
        get the suggested ComputerName and ComputerName confidence level
        @author: ekkehard
        @return: string
        '''
        displayValue = str(self.computerName) + " (" + str(self.computerNameAccuracyLevel) + "%)"
        return displayValue
    
    def getSuggestedComputerNameConfidenceOnly(self):
        '''
        get the suggested ComputerName confidence level
        @author: ekkehard
        @return: real
        '''
        return self.computerNameAccuracyLevel

    def getSuggestedHostName(self):
        '''
        get the suggested HostName
        @author: ekkehard
        @return: string
        '''
        return self.hostName
    
    def getSuggestedHostNameConfidence(self):
        '''
        get the suggested HostName and HostName confidence level
        @author: ekkehard
        @return: string
        '''
        displayValue = str(self.hostName) + " (" + str(self.computerNameAccuracyLevel) + "%)"
        return displayValue
    
    def getSuggestedHostNameConfidenceOnly(self):
        '''
        get the suggested HostName confidence level
        @author: ekkehard
        @return: real
        '''
        return self.computerNameAccuracyLevel

    def getSuggestedLocalHostName(self):
        '''
        get the suggested LocalHostName
        @author: ekkehard
        @return: string
        '''
        return self.localHostname
    
    def getSuggestedLocalHostNameConfidence(self):
        '''
        get the suggested LocalHostName and LocalHostName confidence level
        @author: ekkehard
        @return: string
        '''
        displayValue = str(self.localHostname) + " (" + str(self.computerNameAccuracyLevel) + "%)"
        return displayValue
    
    def getSuggestedLocalHostNameConfidenceOnly(self):
        '''
        get the suggested LocalHostName confidence level
        @author: ekkehard
        @return: real
        '''
        return self.computerNameAccuracyLevel

    def getSuggestedEndUsername(self):
        '''
        get the suggested EndUserName or Owner
        @author: ekkehard
        @return: string
        '''
        return self.endUsername
    
    def getSuggestedEndUsernameConfidence(self):
        '''
        get the suggested EndUserName or Owner and EndUserName or Owner confidence level
        @author: ekkehard
        @return: string
        '''
        displayValue = str(self.endUsername) + " (" + str(self.endUserNameAccuracyLevel) + "%)"
        return displayValue
    
    def getSuggestedEndUsernameConfidenceOnly(self):
        '''
        get the suggested EndUserName or Owner confidence level
        @author: ekkehard
        @return: real
        '''
        return self.endUserNameAccuracyLevel

    def setComputerInfo(self):
        '''
        set the computer info on the computer
        @author: ekkehard
        @return: boolean - True
        '''
        try:
            success = True
            updatesWhereMade = False
            output = []
            computerName = self.getSuggestedComputerName()
            hostname = self.getSuggestedHostName()
            localHostName = self.getSuggestedLocalHostName()
            if self.computerNameAccuracyLevel == 100:
                if not(self.computerNameDiskUtility == computerName):
                    command = [self.scutil,"--set", "ComputerName", computerName]
                    self.ch.executeCommand(command)
                    errorcode = self.ch.getError()
                    output = self.ch.getOutput()
                    updatesWhereMade = True
                    messagestring = " - ComputerName set to " + computerName
                    self.messageAppend(messagestring)
                else:
                    messagestring = " - ComputerName was alreday " + computerName
                    self.messageAppend(messagestring)
                if not(self.hostNameDiskUtility == hostname):
                    command = [self.scutil,"--set", "HostName", hostname]
                    self.ch.executeCommand(command)
                    errorcode = self.ch.getError()
                    output = self.ch.getOutput()
                    updatesWhereMade = True
                    messagestring = " - HostName set to " + hostname
                    self.messageAppend(messagestring)
                else:
                    messagestring = " - HostName was alreday " + hostname
                    self.messageAppend(messagestring)
                if not(self.localHostnameDiskUtility == localHostName):
                    command = [self.scutil,"--set", "LocalHostName", localHostName]
                    errorcode = self.ch.getError()
                    self.ch.executeCommand(command)
                    output = self.ch.getOutput()
                    updatesWhereMade = True
                    messagestring = " - LocalHostName set to " + localHostName
                    self.messageAppend(messagestring)
                else:
                    messagestring = " - LocalHostName was alreday " + localHostName
                    self.messageAppend(messagestring)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            messagestring = traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, messagestring)
        if updatesWhereMade == True:
            self.initializeDiskUtilityInfo()
        return success

    def setJAMFInfo(self):
        '''
        set the assetTag and endUserName via the jamf recon command
        @author: ekkehard
        @return: boolean - True
        '''
        try:
            success = True
            assetTag = self.getSuggestedAssetTag()
            endUser = self.getSuggestedEndUsername()
            if self.assetTagAccuracyLevel == 100 and self.endUserNameAccuracyLevel == 100:
                command = [self.jamf, "recon",
                           "-assetTag", assetTag,
                           "-endUsername", endUser]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                messagestring = " - JAMF assetTag set to " + assetTag + " and endUsername set to " + endUser
                self.messageAppend(messagestring)
            elif self.assetTagAccuracyLevel == 100:
                command = [self.jamf, "recon", "-assetTag", assetTag]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                endUser = ""
                messagestring = " - JAMF assetTag set to " + assetTag
                self.messageAppend(messagestring)
            elif self.endUserNameAccuracyLevel == 100:
                command = [self.jamf, "recon", "-endUsername", endUser]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                assetTag = ""
                messagestring = " - JAMF endUsername set to " + endUser
                self.messageAppend(messagestring)
            else:
                success = False
                messagestring = " - JAMF settings were not changed because confidence was only " + \
                str(self.assetTagAccuracyLevel) + "%"
                self.messageAppend(messagestring)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            messagestring = traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, messagestring)
        return success
        
    def setLANLAssetTagNVRAM(self):
        '''
        set the assetTag and endUserName via the jamf recon command
        @author: ekkehard
        @return: boolean - True
        '''
        try:
            assetTag = self.getSuggestedAssetTag()
            if self.assetTagAccuracyLevel == 100:
                if not(self.getLANLAssetTagNVRAM() == assetTag):
                    command = [self.nvram, "asset_id=" + assetTag]
                    self.ch.executeCommand(command)
                    self.initializeLanlAssetTagNVRAM()
                    messagestring = " - NVRAM asset_id set to " + assetTag
                    self.messageAppend(messagestring)
                else:
                    messagestring = " - NVRAM asset_id was already set to " + assetTag
                    self.messageAppend(messagestring)
            else:
                assetTag = ""
                messagestring = " - NVRAM asset_id was not changed because confidence was only " + \
                str(self.assetTagAccuracyLevel) + "%"
                self.messageAppend(messagestring)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            messagestring = traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, messagestring)
        return assetTag
        
    def setLANLAssetTagFilesystem(self):
        '''
        set the assetTag on the file system
        @author: ekkehard
        @return: boolean - True
        '''
        try:
            assetTag = self.getSuggestedAssetTag()
            if self.assetTagAccuracyLevel == 100:
                if not(self.getLANLAssetTagFilesystem() == assetTag):
                    try :
                        filepointer = open(self.lanl_property_file, "w")
                        filepointer.write(assetTag)
                        filepointer.close()
                        self.initializeLanlAssetTagFilesystem()
                        assetTag = self.getLANLAssetTagFilesystem()
                    except Exception, err :
                        messagestring = "Problem writing: " + self.lanl_property_file + \
                        " error: " + str(err)
                        self.logdispatch.log(LogPriority.DEBUG, messagestring)
            else:
Ejemplo n.º 36
0
class DisableRemoteAppleEvents(Rule):
    '''classdocs'''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 213
        self.rulename = 'DisableRemoteAppleEvents'
        self.formatDetailedResults("initialize")
        self.compliant = False
        self.mandatory = True
        self.rootrequired = True
        self.guidance = ['CIS 1.4.14.10']
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.sethelptext()

        # set up CIs
        datatype = 'bool'
        key = 'DISABLEREMOTEAPPLEEVENTS'
        instructions = 'To allow the use of remote apple events on this system, set the value of DisableRemoteAppleEvents to False.'
        default = True
        self.disableremoteevents = self.initCi(datatype, key, instructions,
                                               default)

        datatype2 = 'list'
        key2 = 'REMOTEAPPLEEVENTSUSERS'
        instructions2 = 'If you have a business requirement to have remote apple events turned on, enter a list of users who will be allowed access to remote apple events on this system'
        default2 = []
        self.secureremoteevents = self.initCi(datatype2, key2, instructions2,
                                              default2)

    def report(self):
        '''return the compliance status of the system with this rule


        :returns: self.compliant

        :rtype: bool

@author: Breen Malmberg

        '''

        self.detailedresults = ""
        self.cmhelper = CommandHelper(self.logger)
        self.compliant = True

        try:

            print(("Value of disableremoteevents CI = " +
                   str(self.disableremoteevents.getcurrvalue())))

            if self.disableremoteevents.getcurrvalue():
                if not self.reportDisabled():
                    self.compliant = False

            print(("Value of secureremoteevents CI = " +
                   str(self.secureremoteevents.getcurrvalue())))

            if self.secureremoteevents.getcurrvalue() != []:
                if not self.reportSecured():
                    self.compliant = False

        except Exception:
            self.detailedresults += '\n' + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def reportDisabled(self):
        ''' '''

        self.logger.log(LogPriority.DEBUG,
                        "Checking if remote apple events are disabled...")

        retval = False
        get_remote_ae = "/usr/sbin/systemsetup -getremoteappleevents"
        searchphrase = "Remote Apple Events: Off"

        self.cmhelper.executeCommand(get_remote_ae)
        outputlist = self.cmhelper.getOutput()
        for line in outputlist:
            if re.search(searchphrase, line, re.IGNORECASE):
                retval = True

        if not retval:
            self.detailedresults += "\nRemote Apple Events are On"
        else:
            self.detailedresults += "\nRemote Apple Events are Off"

        return retval

    def reportSecured(self):
        ''' '''

        self.logger.log(LogPriority.DEBUG,
                        "Checking if remote apple events is secured...")

        retval = True
        uuid_list = self.getUUIDs(self.secureremoteevents.getcurrvalue())
        remote_ae_users = self.getRemoteAEUsers()

        difference = list(set(uuid_list) - set(remote_ae_users))
        if difference:
            retval = False
            self.detailedresults += "\nThe current list of allowed remote access users does not match the desired list of remote access users"
            self.detailedresults += "\nDifference: " + " ".join(difference)

        return retval

    def getRemoteAEUsers(self):
        '''return a list of uuid's of current remote ae users
        (mac os x stores the remote ae users as uuid's in a plist)


        '''

        get_remote_ae_users = "/usr/bin/dscl . read /Groups/com.apple.access_remote_ae GroupMembers"
        remote_ae_users = []

        # it is possible that the key "GroupMembers" does not exist
        # this is because when you remove the last remote ae user from the list,
        # mac os x deletes this key from the plist as well..
        self.cmhelper.executeCommand(get_remote_ae_users)
        retcode = self.cmhelper.getReturnCode()
        if retcode == 0:
            remote_ae_users = self.cmhelper.getOutputString().split()
        else:
            errmsg = self.cmhelper.getErrorString()
            self.logger.log(LogPriority.DEBUG, str(errmsg))
        if "GroupMembers:" in remote_ae_users:
            remote_ae_users.remove("GroupMembers:")

        return remote_ae_users

    def fix(self):
        '''run commands needed to bring the system to a compliant state with this
        rule


        :returns: self.rulesuccess

        :rtype: bool

@author: Breen Malmberg

        '''

        self.iditerator = 0
        self.rulesuccess = True
        self.detailedresults = ""

        try:

            if self.disableremoteevents.getcurrvalue():
                if not self.disable_remote_ae():
                    self.rulesuccess = False

            if self.secureremoteevents.getcurrvalue():
                if not self.secure_remote_ae():
                    self.rulesuccess = False

        except Exception:
            self.detailedresults += '\n' + traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess

    def disable_remote_ae(self):
        ''' '''

        retval = True

        disable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents off"
        undocmd = "/usr/sbin/systemsetup setremoteappleevents on"
        self.cmhelper.executeCommand(disable_remote_ae_cmd)
        retcode = self.cmhelper.getReturnCode()
        if retcode != 0:
            retval = False
        else:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "commandstring", "command": undocmd}

            self.statechglogger.recordchgevent(myid, event)

        return retval

    def secure_remote_ae(self):
        ''' '''

        retval = True
        desired_remote_ae_users = self.getUUIDs(
            self.secureremoteevents.getcurrvalue())
        securecmd = "/usr/bin/dscl . create /Groups/com.apple.access_remote_ae GroupMembers " + " ".join(
            desired_remote_ae_users)
        original_remote_ae_users = self.getRemoteAEUsers()
        if original_remote_ae_users:
            undocmd = "/usr/bin/dscl . create /Groups/com.apple.access_remote_ae GroupMembers " + " ".join(
                original_remote_ae_users)
        else:
            undocmd = "/usr/bin/dscl . delete /Groups/com.apple.access_remote_ae GroupMembers"

        self.cmhelper.executeCommand(securecmd)
        retcode = self.cmhelper.getReturnCode()
        if retcode == 0:
            self.iditerator += 1

            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "commandstring", "command": undocmd}

            self.statechglogger.recordchgevent(myid, event)
        else:
            retval = False
            self.detailedresults += "\nFailed to properly configure the desired remote apple events users"

        # we assume the user wants to use the service if they configure the user list for it
        # and turn off the disable events CI
        if not self.disableremoteevents.getcurrvalue():
            undo_enable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents off"
            enable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents on"
            self.cmhelper.executeCommand(enable_remote_ae_cmd)
            retcode = self.cmhelper.getReturnCode()
            if retcode != 0:
                retval = False
                self.detailedresults += "\nFailed to enable remote apple events service"
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {
                    "eventtype": "commandstring",
                    "command": undo_enable_remote_ae_cmd
                }
                self.statechglogger.recordchgevent(myid, event)

        return retval

    def getUUIDs(self, userlist):
        '''convert the desired (user-specified) list of ae user names
        into uuid's; return as list

        :param userlist: 

        '''

        uuidlist = []

        for user in userlist:
            output = ""
            get_uuid = "/usr/bin/dsmemberutil getuuid -U " + user
            self.cmhelper.executeCommand(get_uuid)
            output = self.cmhelper.getOutputString()
            if re.search("no uuid", output, re.IGNORECASE):
                continue
            else:
                if output:
                    uuidlist.append(output.strip())

        return uuidlist
Ejemplo n.º 37
0
class ConfigureLinuxFirewall(Rule):
    '''The configureLinuxFirewall class attempts to audit and configure firewalls
    for Linux OS based systems. Note: there is tremendous variations in the
    approach taken by the various distributions on how to manage firewalls,
    this code should work effectively for debian, ubuntu, RHEL and close
    derivatives. Note: unlike many other rules this behaves as a binary state
    manager, the undo will set the system back to an as new state with no
    firewalls.


    '''

    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 92
        self.rulename = 'ConfigureLinuxFirewall'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {'type': 'white',
                           'family': ['linux']}
        self.servicehelper = ServiceHelper(self.environ, self.logger)
        self.serviceTarget = ""
        self.cmdhelper = CommandHelper(self.logger)
        self.guidance = ['NIST 800-53 AC-4', 'DISA RHEL 7 STIG 2.5.7.1',
                         'DISA RHEL 7 STIG 2.5.7.1.1',
                         'DISA RHEL 7 STIG 2.5.8.1.1',
                         'DISA RHEL 7 STIG 2.5.8.1.2',
                         'DISA RHEL 7 STIG 2.5.8.1.3',
                         'DISA RHEL 7 STIG 2.5.8.2.1',
                         'DISA RHEL 7 STIG 2.5.8.2.2',
                         'DISA RHEL 7 STIG 2.5.8.2.3',
                         'DISA RHEL 7 STIG 2.5.8.2.4']
        datatype = 'bool'
        key = 'CONFIGURELINUXFIREWALL'
        instructions = '''To disable this rule set the value of \
CONFIGURELINUXFIREWALL to False.'''
        default = False
        self.clfci = self.initCi(datatype, key, instructions, default)
        self.scriptType = ""
        self.iptScriptPath = ""
        self.iptables, self.ip6tables, self.iprestore, self.ip6restore = "", "", "", ""
        self.checkIptables()
        self.iditerator = 0

    def report(self):
        '''Report on whether the firewall meets baseline expectations.


        :returns: bool
        @author: D.Kennel
        @change: dwalker - updating rule to check for every possible firewall
            implementation and configure it rather than mutually exclusively
            checking based on system.

        '''
        try:
            compliant = True
            iptablesrunning = False
            ip6tablesrunning = False
            iptablesenabled = False
            ip6tablesenabled = False
            catchall = False
            catchall6 = False
            self.detailedresults = ""
            scriptExists = ""

            if self.checkFirewalld():
                if not self.servicehelper.auditService('firewalld.service', serviceTarget=self.serviceTarget):
                    compliant = False
                    self.detailedresults = 'This system appears to have firewalld but it is not running as required'

            elif self.checkUFW():
                cmdufw = '/usr/sbin/ufw status'
                if not self.cmdhelper.executeCommand(cmdufw):
                    self.detailedresults += "Unable to run " + \
                        "ufw status command\n"
                    compliant = False
                else:
                    outputufw = self.cmdhelper.getOutputString()
                    if re.search('Status: inactive', outputufw):
                        compliant = False
                        self.detailedresults += 'This system appears to have ' + \
                            'ufw but it is not running as required'
                    elif re.search('Status: active', outputufw):
                        cmdufw = "/usr/sbin/ufw status verbose"
                        if not self.cmdhelper.executeCommand(cmdufw):
                            compliant = False
                            self.detailedresults += "Cannot retrieve firewall rules\n"
                        else:
                            outputufw = self.cmdhelper.getOutputString()
                            if not re.search("Default\:\ deny\ \(incoming\)", outputufw):
                                compliant = False
                                self.detailedresults += "The default value for " + \
                                    "incoming unspecified packets is not deny\n"
            else:
                if os.path.exists("/etc/network/if-pre-up.d"):
                    self.iptScriptPath = "/etc/network/if-pre-up.d/iptables"
                    self.scriptType = "debian"
                elif os.path.exists("/etc/sysconfig/scripts"):
                    self.iptScriptPath = "/etc/sysconfig/scripts/SuSEfirewall2-custom"
                    self.scriptType = "suse"
                else:
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "No acceptable path for a startup " +
                                     "script found"])
                if self.iptScriptPath:
                    if os.path.exists(self.iptScriptPath):
                        scriptExists = True
                    else:
                        scriptExists = False
                else:
                    scriptExists = True

                if self.scriptType != "debian":
                    self.servicehelper.stopService('iptables')
                    self.servicehelper.startService('iptables')
                    self.servicehelper.stopService('ip6tables')
                    self.servicehelper.startService('ip6tables')
                    if self.servicehelper.isRunning('iptables'):
                        iptablesrunning = True
                    if self.servicehelper.isRunning('ip6tables'):
                        ip6tablesrunning = True
                    if self.servicehelper.auditService('iptables'):
                        iptablesenabled = True
                    if self.servicehelper.auditService('ip6tables'):
                        ip6tablesenabled = True
                else:
                    iptablesrunning = True
                    ip6tablesrunning = True
                    iptablesenabled = True
                    ip6tablesenabled = True

                if self.iptables:
                    cmd = [self.iptables, "-L"]
                    if not self.cmdhelper.executeCommand(cmd):
                        self.detailedresults += "Unable to run " + \
                            "iptables -L command\n"
                        compliant = False
                    else:
                        output = self.cmdhelper.getOutput()
                        for line in output:
                            if re.search('Chain INPUT \(policy REJECT\)|REJECT' +
                                         '\s+all\s+--\s+anywhere\s+anywhere', line):
                                catchall = True
                                break

                # check to see if the kernel was compiled with ipv6 support first
                lsmod_paths = ["/sbin/lsmod", "/usr/sbin/lsmod"]
                lsmod_path = ""
                for p in lsmod_paths:
                    if os.path.exists(p):
                        lsmod_path = p
                if lsmod_path:
                    check_ipv6_kernel = "/sbin/lsmod | grep -w 'ipv6'"
                    self.cmdhelper.executeCommand(check_ipv6_kernel)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode == 0:
                        # if system has kernel support for ipv6, then check for ip6tables bin, then do ip6tables rules check
                        if self.ip6tables:
                            cmd6 = [self.ip6tables, "-L"]
                            if not self.cmdhelper.executeCommand(cmd6):
                                self.detailedresults += "Unable to run " + \
                                    "ip6tables -L command\n"
                                compliant = False
                            else:
                                output6 = self.cmdhelper.getOutput()
                                for line in output6:
                                    if re.search('Chain INPUT \(policy REJECT\)|REJECT' +
                                                 '\s+all\s+anywhere\s+anywhere', line):
                                        catchall6 = True
                                        break
                        if not catchall6:
                            compliant = False
                            self.detailedresults += 'This system appears to use ' + \
                                                    'ip6tables but does not contain the expected rules. ' + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            ['ConfigureLinuxFirewall.report',
                                             "Missing v6 deny all."])
                        if not ip6tablesenabled:
                            compliant = False
                            self.detailedresults += "ip6tables not enabled " + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                        if not ip6tablesrunning:
                            compliant = False
                            self.detailedresults += 'This system appears to use ' + \
                                                    'ip6tables but it is not running as required. ' + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            ['ConfigureLinuxFirewall.report',
                                             "RHEL 6 type system. IP6tables not running."])
                    else:
                        self.logger.log(LogPriority.DEBUG, "This system's kernel was compiled without ipv6 support. Skipping ip6tables checks...")
                        self.detailedresults += "\nThis system does not have support for ipv6. Skipping ip6tables checks..."

                else:
                    self.logger.log(LogPriority.DEBUG, "Unable to detect lsmod utility - which is required to perform a necessary pre-check before attempting to run ip6tables commands.")
                    self.detailedresults += "\nUnable to determine if this system has support for ipv6. Skipping ip6tables checks..."

                if not catchall:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but does not contain the expected rules\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "Missing v4 deny all."])

                if not iptablesrunning:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but it is not running as required.\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "RHEL 6 type system. IPtables not running."])
                if not iptablesenabled:
                    compliant = False
                    self.detailedresults += "iptables not enabled\n"

                if not scriptExists:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but the startup script is not present\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "Missing startup script"])
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        '''Enable the firewall services and establish basic rules if needed.

        @author: D. Kennel


        '''
        try:
            if not self.clfci.getcurrvalue():
                return
            self.iditerator = 0
            self.detailedresults = ""
            success = True
            # delete past state change records from previous fix
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            #firewall-cmd portion
            if self.checkFirewalld():
                if self.servicehelper.enableService('firewalld.service', serviceTarget=self.serviceTarget):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    cmd = "/usr/bin/systemctl disable firewalld.service"
                    event = {"eventtype": "commandstring",
                             "command": cmd}
                    self.statechglogger.recordchgevent(myid, event)
                    self.detailedresults += "Firewall configured.\n "
                else:
                    success = False
                    self.detailedresults += "Unable to enable firewall\n"
                    debug = "Unable to enable firewall\n"
                    self.logger.log(LogPriority.DEBUG, debug)

            #ufw command portion
            elif self.checkUFW():
                self.logger.log(LogPriority.DEBUG, "System uses ufw. Running ufw commands...")
                cmdufw = '/usr/sbin/ufw status'
                if not self.cmdhelper.executeCommand(cmdufw):
                    self.detailedresults += "Unable to run " + \
                        "ufw status command\n"
                    success = False
                else:
                    outputufw = self.cmdhelper.getOutputString()
                    if re.search('Status: inactive', outputufw):
                        ufwcmd = '/usr/sbin/ufw --force enable'
                        if not self.cmdhelper.executeCommand(ufwcmd):
                            self.detailedresults += "Unable to run " + \
                                "ufw enable command\n"
                            success = False
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            undocmd = "/usr/sbin/ufw --force disable"
                            event = {"eventtype": "commandstring",
                                     "command": undocmd}
                            self.statechglogger.recordchgevent(myid, event)
                            cmdufw = "/usr/sbin/ufw status verbose"
                            if not self.cmdhelper.executeCommand(cmdufw):
                                self.detailedresults += "Unable to retrieve firewall rules\n"
                                success = False
                            else:
                                outputfw = self.cmdhelper.getOutputString()
                                if not re.search("Default\:\ deny\ \(incoming\)", outputfw):
                                    ufwcmd = "/usr/sbin/ufw default deny incoming"
                                    if not self.cmdhelper.executeCommand(ufwcmd):
                                        self.detailedresults += "Unable to set default " + \
                                            "rule for incoming unspecified packets\n"
                                        success = False
                                    else:
                                        self.iditerator += 1
                                        myid = iterate(self.iditerator, self.rulenumber)
                                        undocmd = "/usr/sbin/ufw default allow incoming"
                                        event = {"eventtype": "commandstring",
                                                 "command": undocmd}
                                        self.statechglogger.recordchgevent(myid, event)
                    elif re.search('Status: active', outputufw):
                        cmdufw = "/usr/sbin/ufw status verbose"
                        if not self.cmdhelper.executeCommand(cmdufw):
                            self.detailedresults += "Cannot retrieve firewall rules\n"
                            success = False
                        else:
                            outputufw = self.cmdhelper.getOutputString()
                            if not re.search("Default\:\ deny\ \(incoming\)", outputufw):
                                ufwcmd = "/usr/sbin/ufw default deny incoming"
                                if not self.cmdhelper.executeCommand(ufwcmd):
                                    self.detailedresults += "Unable to set default " + \
                                        "rule for incoming unspecified packets\n"
                                    success = False
                                else:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    undocmd = "/usr/sbin/ufw default allow incoming"
                                    event = {"eventtype": "commandstring",
                                             "command": undocmd}
                                    self.statechglogger.recordchgevent(myid, event)
            else:
                #following portion is mainly for debian and opensuse systems only

                if os.path.exists("/etc/network/if-pre-up.d"):
                    self.iptScriptPath = "/etc/network/if-pre-up.d/iptables"
                    self.scriptType = "debian"
                    servicename = "networking"
                elif os.path.exists("/etc/sysconfig/scripts"):
                    self.iptScriptPath = "/etc/sysconfig/scripts/SuSEfirewall2-custom"
                    self.scriptType = "suse"
                    servicename = "network"
                #this script will ensure that iptables gets configured
                #each time the network restarts
                iptables = self.getScriptValues("iptables")
                ip6tables = self.getScriptValues("ip6tables")
                iptScript = ""
                created = False
                if self.iptScriptPath:
                    if self.scriptType == "debian":
                        if self.iprestore and self.ip6restore:
                            iptScript = '#!/bin/bash\n' + self.iprestore + \
                                        ' <<< "' + iptables + '"\n' + self.ip6restore + \
                                        ' <<< "' + ip6tables + '"'
                    else:
                        iptScript = self.getScriptValues("iptscript")
                    if iptScript:
                        if not os.path.exists(self.iptScriptPath):
                            if not createFile(self.iptScriptPath, self.logger):
                                success = False
                                self.detailedresults += "Unable to create file " + self.iptScriptPath + "\n"
                            else:
                                created = True
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                event = {"eventtype": "creation",
                                         "filepath": self.iptScriptPath}
                                self.statechglogger.recordchgevent(myid, event)
                        if os.path.exists(self.iptScriptPath):
                            if not checkPerms(self.iptScriptPath, [0, 0, 0o755], self.logger):
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    if not setPerms(self.iptScriptPath, [0, 0, 0o755], self.logger, self.statechglogger, myid):
                                        success = False
                                        self.detailedresults += "Unable to set permissions on " + self.iptScriptPath + "\n"
                            contents = readFile(self.iptScriptPath, self.logger)
                            if contents != iptScript:
                                tempfile = self.iptScriptPath + ".tmp"
                                if not writeFile(tempfile, iptScript, self.logger):
                                    success = False
                                    self.detailedresults += "Unable to write contents to " + self.iptScriptPath + "\n"
                                else:
                                    if not created:
                                        self.iditerator += 1
                                        myid = iterate(self.iditerator, self.rulenumber)
                                        event = {"eventtype": "conf",
                                                 "filepath": self.iptScriptPath}
                                        self.statechglogger.recordchgevent(myid, event)
                                        self.statechglogger.recordfilechange(self.iptScriptPath, tempfile, myid)
                                    os.rename(tempfile, self.iptScriptPath)
                                    os.chown(self.iptScriptPath, 0, 0)
                                    os.chmod(self.iptScriptPath, 0o755)
                                    resetsecon(self.iptScriptPath)

                            stonixfilepath = "/var/db/stonix/"
                            savecmd = "/sbin/iptables-save > " + stonixfilepath + "user-firewall-pre-stonix"
                            if not self.cmdhelper.executeCommand(savecmd):
                                success = False
                                self.detailedresults += "Unable to save current ipv4 " + \
                                                        "firewall rules for revert\n"
                                debug = "Unable to save current ipv4 " + \
                                        "firewall rules for revert\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            save6cmd = "/sbin/ip6tables-save > " + stonixfilepath + "user-firewall6-pre-stonix"
                            if not self.cmdhelper.executeCommand(save6cmd):
                                success = False
                                self.detailedresults += "Unable to save current ipv6 " + \
                                                        "firewall rules for revert\n"
                                debug = "Unable to save current ipv6 " + \
                                        "firewall rules for revert\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            self.servicehelper.stopService(servicename)

                            if not self.servicehelper.startService(servicename):
                                success = False
                                self.detailedresults += "Unable to restart networking\n"
                                debug = "Unable to restart networking\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            else:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                cmd = "/sbin/iptables-restore < " + stonixfilepath + "user-firewall-pre-stonix"
                                event = {"eventtype": "commandstring",
                                         "command": cmd}
                                self.statechglogger.recordchgevent(myid, event)
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                cmd = "/sbin/ip6tables-restore < " + stonixfilepath + "user-firewall6-pre-stonix"
                                event = {"eventtype": "commandstring",
                                         "command": cmd}
                                self.statechglogger.recordchgevent(myid, event)

                    else:
                        success = False
                        self.detailedresults += "There is no iptables startup script\n"
                        debug = "There is no iptables startup script\n"
                        self.logger.log(LogPriority.DEBUG, debug)

                #this portion mostly applies to RHEL6 and Centos6
                if os.path.exists('/usr/bin/system-config-firewall') or \
                        os.path.exists('/usr/bin/system-config-firewall-tui'):
                    systemconfigfirewall = self.getScriptValues("systemconfigfirewall")
                    sysconfigiptables = self.getScriptValues("sysconfigiptables")
                    sysconfigip6tables = self.getScriptValues("sysconfigip6tables")

                    fwpath = '/etc/sysconfig/system-config-firewall'
                    iptpath = '/etc/sysconfig/iptables'
                    ip6tpath = '/etc/sysconfig/ip6tables'
                    #portion to handle the system-config-firewall file
                    created = False
                    if not os.path.exists(fwpath):
                        if not createFile(fwpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + fwpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": fwpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(fwpath):
                        if not checkPerms(fwpath, [0, 0, 0o600], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(fwpath, [0, 0, 0o600], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + fwpath + "\n"
                        contents = readFile(fwpath, self.logger)
                        if contents != systemconfigfirewall:
                            print("contents don't equal systemconfigurefirewall contents\n")
                            tempfile = fwpath + ".tmp"
                            if not writeFile(tempfile, systemconfigfirewall, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + fwpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": fwpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(fwpath, tempfile, myid)
                                os.rename(tempfile, fwpath)
                                os.chown(fwpath, 0, 0)
                                os.chmod(fwpath, 0o600)
                                resetsecon(fwpath)
                    created = False
                    #portion to handle the iptables rules file
                    if not os.path.exists(iptpath):
                        if not createFile(iptpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + iptpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": iptpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(iptpath):
                        if not checkPerms(iptpath, [0, 0, 0o644], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(iptpath, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + iptpath + "\n"
                        contents = readFile(iptpath, self.logger)
                        if contents != sysconfigiptables:
                            tempfile = iptpath + ".tmp"
                            if not writeFile(tempfile, sysconfigiptables, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + iptpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": iptpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(iptpath, tempfile, myid)
                                os.rename(tempfile, iptpath)
                                os.chown(iptpath, 0, 0)
                                os.chmod(iptpath, 0o644)
                                resetsecon(iptpath)
                    created = False
                    #portion to handle ip6tables rules file
                    if not os.path.exists(ip6tpath):
                        if not createFile(ip6tpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + ip6tpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": ip6tpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(ip6tpath):
                        if not checkPerms(ip6tpath, [0, 0, 0o644], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(ip6tpath, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + ip6tpath + "\n"
                        contents = readFile(ip6tpath, self.logger)
                        if contents != sysconfigip6tables:
                            tempfile = ip6tpath + ".tmp"
                            if not writeFile(tempfile, sysconfigip6tables, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + ip6tpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": ip6tpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(ip6tpath, tempfile, myid)
                                os.rename(tempfile, ip6tpath)
                                os.chown(ip6tpath, 0, 0)
                                os.chmod(ip6tpath, 0o644)
                                resetsecon(ip6tpath)
                    # check if iptables is enabled to run at start
                    if not self.servicehelper.auditService('iptables'):
                        # enable service to run at start if not
                        if not self.servicehelper.enableService('iptables'):
                            self.detailedresults += "Unable to enable iptables service\n"
                            debug = "Unable to enable iptables service\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                        else:
                            # record event if successful
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = self.servicehelper.getDisableCommand('iptables')
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)

                    self.servicehelper.stopService('iptables')
                    # start iptables if not
                    if not self.servicehelper.startService('iptables'):
                        self.detailedresults += "Unable to start iptables service\n"
                        debug = "Unable to start iptables service\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                    else:
                        stonixfilepath = "/var/db/stonix/"
                        savecmd = "/sbin/iptables-save > " + stonixfilepath + "user-firewall-pre-stonix"
                        if not self.cmdhelper.executeCommand(savecmd):
                            success = False
                            self.detailedresults += "Unable to save current ipv4 " + \
                                                    "firewall rules for revert\n"
                            debug = "Unable to save current ipv4 " + \
                                    "firewall rules for revert\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = "/sbin/iptables-restore < " + stonixfilepath + "user-firewall-pre-stonix"
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)
                        savecmd = "/sbin/ip6tables-save > " + stonixfilepath + "user-firewall6-pre-stonix"
                        if not self.cmdhelper.executeCommand(savecmd):
                            success = False
                            self.detailedresults += "Unable to save current ipv6 " + \
                                                    "firewall rules for revert\n"
                            debug = "Unable to save current ipv6 " + \
                                    "firewall rules for revert\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = "/sbin/ip6tables-restore < " + stonixfilepath + "user-firewall6-pre-stonix"
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)
                    # check if ip6tables is enabled to run at start
                    if not self.servicehelper.auditService('ip6tables'):
                        # enable service to run at start if not
                        if not self.servicehelper.enableService('ip6tables'):
                            self.detailedresults += "Unable to enable ip6tables service\n"
                            debug = "Unable to enable ip6tables service\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                        else:
                            # record event if successful
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = self.servicehelper.getDisableCommand('ip6tables')
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)

                    self.servicehelper.stopService('ip6tables')
                    # start ip6tables if not
                    if not self.servicehelper.startService('ip6tables'):
                        self.detailedresults += "Unable to start ip6tables service\n"
                        debug = "Unable to start ip6tables service\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False

                     # Sleep for a bit to let the restarts occur
                    time.sleep(10)
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def checkFirewalld(self):
        """

        :return: bool; True if the firewall-cmd path exists, False if not
        """

        firewalld_paths = ["/bin/firewall-cmd", "/usr/bin/firewall-cmd"]
        for p in firewalld_paths:
            if os.path.exists(p):
                return True

    def checkUFW(self):
        #for Ubuntu systems mostly
        if os.path.exists('/usr/sbin/ufw'):
            return True

    def checkIsOther(self):
        #for debian and opensuse mostly
        if "iptables" not in self.servicehelper.listServices():
            return True

    def checkIptables(self):
        # mostly pertains to RHEL6, Centos6

        if os.path.exists("/usr/sbin/iptables"):
            self.iptables = "/usr/sbin/iptables"
        elif os.path.exists("/sbin/iptables"):
            self.iptables = "/sbin/iptables"

        if os.path.exists("/usr/sbin/ip6tables"):
            self.ip6tables = "/usr/sbin/ip6tables"
        elif os.path.exists("/sbin/ip6tables"):
            self.ip6tables = "/sbin/ip6tables"

        if os.path.exists("/usr/sbin/iptables-restore"):
            self.iprestore = "/usr/sbin/iptables-restore"
        elif os.path.exists("/sbin/iptables-restore"):
            self.iprestore = "/sbin/iptables-restore"

        if os.path.exists("/usr/sbin/ip6tables-restore"):
            self.ip6restore = "/usr/sbin/ip6tables-restore"
        elif os.path.exists("/sbin/ip6tables-restore"):
            self.ip6restore = "/sbin/ip6tables-restore"

    def getScriptValues(self, scriptname):
        if scriptname == "iptscript":
            iptScript = '''fw_custom_after_chain_creation() {
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
    true
}

fw_custom_before_port_handling() {
    true
}

fw_custom_before_masq() {
    true
}

fw_custom_before_denyall() {
    true
}

fw_custom_after_finished() {
    true
}
'''
            return iptScript
        elif scriptname == "iptables":
            iptables = '''*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
'''
            return iptables
        elif scriptname == "ip6tables":
            ip6tables = '''*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
'''
            return ip6tables
        elif scriptname == "systemconfigfirewall":
            systemconfigfirewall = '''# Configuration file for system-config-firewall

--enabled
--service=ssh
'''
            return systemconfigfirewall
        elif scriptname == "sysconfigiptables":
            sysconfigiptables = '''# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
'''
            return sysconfigiptables
        elif scriptname == "sysconfigip6tables":
            sysconfigip6tables = '''# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
'''
            return sysconfigip6tables
class RestrictAccessToKernelMessageBuffer(Rule):
    '''Unprivileged access to the kernel syslog can expose sensitive kernel address information.'''

    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.environ = environ
        self.logger = logger
        self.rulenumber = 86
        self.rulename = "RestrictAccessToKernelMessageBuffer"
        self.mandatory = True
        self.rootrequired = True
        self.rulesuccess = True
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.guidance = ["CCE-RHEL7-CCE-TBD 2.2.4.5"]
        self.applicable = {"type": "white",
                           "family": "linux"}
        datatype = "bool"
        key = "RESTRICTACCESSTOKERNELMESSAGEBUFFER"
        instructions = "To prevent this rule from running, set the value of RestrictAccessToKernelMessageBuffer to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.localize()
        self.initobjs()

    def localize(self):
        '''determine system-specific settings
        
        @author: Breen Malmberg


        '''

        # set defaults
        self.fixcommand = "sysctl -w kernel.dmesg_restrict=1"
        self.reportcommand = "sysctl kernel.dmesg_restrict"

    def initobjs(self):
        '''initialize class objects
        
        @author: Breen Malmberg


        '''

        self.ch = CommandHelper(self.logger)

    def report(self):
        '''run report actions for this rule


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        self.detailedresults = ""
        self.compliant = True
        kernelopt = False

        try:

            self.ch.executeCommand(self.reportcommand)
            output = self.ch.getOutput()
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                self.detailedresults += "\nError while running command: " + str(self.reportcommand)
            for line in output:
                if re.search("kernel\.dmesg\_restrict", line):
                    kernelopt = True
                    sline = line.split('=')
                    if len(sline) > 1:
                        if str(sline[1]).strip() == '0':
                            self.compliant = False
                            self.detailedresults += "\nKernel message buffer is currently not restricted."
                    else:
                        self.compliant = False
                        self.detailedresults += "\nCould not determine the state of the kernel message buffer access restrictions."
            if not kernelopt:
                self.compliant = False
                self.detailedresults += "\nCould not determine the state of the kernel message buffer access restrictions."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        return self.compliant

    def fix(self):
        '''run fix actions for this rule


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

        success = True
        self.detailedresults = ""

        try:

            self.ch.executeCommand(self.fixcommand)
            rcode = self.ch.getReturnCode()
            errmsg = self.ch.getErrorString()
            if rcode != '0':
                success = False
                self.detailedresults += "\n" + str(errmsg)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", success, self.detailedresults)
        return success