Example #1
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
Example #2
0
class ConfigureSystemAuthentication(Rule):
    """
    Configure system authentication and password settings in accordance
with rhel 7 stig requirements
    """

    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 = 57
        self.rulename = "ConfigureSystemAuthentication"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.applicable = {'type': 'white',
                           'family': 'linux'}
        datatype = "bool"
        key = "CONFIGSYSAUTH"
        instructions = "To disable this rule, set the value of " + \
            "CONFIGSYSAUTH to False."
        default = True
        self.ci1 = self.initCi(datatype, key, instructions, default)

        datatype = "bool"
        key = "PASSWORDREQ"
        instructions = "To not configure password requirements, set " + \
            "PASSWORDREQ to False. This configuration item will configure " + \
            "PAM's password requirements when changing to a new password."
        default = True
        self.ci2 = self.initCi(datatype, key, instructions, default)

        datatype = "bool"
        key = "PASSWORDFAIL"
        instructions = "To not configure password fail locking, set " + \
            "PASSWORDFAIL to False. This configuration item will " + \
            "configure PAM's failed login attempts mechanism using either " + \
            "faillock or tally2."
        default = True
        self.ci3 = self.initCi(datatype, key, instructions, default)

        datatype = "bool"
        key = "PWHASHING"
        instructions = "To not set the hashing algorithm, set " + \
            "PWHASHING to False. This configuration item will configure " + \
            "libuser and/or login.defs, which specifies the hashing " + \
            "algorithm to use."
        default = True
        self.ci4 = self.initCi(datatype, key, instructions, default)

        self.guidance = ["NSA 2.3.3.1,", "NSA 2.3.3.2"]
        self.created = False
        self.localize()

    def localize(self):
        """
        set up session variables based on system platform and
        version
        """

        myos = self.environ.getostype().lower()

        if re.search("suse", myos):
            self.password = PASSWORD_ZYPPER
            self.auth = AUTH_ZYPPER
            self.acct = ACCOUNT_ZYPPER
        elif re.search("debian|ubuntu", myos):
            self.password = PASSWORD_APT
            self.auth = AUTH_APT
            self.acct = ACCOUNT_APT
        else:
            self.password = PASSWORD_YUM
            self.auth = AUTH_YUM
            self.acct = ACCOUNT_YUM
            self.session = SESSION_YUM

    def report(self):
        """
        ConfigureSystemAuthentication() report method to report if system
        is compliant with authentication and password settings

        @author: Derek Walker

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

        self.compliant = True
        self.detailedresults = ""
        self.ci2comp, self.ci3comp, self.ci4comp = True, True, True

        try:

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

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = 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 fix(self):
        """
        ConfigureSystemAuthentication.fix() method to fix the system to be
        compliant with authentication and password settings

        @author: Derek Walker

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

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

        try:
            if not self.ci1.getcurrvalue():
                return self.rulesuccess

            # delete past state change records from previous fix
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            if not self.fixLinux():
                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

    def reportLinux(self):
        """
        Linux-specific submethod for config reporting

        @author: Derek Walker

        :return: compliant
        :rtype: bool
        """

        self.logindefs = "/etc/login.defs"
        debug = ""
        compliant = True
        self.editor1, self.editor2 = "", ""
        self.pwqeditor = ""
        self.usingpwquality, self.usingcracklib = False, False
        self.usingpamtally2, self.usingpamfail = False, False
        self.created1, self.created2 = False, False
        self.libuserfile = "/etc/libuser.conf"
        self.ph = Pkghelper(self.logger, self.environ)
        self.determine_pwreqs_mechanism()

        # set pam password and authentication file paths
        pampassfiles = ["/etc/pam.d/common-password", "/etc/pam.d/common-password-pc", "/etc/pam.d/password-auth", "/etc/pam.d/password-auth-ac"]
        pamauthfiles = ["/etc/pam.d/common-auth", "/etc/pam.d/common-auth-pc", "/etc/pam.d/system-auth", "/etc/pam.d/system-auth-ac"]
        for f in pampassfiles:
            if os.path.isfile(f):
                self.pampassfile = f
        for f in pamauthfiles:
            if os.path.isfile(f):
                self.pamauthfile = f
        if not bool(self.pampassfile and self.pamauthfile):
            if self.ph.manager == "apt-get":
                self.pampassfile = "/etc/pam.d/common-password"
                self.pamauthfile = "/etc/pam.d/common-auth"
            elif self.ph.manager == "zypper":
                self.pampassfile = "/etc/pam.d/common-password-pc"
                self.pamauthfile = "/etc/pam.d/common-auth-pc"
            else:
                self.pampassfile = "/etc/pam.d/password-auth"
                self.pamauthfile = "/etc/pam.d/system-auth"

        if not self.check_pwreqs_configured():
            self.ci2comp = False
            debug += "checkpasswordreqs method is False compliancy\n"
            compliant = False
        if not self.checkaccountlockout():
            self.ci3comp = False
            debug += "checkaccountlockout method is False compliancy\n"
            compliant = False
        if not self.checklogindefs():
            self.ci4comp = False
            debug += "checklogindefs method is False compliancy\n"
            compliant = False
        if not self.checklibuser():
            self.ci4comp = False
            debug += "checklibuser method is False compliancy\n"
            compliant = False
        if debug:
            self.logger.log(LogPriority.DEBUG, debug)

        if not self.check_showfailed_logins():
            compliant = False

        return compliant

    def fixLinux(self):
        """
        Linux specific submethod to correct linux distributions.  If your
        system is portage based, i.e. gentoo, you will need to do a manual
        fix for everything except the login.defs file

        @author: Derek Walker

        :return: success
        :rtype: bool
        """

        success = True

        try:
            if self.ph.manager == "dnf":
                if not self.ph.check("authconfig"):
                    self.ph.install("authconfig") # this is needed by fedora to continue
        except:
            pass

        # """create backups of pamfiles"""
        if os.path.exists(self.pampassfile):
            createFile(self.pampassfile + ".backup", self.logger)
        if os.path.exists(self.pamauthfile):
            createFile(self.pamauthfile + ".backup", self.logger)

        if self.ci2.getcurrvalue():
            if not self.ci2comp:

                # configure regex for pwquality
                if self.usingpwquality:
                    self.password = re.sub("pam_cracklib\.so", "pam_pwquality.so", self.password)
                    if self.environ.getsystemfismacat() == "high":
                        self.password = re.sub("minlen=8", "minlen=14", self.password)
                        self.password = re.sub("minclass=3", "minclass=4", self.password)
                        regex = PWQUALITY_HIGH_REGEX
                    else:
                        regex = PWQUALITY_REGEX
                    if self.pwqinstalled:
                        if not self.setpasswordsetup(regex):
                            success = False
                    else:
                        if not self.setpasswordsetup(regex, self.pwqualitypkgs):
                            success = False

                # configure regex for cracklib
                elif self.usingcracklib:
                    self.password = re.sub("pam_pwquality\.so", "pam_cracklib.so", self.password)
                    if self.environ.getsystemfismacat() == "high":
                        self.password = re.sub("minlen=8", "minlen=14", self.password)
                        self.password = re.sub("minclass=3", "minclass=4", self.password)
                        regex = CRACKLIB_HIGH_REGEX
                    else:
                        regex = CRACKLIB_REGEX
                    if self.clinstalled:
                        if not self.setpasswordsetup(regex):
                            success = False
                    else:
                        if not self.setpasswordsetup(regex, self.cracklibpkgs):
                            success = False
                else:
                    error = "Could not find pwquality/cracklib pam module. Fix failed."
                    self.logger.log(LogPriority.ERROR, error)
                    self.detailedresults += error + "\n"
                    return False
        if self.ci3.getcurrvalue():
            if not self.ci3comp:
                if self.usingpamfail:
                    regex = PAMFAIL_REGEX
                    if not self.setaccountlockout(regex):
                        success = False
                        self.detailedresults += "Unable to configure pam for faillock\n"
                elif self.usingpamtally2:
                    regex = PAMTALLY_REGEX
                    if not self.setaccountlockout(regex):
                        success = False
                        self.detailedresults += "Unable to configure pam for pam_tally2\n"
                else:
                    self.detailedresults += "There is no account lockout program available for this system\n"
                    success = False
        if self.ci4.getcurrvalue():
            if not self.ci4comp:
                if not self.checklibuser():
                    if not self.setlibuser():
                        debug = "setlibuser() failed\n"
                        self.detailedresults += "Unable to configure /etc/libuser.conf\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                if not self.checklogindefs():
                    if not self.setlogindefs():
                        debug = "setdefpasshash() failed\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        self.detailedresults += "Unable to configure /etc/login.defs file\n"
                        success = False

        if not self.set_showfailed_logins():
            success = False

        return success

    def check_showfailed_logins(self):
        """
        config file: /etc/pam.d/postlogin-ac
        config option: session required pam_lastlog.so showfailed

        :return:
        """

        configured = True
        search_line = {"session": "required pam_lastlog.so showfailed"}
        config_file = "/etc/pam.d/postlogin-ac"
        tmpfile = config_file + ".stonixtmp"

        self.lastlog_editor = KVEditorStonix(self.statechglogger, self.logger, "conf", config_file, tmpfile, search_line, "present", "space")
        if not self.lastlog_editor.report():
            configured = False
            self.detailedresults += "\nShow failed logins option is not configured correctly in PAM"

        return configured

    def set_showfailed_logins(self):
        """
        config file: /etc/pam.d/postlogin-ac
        config option: session required pam_lastlog.so showfailed

        :return: success
        :rtype: bool
        """

        success = True

        if not self.lastlog_editor.fix():
            success = False
            self.detailedresults += "\nFailed to enable show failed logins option in PAM"
        elif not self.lastlog_editor.commit():
            success = False
            self.detailedresults += "\nFailed to enable show failed logins option in PAM"

        return success

    def determine_pwreqs_mechanism(self):
        """
        determine whether this system is using cracklib or pwquality
        as a password requirements control mechanism
        use pwquality by default since cracklib is legacy

        """

        self.usingcracklib = False
        self.usingpwquality = False
        self.pwqualitypkg = ""
        self.cracklibpkg = ""

        # potential password requirements package names
        self.cracklibpkgs = ["libpam-cracklib",
                             "cracklib"]
        self.pwqualitypkgs = ["libpam-pwquality",
                              "pam_pwquality",
                              "libpwquality",
                              "libpwquality1"]

        # pwquality check
        for pkg in self.pwqualitypkgs:
            if self.ph.check(pkg):
                self.usingpwquality = True
                self.pwqualitypkg = pkg
                break
        if not self.usingpwquality:
            for pkg in self.pwqualitypkgs:
                if self.ph.checkAvailable(pkg):
                    self.usingpwquality = True
                    self.pwqualitypkg = pkg
                    break

        # cracklib check (only runs if pwquality check turns up nothing)
        if not self.usingpwquality:
            for pkg in self.cracklibpkgs:
                if self.ph.check(pkg):
                    self.usingcracklib = True
                    self.cracklibpkg = pkg
                    break
            if not self.usingcracklib:
                for pkg in self.cracklibpkgs:
                    if self.ph.checkAvailable(pkg):
                        self.usingcracklib = True
                        self.cracklibpkg = pkg
                        break

    def check_pwreqs_installed(self):
        """
        determine if either a cracklib package or pwquality
        package is installed

        :return: installed
        :rtype: bool
        """

        installed = False
        self.pwqinstalled = False
        self.clinstalled = False

        for pkg in self.pwqualitypkgs:
            if self.ph.check(pkg):
                self.pwqinstalled = True
        for pkg in self.cracklibpkgs:
            if self.ph.check(pkg):
                self.clinstalled = True

        if bool(self.clinstalled or self.pwqinstalled):
            installed = True

        return installed

    def check_pwreqs_configured(self):
        """
        check whether the password requirements have been properly configured
        in PAM, using either cracklib or pwquality

        :return: passwords_configured
        :rtype: bool
        """

        passwords_configured = True

        if not self.check_pwreqs_installed():
            passwords_configured = False
            return passwords_configured

        if self.usingpwquality:
            if not self.pwqinstalled:
                for pkg in self.pwqualitypkgs:
                    if self.ph.install(pkg):
                        self.pwqinstalled = True
                        break
            if not self.checkpasswordsetup("pwquality"):
                self.detailedresults += "System is using pwquality but it's not configured properly in PAM\n"
                passwords_configured = False

        elif self.usingcracklib:
            if not self.clinstalled:
                for pkg in self.cracklibpkgs:
                    if self.ph.install(pkg):
                        self.clinstalled = True
                        break
            if not self.checkpasswordsetup("cracklib"):
                self.detailedresults += "System is using cracklib but it's not configured properly in PAM\n"
                passwords_configured = False

        return passwords_configured

    def checkpasswordsetup(self, package):
        """
        Method called from within checkpasswordreqs method

        @author: Derek Walker

        :param package: string; name of package to check for
        :return: compliant
        :rtype: bool
        """

        compliant = True
        regex1 = ""

        if package == "pwquality":
            self.password = re.sub("pam_cracklib\.so", "pam_pwquality.so", self.password)
            if self.environ.getsystemfismacat() == "high":
                self.password = re.sub("minlen=8", "minlen=14", self.password)
                self.password = re.sub("minclass=3", "minclass=4", self.password)
                regex1 = PWQUALITY_HIGH_REGEX
            else:
                regex1 = PWQUALITY_REGEX
            if not self.chkpwquality():
                compliant = False

        elif package == "cracklib":
            self.password = re.sub("pam_pwquality\.so", "pam_cracklib.so", self.password)
            if self.environ.getsystemfismacat() == "high":
                self.password = re.sub("minlen=8", "minlen=14", self.password)
                self.password = re.sub("minclass=3", "minclass=4", self.password)
                regex1 = CRACKLIB_HIGH_REGEX
            else:
                regex1 = CRACKLIB_REGEX

        regex2 = "^password[ \t]+sufficient[ \t]+pam_unix.so\s+sha512\s+shadow\s+try_first_pass\s+use_authtok\s+remember=10"
        pamfiles = []

        if self.ph.manager in ("yum", "dnf"):
            pamfiles.append(self.pamauthfile)
            pamfiles.append(self.pampassfile)
        else:
            pamfiles.append(self.pampassfile)

        for pamfile in pamfiles:
            found1, found2 = False, False
            if not os.path.exists(pamfile):
                self.detailedresults += pamfile + " doesn't exist\n"
                compliant = False
            else:
                if not checkPerms(pamfile, [0, 0, 0o644], self.logger):
                    self.detailedresults += "Incorrect permissions or ownership exist for file " + pamfile
                    compliant = False
                contents = readFile(pamfile, self.logger)
                if not contents:
                    self.detailedresults += pamfile + " is blank\n"
                    compliant = False
                else:
                    for line in contents:
                        if re.search(regex1, line.strip()):
                            found1 = True
                        if re.search(regex2, line.strip()):
                            found2 = True

                    if not found1:
                        self.detailedresults += "\n'password requisite ...' line not correct in " + pamfile
                    if not found2:
                        self.detailedresults += "\n'password sufficient ...' line not correct in " + pamfile
                        compliant = False

        return compliant

    def setpasswordsetup(self, regex1, pkglist = None):
        """
        configure password requirements in pam, install necessary packages

        :param regex1: string; regular expression
        :param pkglist: list; string names of packages to install
        :return: success
        :rtype: bool
        """

        regex2 = "^password[ \t]+sufficient[ \t]+pam_unix.so sha512 shadow " + \
            "try_first_pass use_authtok remember=10"
        success = True
        pamfiles = []
        installed = False
        if pkglist:
            for pkg in pkglist:
                if self.ph.check(pkg):
                    installed = True
                    break
        else:
            installed = True
        if not installed:
            for pkg in pkglist:
                if self.ph.checkAvailable(pkg):
                    if not self.ph.install(pkg):
                        self.detailedresults += "Unable to install pkg " + pkg + "\n"
                        return False
                    else:
                        installed = True
                        if self.usingpwquality:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            comm = self.ph.getRemove() + pkg
                            event = {"eventtype": "commandstring",
                                     "command": comm}
                            self.statechglogger.recordchgevent(myid, event)
                            pwqfile = "/etc/security/pwquality.conf"
                            tmpfile = pwqfile + ".stonixtmp"
                            if self.environ.getsystemfismacat() == "high":
                                data = {"difok": "7",
                                        "minlen": "14",
                                        "dcredit": "0",
                                        "ucredit": "0",
                                        "lcredit": "0",
                                        "ocredit": "0",
                                        "maxrepeat": "3",
                                        "minclass": "4"}
                            else:
                                data = {"difok": "7",
                                        "minlen": "8",
                                        "dcredit": "0",
                                        "ucredit": "0",
                                        "lcredit": "0",
                                        "ocredit": "0",
                                        "maxrepeat": "3",
                                        "minclass": "3"}
                            self.pwqeditor = KVEditorStonix(self.statechglogger,
                                                            self.logger, "conf",
                                                            pwqfile, tmpfile, data,
                                                            "present", "openeq")
                            self.pwqeditor.report()
                            break
        if not installed:
            self.detailedresults += "No password checking program available\n"
            return False
        if self.usingpwquality:
            if not self.setpwquality():
                success = False
        if self.ph.manager in ("yum", "dnf"):
            writecontents = self.auth + "\n" + self.acct + "\n" + \
                self.password + "\n" + self.session
            pamfiles.append(self.pamauthfile)
            pamfiles.append(self.pampassfile)
        else:
            writecontents = self.password
            pamfiles.append(self.pampassfile)
        for pamfile in pamfiles:
            if not os.path.exists(pamfile):
                self.detailedresults += pamfile + " doesn't exist.\n" + \
                    "Stonix will not attempt to create this file " + \
                    "and the fix for the this rule will not continue\n"
                return False
        # """Check permissions on pam file(s)"""
        for pamfile in pamfiles:
            if not checkPerms(pamfile, [0, 0, 0o644], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(pamfile, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                    success = False
                    self.detailedresults += "Unable to set correct permissions on " + pamfile + "\n"
            contents = readFile(pamfile, self.logger)
            found1, found2 = False, False
            for line in contents:
                if re.search(regex1, line.strip()):
                    found1 = True
                if re.search(regex2, line.strip()):
                    found2 = True
            if not found1 or not found2:
                tmpfile = pamfile + ".stonixtmp"
                if writeFile(tmpfile, writecontents, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {'eventtype': 'conf',
                             'filepath': pamfile}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(pamfile, tmpfile, myid)
                    os.rename(tmpfile, pamfile)
                    os.chown(pamfile, 0, 0)
                    os.chmod(pamfile, 0o644)
                    resetsecon(pamfile)
                else:
                    self.detailedresults += "Unable to write to " + pamfile + "\n"
                    success = False
        return success

    def checkaccountlockout(self):
        """
        Method to determine which account locking program to
        use if any

        @author: Derek Walker

        :return: compliant
        :rtype: bool
        """

        which = "/usr/bin/which "
        cmd1 = which + "faillock"
        cmd2 = which + "pam_tally2"
        ch = CommandHelper(self.logger)
        pamfiles = []
        compliant = True
        regex = ""
        if ch.executeCommand(cmd1):
            debug = "ran " + cmd1 + " successfully\n"
            self.logger.log(LogPriority.DEBUG, debug)
            if ch.getReturnCode() == 0:
                debug = "return code of 0 and using faillock\n"
                self.logger.log(LogPriority.DEBUG, debug)
                self.usingpamfail = True
            elif ch.executeCommand(cmd2):
                debug = "ran " + cmd2 + " successfully\n"
                self.logger.log(LogPriority.DEBUG, debug)
                if ch.getReturnCode() == 0:
                    debug = "return code of 0 and using pam_tally2\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    self.usingpamtally2 = True
            else:
                self.detailedresults += "There is no account " + \
                        "locking program available for this " + \
                        "distribution\n"
                return False
        elif ch.executeCommand(cmd2):
                debug = "ran " + cmd2 + " successfully\n"
                self.logger.log(LogPriority.DEBUG, debug)
                if ch.getReturnCode() == 0:
                    debug = "return code of 0 and using pam_tally2\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    self.usingpamtally2 = True
                else:
                    self.detailedresults += "There is no account " + \
                        "locking program available for this " + \
                        "distribution\n"
                    return False
        else:
            self.detailedresults += "There is no account " + \
                "locking program available for this " + \
                "distribution\n"
            return False
        if self.usingpamfail:
            regex = "^auth[ \t]+required[ \t]+pam_faillock.so preauth silent audit " + \
                "deny=5 unlock_time=900 fail_interval=900"
        elif self.usingpamtally2:
            regex = "^auth[ \t]+required[ \t]+pam_tally2.so deny=5 " + \
                "unlock_time=900 onerr=fail"
        if self.ph.manager in("yum", "dnf"):
            pamfiles.append(self.pamauthfile)
            pamfiles.append(self.pampassfile)
        else:
            pamfiles.append(self.pamauthfile)
        for pamfile in pamfiles:
            found = False
            if not os.path.exists(pamfile):
                self.detailedresults += "Critical pam file " + pamfile + " doesn't exist\n"
                compliant = False
            else:
                if not checkPerms(pamfile, [0, 0, 0o644], self.logger):
                    self.detailedresults += "Permissions aren't correct on " + pamfile + "\n"
                    self.ci3comp = False
                    compliant = False
                contents = readFile(pamfile, self.logger)
                if not contents:
                    self.detailedresults += pamfile + " is blank\n"
                    self.ci3comp = False
                    compliant = False
                else:
                    for line in contents:
                        if re.search(regex, line.strip()):
                            found = True
                    if not found:
                        self.detailedresults += "Didn't find the correct contents in " + pamfile + "\n"
                        self.ci3comp = False
                        compliant = False
        return compliant

    def setaccountlockout(self, regex):
        """
        configure the account lockout time in pam

        :param regex: string; regular expression
        :return: success
        :rtype: bool
        """

        success = True
        pamfiles = []
        if self.ph.manager in ("yum", "dnf"):
            pamfiles.append(self.pamauthfile)
            pamfiles.append(self.pampassfile)
            writecontents = self.auth + "\n" + self.acct + "\n" + \
                        self.password + "\n" + self.session
        else:
            pamfiles.append(self.pamauthfile)
            writecontents = self.auth
        for pamfile in pamfiles:
            if not os.path.exists(pamfile):
                self.detailedresults += pamfile + " doesn't exist.\n" + \
                    "Stonix will not attempt to create this file " + \
                    "and the fix for the this rule will not continue\n"
                return False
        # """Check permissions on pam file(s)"""
        for pamfile in pamfiles:
            if not checkPerms(pamfile, [0, 0, 0o644], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(pamfile, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                    success = False
                    self.detailedresults += "Unable to set " + \
                        "correct permissions on " + pamfile + "\n"
            contents = readFile(pamfile, self.logger)
            found = False
            for line in contents:
                if re.search(regex, line.strip()):
                    found = True
            if not found:
                tmpfile = pamfile + ".stonixtmp"
                if writeFile(tmpfile, writecontents, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {'eventtype': 'conf',
                             'filepath': pamfile}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(pamfile, tmpfile, myid)
                    os.rename(tmpfile, pamfile)
                    os.chown(pamfile, 0, 0)
                    os.chmod(pamfile, 0o644)
                    resetsecon(pamfile)
                else:
                    self.detailedresults += "Unable to write to " + pamfile + "\n"
                    success = False
        return success

    def chkpwquality(self):
        """
        check settings of pwquality pam plugin

        :return: compliant
        :rtype: bool
        """

        compliant = True
        pwqfile = "/etc/security/pwquality.conf"
        if os.path.exists(pwqfile):
            tmpfile = pwqfile + ".stonixtmp"
            if self.environ.getsystemfismacat() == "high":
                data = {"difok": "7",
                        "minlen": "14",
                        "dcredit": "0",
                        "ucredit": "0",
                        "lcredit": "0",
                        "ocredit": "0",
                        "maxrepeat": "3",
                        "minclass": "4"}
            else:
                data = {"difok": "7",
                        "minlen": "8",
                        "dcredit": "0",
                        "ucredit": "0",
                        "lcredit": "0",
                        "ocredit": "0",
                        "maxrepeat": "3",
                        "minclass": "3"}
            self.pwqeditor = KVEditorStonix(self.statechglogger, self.logger,
                                            "conf", pwqfile, tmpfile, data,
                                            "present", "openeq")
            if not self.pwqeditor.report():
                compliant = False
                self.detailedresults += "Not all correct contents were found in " + pwqfile + "\n"
        else:
            compliant = False
            self.detailedresults += "System is using pwquality and crucial file /etc/security/pwquality.conf doesn't exist\n"
        return compliant

    def checklogindefs(self):
        """
        Method to check the password hash algorithm settings in
        login.defs

        :return: compliant
        :rtype: bool
        """
        compliant = True
        if os.path.exists(self.logindefs):
            if not checkPerms(self.logindefs, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions incorrect for " + \
                    self.logindefs + " file\n"
                compliant = False

        data = {"MD5_CRYPT_ENAB": "no",
                "ENCRYPT_METHOD": "SHA512",
                "PASS_MAX_DAYS": "180",
                "PASS_MIN_DAYS": "1",
                "PASS_WARN_AGE": "7",
                "FAIL_DELAY": "4"}
        tmppath = self.logindefs + ".stonixtmp"
        self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                      "conf", self.logindefs, tmppath,
                                      data, "present", "space")
        if not self.editor2.report():
            debug = self.logindefs + " doesn't contain the correct " + \
                "contents\n"
            self.detailedresults += self.logindefs + " doesn't contain " + \
                "the correct contents\n"
            self.logger.log(LogPriority.DEBUG, debug)
            compliant = False
        return compliant

    def checklibuser(self):
        """
        Private method to check the password hash algorithm settings in
        libuser.conf

        @author: Derek Walker

        :return: compliant
        :rtype: bool
        """
        compliant = True
        # """check if libuser is intalled"""
        if not self.ph.check("libuser"):
            # """if not, check if available"""
            if self.ph.checkAvailable("libuser"):
                self.detailedresults += "libuser available but not installed\n"
                return False
            else:
                # """not available, not a problem"""
                return True
        # """create a kveditor for file if it exists, if not, we do it in
        # the setlibuser method inside the fix"""
        if os.path.exists(self.libuserfile):
            data = {"defaults": {"crypt_style": "sha512"}}
            datatype = "tagconf"
            intent = "present"
            tmppath = self.libuserfile + ".stonixtmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          datatype, self.libuserfile,
                                          tmppath, data, intent, "openeq")
            if not self.editor1.report():
                debug = "/etc/libuser.conf doesn't contain the correct contents\n"
                self.detailedresults += "/etc/libuser.conf doesn't contain the correct contents\n"
                self.logger.log(LogPriority.DEBUG, debug)
                compliant = False
            if not checkPerms(self.libuserfile, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions are incorrect on " + self.libuserfile + "\n"
                compliant = False
        else:
            self.detailedresults += "Libuser installed but libuser file doesn't exist\n"
            compliant = False
        return compliant

    def setpwquality(self):
        """

        :return:
        """
        success = True
        created = False
        pwqfile = "/etc/security/pwquality.conf"
        if not os.path.exists(pwqfile):
            createFile(pwqfile, self.logger)
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'creation',
                     'filepath': pwqfile}
            self.statechglogger.recordchgevent(myid, event)
            created = True
        tmpfile = pwqfile + ".stonixtmp"
        if self.environ.getsystemfismacat() == "high":
            data = {"difok": "7",
                    "minlen": "14",
                    "dcredit": "0",
                    "ucredit": "0",
                    "lcredit": "0",
                    "ocredit": "0",
                    "maxrepeat": "3",
                    "minclass": "4"}
        else:
            data = {"difok": "7",
                    "minlen": "8",
                    "dcredit": "0",
                    "ucredit": "0",
                    "lcredit": "0",
                    "ocredit": "0",
                    "maxrepeat": "3",
                    "minclass": "3"}
        self.pwqeditor = KVEditorStonix(self.statechglogger, self.logger,
                                        "conf", pwqfile, tmpfile, data,
                                        "present", "openeq")
        self.pwqeditor.report()
        if self.pwqeditor.fixables:
            if self.pwqeditor.fix():
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.pwqeditor.setEventID(myid)
                if not self.pwqeditor.commit():
                    success = False
                    self.detailedresults += "Unable to correct " + pwqfile + "\n"
            else:
                success = False
                self.detailedresults += "Unable to correct " + pwqfile + "\n"
        return success

    def setlibuser(self):
        """Method to check if libuser is installed and the contents of libuser
        file.
        @author: Derek Walker


        :return: bool

        """
        created = False
        success = True
        data = {"defaults": {"crypt_style": "sha512"}}
        # """check if installed"""
        if not self.ph.check("libuser"):
            # """if not installed, check if available"""
            if self.ph.checkAvailable("libuser"):
                # """if available, install it"""
                if not self.ph.install("libuser"):
                    self.detailedresults += "Unable to install libuser\n"
                    return False
                else:
                    # """since we're just now installing it we know we now
                    # need to create the kveditor"""
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    comm = self.ph.getRemove() + "libuser"
                    event = {"eventtype": "commandstring",
                             "command": comm}
                    self.statechglogger.recordchgevent(myid, event)
                    datatype = "tagconf"
                    intent = "present"
                    tmppath = self.libuserfile + ".stonixtmp"
                    self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                                  datatype, self.libuserfile,
                                                  tmppath, data, intent, "openeq")
                    self.editor1.report()
            else:
                return True
        if not os.path.exists(self.libuserfile):
            if not createFile(self.libuserfile, self.logger):
                self.detailedresults += "Unable to create libuser file\n"
                debug = "Unable to create the libuser file\n"
                self.logger.log(LogPriority.DEBUG, debug)
                return False
            created = True
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation",
                     "filepath": self.libuserfile}
            self.statechglogger.recordchgevent(myid, event)
            tmppath = self.libuserfile + ".stonixtmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "tagconf", self.libuserfile,
                                          tmppath, data, "present", "openeq")
            self.editor1.report()
        if not checkPerms(self.libuserfile, [0, 0, 0o644], self.logger):
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(self.libuserfile, [0, 0, 0o644], self.logger,
                                self.statechglogger, myid):
                    success = False
                    self.detailedresults += "Unable to set the permissions on " + self.libuserfile + "\n"
            elif not setPerms(self.libuserfile, [0, 0, 0o644], self.logger):
                success = False
                self.detailedresults += "Unable to set the permissions on " + self.libuserfile + "\n"
        if self.editor1.fixables:
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor1.setEventID(myid)
            if self.editor1.fix():
                if self.editor1.commit():
                    debug = "/etc/libuser.conf has been corrected\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    os.chown(self.libuserfile, 0, 0)
                    os.chmod(self.libuserfile, 0o644)
                    resetsecon(self.libuserfile)
                else:
                    self.detailedresults += "/etc/libuser.conf couldn't be corrected\n"
                    success = False
            else:
                self.detailedresults += "/etc/libuser.conf couldn't be corrected\n"
                success = False
        return success

    def setlogindefs(self):
        """
        configure login.defs options

        :return: success
        :rtype: bool
        """

        success = True
        if not checkPerms(self.logindefs, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.logindefs, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                self.detailedresults += "Unable to set permissions on " + self.logindefs + " file\n"
                success = False

        if self.editor2:
            if self.editor2.fixables:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor2.setEventID(myid)
                if self.editor2.fix():
                    if self.editor2.commit():
                        debug = "/etc/login.defs file has been corrected\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        os.chown(self.logindefs, 0, 0)
                        os.chmod(self.logindefs, 0o644)
                        resetsecon(self.logindefs)
                    else:
                        debug = "Unable to correct the contents of /etc/login.defs\n"
                        self.detailedresults += "Unable to correct the contents of /etc/login.defs\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                else:
                    self.detailedresults += "Unable to correct the contents of /etc/login.defs\n"
                    debug = "Unable to correct the contents of /etc/login.defs\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False

        return success
Example #3
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)
Example #4
0
class AuditFirefoxUsage(Rule):
    """
    This module will audit firefox browser usage to determine
    if any site not listed in the APPROVEDDOMAINS CI has been visited
    while running in root mode

    """
    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.logger = logger
        self.rulenumber = 84
        self.rulename = "AuditFirefoxUsage"
        self.mandatory = True
        self.rootrequired = True
        self.sethelptext()
        self.formatDetailedResults("initialize")
        self.guidance = []
        self.applicable = {'type': 'white', 'family': ['linux']}
        # Configuration item instantiation
        datatype = 'list'
        key = 'APPROVEDDOMAINS'
        instructions = """This is a list of domains which the root user is \
approved to browse."""
        default = LOCALDOMAINS
        if default == None:
            default = ["localhost"]
        elif not default:
            default = ["localhost"]
        self.approvedDomainsCi = self.initCi(datatype, key, instructions,
                                             default)

        datatype = 'bool'
        key = 'DISABLEPROXY'
        instructions = """To disable Firefox's proxy settings, ensuring that \
browsing is limited to local domains in a proxied environment, set \
DISABLEPROXY to True."""
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.ph = Pkghelper(self.logger, self.environ)
        self.auditonly = True

    def report(self):
        """
        report the status of the rule's compliance with root-enabled firefox browsing

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

        """

        # UPDATE THIS SECTION IF YOU CHANGE THE CONSTANTS BEING USED IN THE RULE
        constlist = [LOCALDOMAINS]
        if not self.checkConsts(constlist):
            self.compliant = False
            self.detailedresults = "\nPlease ensure that the constant: LOCALDOMAINS, 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

        try:
            self.detailedresults = ""
            compliant = True
            ffDirs = self.getFirefoxDirs()
            urls = []
            package = ""

            if self.ph.checkAvailable("sqlite3"):
                package = "sqlite3"
            elif self.ph.checkAvailable("sqlite"):
                package = "sqlite"
            if not self.ph.check(package):
                self.ph.install(package)

            for ffDir in ffDirs:
                placesPath = ffDir + "/places.sqlite"
                if not os.path.exists(placesPath):
                    continue
                ch = CommandHelper(self.logger)
                command = ["/usr/bin/sqlite3", placesPath, ".tables"]
                ch.executeCommand(command)
                if not ch.getReturnCode() == 0:
                    # The version of sqlite3 on RHEL 6 cannot read these dbs.
                    # Instead, we will examine the file manually, looking for
                    # entries beginning with %\x08.
                    sqlBin = open(placesPath, "rb").read()
                    urlList = re.findall("%\x08https?://.*?/", sqlBin)
                    for url in urlList:
                        urls.append(url[2:])
                else:
                    conn = sqlite3.connect(placesPath)
                    c = conn.cursor()
                    c.execute("SELECT host FROM moz_hosts")
                    results = c.fetchall()
                    debug = "Results of 'SELECT host FROM moz_hosts': " + \
                        str(results)
                    self.logger.log(LogPriority.DEBUG, debug)
                    # Results are inside of a tuple
                    for item in results:
                        urls.append(item[0])
            badUrls = []
            urls = list(set(urls))
            if urls:
                self.logger.log(LogPriority.DEBUG, "URLs found: " + str(urls))
                for url in urls:
                    approved = self.approvedDomainsCi.getcurrvalue()
                    foundApproved = False
                    for site in approved:
                        if re.search(site, url):
                            foundApproved = True
                    if not foundApproved:
                        compliant = False
                        badUrls.append(url)
                if badUrls:
                    self.detailedresults = "URLs found in root's " + \
                        "Firefox history that are not on the approved " + \
                        "list: " + ", ".join(badUrls) + "\n"
            self.logger.log(LogPriority.DEBUG,
                            "Bad URLs found: " + str(badUrls))
            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 getFirefoxDirs(self):
        """Discover the randomly-generated Firefox profile directory(ies) for the
        root user.

        :returns: ffDirs - List of Firefox profile directories on the system

        """
        # It's possible to have several FF profiles for each user account. This
        # method will therefore return a list.
        ffDirs = []
        homeDir = "/root"
        ffParent = homeDir + "/.mozilla/firefox"
        if os.path.exists(ffParent):
            profileDirs = glob(ffParent + "/*.default")
            debug = "Found the following Firefox profile directories: " + \
                str(profileDirs)
            self.logger.log(LogPriority.DEBUG, debug)
            for pDir in profileDirs:
                # Since we gave glob the full path, the returned list will
                # have the full path for each entry
                ffDirs.append(pDir)
        return ffDirs
Example #5
0
class ConfigureKerberos(Rule):
    '''@author: Ekkehard J. Koch'''

    def __init__(self, config, environ, logdispatcher, statechglogger):
        """

        @param config:
        @param environ:
        @param logdispatcher:
        @param statechglogger:
        """

        Rule.__init__(self, config, environ, logdispatcher,
                              statechglogger)
        self.rulenumber = 255
        self.rulename = 'ConfigureKerberos'
        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']}}
        # This if/else statement fixes a bug in Configure Kerberos that
        # occurs on Debian systems due to the fact that Debian has no wheel
        # group by default.
        if self.environ.getosfamily() == 'darwin':
            self.files = {"krb5.conf":
                          {"path": "/etc/krb5.conf",
                           "remove": False,
                           "content": MACKRB5,
                           "permissions": 0o644,
                           "owner": os.getuid(),
                           "group": "wheel",
                           "eventid": str(self.rulenumber).zfill(4) + "krb5"},
                          "edu.mit.Kerberos":
                          {"path": "/Library/Preferences/edu.mit.Kerberos",
                           "remove": True,
                           "content": None,
                           "permissions": None,
                           "owner": None,
                           "group": None,
                           "eventid": str(self.rulenumber).zfill(4) +
                           "Kerberos"},
                          "edu.mit.Kerberos.krb5kdc.launchd":
                          {"path": "/Library/Preferences/edu.mit.Kerberos.krb5kdc.launchd",
                           "remove": True,
                           "content": None,
                           "permissions": None,
                           "owner": None,
                           "group": None,
                           "eventid": str(self.rulenumber).zfill(4) +
                           "krb5kdc"},
                          "kerb5.conf":
                          {"path": "/etc/kerb5.conf",
                           "remove": True,
                           "content": None,
                           "permissions": None,
                           "owner": None,
                           "group": None,
                           "eventid": str(self.rulenumber).zfill(4) + "kerb5"},
                          "edu.mit.Kerberos.kadmind.launchd":
                          {"path": "/Library/Preferences/edu.mit.Kerberos.kadmind.launchd",
                           "remove": True,
                           "content": None,
                           "permissions": None,
                           "owner": None,
                           "group": None,
                           "eventid": str(self.rulenumber).zfill(4) +
                           "kadmind"},
                          }
        else:
            self.files = {"krb5.conf":
                          {"path": "/etc/krb5.conf",
                           "remove": False,
                           "content": LINUXKRB5,
                           "permissions": 0o644,
                           "owner": "root",
                           "group": "root",
                           "eventid": str(self.rulenumber).zfill(4) + "krb5"}}
        self.ch = CommandHelper(self.logdispatch)
        self.fh = FileHelper(self.logdispatch, self.statechglogger)
        if self.environ.getosfamily() == 'linux':
                self.ph = Pkghelper(self.logdispatch, self.environ)
        self.filepathToConfigure = []
        for filelabel, fileinfo in sorted(self.files.items()):
            if fileinfo["remove"]:
                msg = "Remove if present " + str(fileinfo["path"])
            else:
                msg = "Add or update if needed " + str(fileinfo["path"])
            self.filepathToConfigure.append(msg)
            self.fh.addFile(filelabel,
                            fileinfo["path"],
                            fileinfo["remove"],
                            fileinfo["content"],
                            fileinfo["permissions"],
                            fileinfo["owner"],
                            fileinfo["group"],
                            fileinfo["eventid"]
                            )
        # Configuration item instantiation
        datatype = "bool"
        key = "CONFIGUREFILES"
        instructions = "When Enabled will fix these files: " + \
            str(self.filepathToConfigure)
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

    def report(self):
        '''run report actions for configure kerberos
        determine compliance status of the current system
        return True if compliant, False if non-compliant


        :returns: self.compliant

        :rtype: bool
@author: ???
@change: Breen Malmberg - 2/23/2017 - added doc string; added const checks preamble to report and fix methods

        '''

        self.compliant = True
        self.detailedresults = ""

        # UPDATE THIS SECTION IF YOU CHANGE THE CONSTANTS BEING USED IN THE RULE
        constlist = [MACKRB5, LINUXKRB5]
        if not self.checkConsts(constlist):
            self.compliant = False
            self.detailedresults = "\nPlease ensure that the constants: MACKRB5, LINUXKRB5, in localize.py, are defined and are not None. This rule will not function without them."
            self.formatDetailedResults("report", self.compliant, self.detailedresults)
            return self.compliant

        try:

            if self.environ.getosfamily() == 'linux':
                packagesRpm = ["pam_krb5", "krb5-libs", "krb5-workstation",
                               "sssd-krb5", "sssd-krb5-common"]
                packagesDeb = ["krb5-config", "krb5-user", "libpam-krb5"]
                packagesSuse = ["pam_krb5", "sssd-krb5", "sssd-krb5-common",
                                "krb5-client", "krb5"]
                if self.ph.determineMgr() == "apt-get":
                    self.packages = packagesDeb
                elif self.ph.determineMgr() == "zypper":
                    self.packages = packagesSuse
                else:
                    self.packages = packagesRpm
                for package in self.packages:
                    if not self.ph.check(package) and self.ph.checkAvailable(package):
                        self.compliant = False
                        self.detailedresults += package + " is not installed\n"
            if not self.fh.evaluateFiles():
                self.compliant = False
                self.detailedresults += self.fh.getFileMessage()

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            self.detailedresults += 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):
        '''run fix actions


        :returns: self.rulesuccess

        :rtype: bool
@author: ???
@change: Breen Malmberg - 2/23/2017 - added doc string; added checkconsts preamble to ensure
        the rule does not attempt to run without requied information (from localize.py)

        '''

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

        # UPDATE THIS SECTION IF YOU CHANGE THE CONSTANTS BEING USED IN THE RULE
        constlist = [MACKRB5, LINUXKRB5]
        if not self.checkConsts(constlist):
            fixsuccess = False
            self.formatDetailedResults("fix", fixsuccess, self.detailedresults)
            return fixsuccess

        try:

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

            if self.ci.getcurrvalue():
                pkgsToInstall = []
                if self.environ.getosfamily() == 'linux':
                    for package in self.packages:
                        if not self.ph.check(package):
                            if self.ph.checkAvailable(package):
                                pkgsToInstall.append(package)
                    for package in pkgsToInstall:
                        if self.ph.install(package):
                            self.iditerator += 1
                            myid = iterate(self.iditerator,
                                           self.rulenumber)
                            event = {"eventtype": "pkghelper",
                                     "pkgname": package,
                                     "startstate": "removed",
                                     "endstate": "installed"}
                            self.statechglogger.recordchgevent(myid, event)
                        else:
                            self.rulesuccess = False
                            self.detailedresults += "Installation of " + package + " did not succeed.\n"
                if not self.fh.fixFiles():
                    self.rulesuccess = False
                    self.detailedresults += self.fh.getFileMessage()
            else:
                self.rulesuccess = False
                self.detailedresults = str(self.ci.getkey()) + " was disabled. No action was taken!"

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += 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
Example #6
0
class ConfigureAIDE(Rule):
    """Install and configure Advanced Intrusion Detection Environment (AIDE).
This rule is optional and will install and configure AIDE when it is run."""
    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.logger = logger
        self.rulenumber = 110
        self.rulename = 'ConfigureAIDE'
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.guidance = ['NSA(2.1.3)', 'cce-4209-3']

        # init CIs
        datatype = 'bool'
        key = 'CONFIGUREAIDE'
        instructions = 'If you set the ConfigureAIDE variable to yes, or ' + \
            'true, ConfigureAIDE will install and set up the Advanced ' + \
            'Intrusion Detection Environment on this system.'
        default = False
        self.applicable = {'type': 'white', 'family': ['linux']}
        self.ci = self.initCi(datatype, key, instructions, default)

        datatype2 = 'string'
        key2 = 'AIDEJOBTIME'
        instructions2 = """This string contains the time when the cron job for
        /usr/sbin/aide --check will run in /etc/crontab. The default value is
        00 18 * * 07 (which means weekly, on sundays at 6pm)"""
        default2 = "00 18 * * 07"
        self.aidetime = self.initCi(datatype2, key2, instructions2, default2)
        pattern = "^([0-9]{1,2})\s+([0-9]{1,2})\s+(\*|(3[0-1]|[0-2]?[0-9]))\s+(\*|(0[0-9]|[0-1]?[0-2]))\s+(\*|([0]?[0-7]))$"
        self.aidetime.setregexpattern(pattern)

    def report(self):
        """Check if AIDE is installed and properly configured.
        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.

        :return: self.compliant
        :rtype: bool

        """

        try:

            self.compliant = True
            self.detailedresults = ""

            self.init_objs()

            try:
                self.set_aide_paths()
                self.set_aide_cron_job()
            except:
                pass

            if not self.report_aide_installed():
                self.compliant = False
            else:
                if not self.report_aide_conf():
                    self.compliant = False
                if not self.report_aide_cronjob():
                    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.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.compliant

    def init_objs(self):
        """

        """

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

    def set_aide_paths(self):
        """

        """

        self.aide_conf_file = ""
        aide_conf_paths = ["/etc/aide/aide.conf", "/etc/aide.conf"]
        for p in aide_conf_paths:
            if os.path.isfile(p):
                self.aide_conf_file = p
                break
        self.aide_package = "aide"
        self.aide_cron_file = "/etc/cron.d/stonix_aide_check"
        self.aide_bin_path = ""
        aide_bin_paths = ["/usr/sbin/aide", "/usr/bin/aide"]
        for p in aide_bin_paths:
            if os.path.isfile(p):
                self.aide_bin_path = p
                break
        self.crontab_bin = ""
        crontab_bin_locs = ["/bin/crontab", "/sbin/crontab"]
        for b in crontab_bin_locs:
            if os.path.isfile(b):
                self.crontab_bin = b
                break

    def set_aide_cron_job(self):
        """

        """

        aidetime = self.aidetime.getcurrvalue()

        if not aidetime:
            self.aide_cron_job = ""
        else:

            if self.ph.manager == "apt-get":

                self.aide_cron_job = str(
                    self.aidetime.getcurrvalue()
                ) + " root nice -n 19 " + self.aide_bin_path + " -c " + self.aide_conf_file + " --check"
            else:
                self.aide_cron_job = str(self.aidetime.getcurrvalue(
                )) + " root nice -n 19 " + self.aide_bin_path + " --check"

    def report_aide_installed(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        if not self.ph.check(self.aide_package):
            compliant = False
            self.detailedresults += "\naide is not installed"

        return compliant

    def report_aide_conf(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        aide_conf_dict = {
            "/boot": "R",
            "/etc": "R",
            "/lib": "R",
            "/lib64": "R",
            "/sbin$": "R"
        }

        tmppath = self.aide_conf_file + ".stonixtmp"
        self.aide_conf_editor = KVEditorStonix(self.statechglogger,
                                               self.logger, "conf",
                                               self.aide_conf_file, tmppath,
                                               aide_conf_dict, "present",
                                               "space")
        if not self.aide_conf_editor.report():
            compliant = False
            self.detailedresults += "\nThe following aide conf file options are incorrect:\n" + "\n".join(
                self.aide_conf_editor.fixables)

        return compliant

    def report_aide_cronjob(self):
        """

        :return: compliant
        :rtype: bool

        """

        compliant = True

        if not os.path.isfile(self.aide_cron_file):
            compliant = False
            self.detailedresults += "\naide cron job not found"

        return compliant

    def fix(self):
        """Attempt to install and configure AIDE.
        self.rulesuccess will be updated if the rule does not succeed.

        :return: self.rulesuccess - True if fix succeeded; False if not
        :rtype: bool

        """

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

        try:

            if not self.ci.getcurrvalue():
                self.detailedresults += '\nThis rule is currently not enabled, so nothing was done'
                self.formatDetailedResults("fix", self.rulesuccess,
                                           self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.rulesuccess

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

            if not self.fix_aide_installed():
                self.rulesuccess = False
            if not self.fix_aide_conf():
                self.rulesuccess = False
            if not self.fix_aide_init():
                self.rulesuccess = False
            if not self.fix_aide_cronjob():
                self.rulesuccess = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            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 fix_aide_conf(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        self.logger.log(LogPriority.DEBUG, "Fixing aide configuration file")

        if not self.aide_conf_editor.fix():
            success = False
        elif not self.aide_conf_editor.commit():
            success = False

        if not success:
            self.logger.log(LogPriority.DEBUG, "Failed to configure aide.conf")

        return success

    def fix_aide_cronjob(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        self.logger.log(LogPriority.DEBUG, "Creating aide cron job")

        try:

            f = open(self.aide_cron_file, "w")
            f.write(self.aide_cron_job)
            f.close()
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation", "filepath": self.aide_cron_file}
            self.statechglogger.recordchgevent(myid, event)

        except:
            success = False
            self.logger.log(LogPriority.DEBUG,
                            "Failed to create aide cron job")

        return success

    def fix_aide_installed(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        self.logger.log(LogPriority.DEBUG, "Installing aide package")

        if not self.ph.install(self.aide_package):
            success = False
            self.logger.log(LogPriority.DEBUG,
                            "Failed to install aide package")
            if not self.ph.checkAvailable(self.aide_package):
                self.logger.log(LogPriority.DEBUG,
                                "aide package not available on this system")
        else:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {
                "eventtype": "pkghelper",
                "pkgname": self.aide_package,
                "startstate": "removed",
                "endstate": "installed"
            }
            self.statechglogger.recordchgevent(myid, event)
            self.set_aide_paths()
            self.set_aide_cron_job()
            self.report_aide_conf()
            self.report_aide_cronjob()

        return success

    def fix_aide_init(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        self.logger.log(LogPriority.DEBUG, "Initializing aide database")

        if self.ph.manager == "apt-get":
            aide_init_cmd = "aideinit"
        else:
            aide_init_cmd = self.aide_bin_path + " --init"

        self.ch.executeCommand(aide_init_cmd)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)
        else:
            # to start using the aide database created by aide init,
            # remove the 'new' part of the aide database string
            # this must be done before check can be run
            # (https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-using-aide)
            if os.path.isfile('/var/lib/aide/aide.db.new.gz'):
                newaidedb = '/var/lib/aide/aide.db.new.gz'
                aidedb = '/var/lib/aide/aide.db.gz'
            elif os.path.isfile('/var/lib/aide/aide.db.new'):
                newaidedb = '/var/lib/aide/aide.db.new'
                aidedb = '/var/lib/aide/aide.db'
            else:
                newaidedb = ""
                aidedb = ""
            if bool(newaidedb and aidedb):
                os.rename(newaidedb, aidedb)
                os.chmod(aidedb, 0o600)
                os.chown(aidedb, 0, 0)
            else:
                self.logger.log(LogPriority.DEBUG,
                                "Failed to locate aide database")
                success = False

        return success
Example #7
0
class SSHTimeout(Rule):
    '''This rule will configure the ssh timeout period for
    ssh sessions, if ssh is installed.
    
    @author: dwalker


    '''
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rootrequired = True
        self.rulenumber = 127
        self.rulename = 'SSHTimeout'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.boolCi = self.initCi(
            "bool", "SSHTIMEOUTON",
            "To disable this rule set the value " + "of SSHTIMEOUTON to False",
            True)
        self.intCi = self.initCi(
            "int", "SSHTIMEOUT", "Set your preferred timeout value here, " +
            "in seconds. Default is 900 (15 minutes).", 900)
        self.guidance = ['NSA 3.5.2.3']
        self.iditerator = 0
        self.editor = ""
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.ph = Pkghelper(self.logger, self.environ)

    def report(self):
        '''SSHTimeout.report(): produce a report on whether or not a valid
        time for timing out of ssh is set.
        @author: D.Walker


        '''

        try:
            self.detailedresults = ""
            compliant = True
            results = ""
            timeout = self.intCi.getcurrvalue()
            if self.environ.getostype() == "Mac OS X":
                self.path = '/private/etc/ssh/sshd_config'
                self.tpath = '/private/etc/ssh/sshd_config.tmp'
            else:
                self.path = '/etc/ssh/sshd_config'
                self.tpath = '/etc/ssh/sshd_config.tmp'

                if self.ph.manager == "zypper":
                    openssh = "openssh"
                else:
                    openssh = "openssh-server"

                if not self.ph.check(openssh):
                    self.compliant = True
                    self.detailedresults += "Package " + openssh + " is not installed.\nNothing to configure."
                    self.formatDetailedResults("report", self.compliant,
                                               self.detailedresults)
                    return self.compliant

            self.ssh = {
                "ClientAliveInterval": str(timeout),
                "ClientAliveCountMax": "0"
            }
            if os.path.exists(self.path):
                compliant = True
                kvtype = "conf"
                intent = "present"
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             kvtype, self.path, self.tpath,
                                             self.ssh, intent, "space")
                if not self.editor.report():
                    compliant = False
                    results += "Settings in " + self.path + " are not " + \
                        "correct\n"
                if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                    compliant = False
                    results += self.path + " permissions are incorrect\n"
            else:
                compliant = False
                results += self.path + " does not exist\n"

            self.detailedresults = results
            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):
        '''SSHTimeout.fix(): set the correct values in /etc/ssh/sshd_config
        so that ssh sessions time out appropriately.
        @author: D.Walker


        '''

        try:
            if not self.boolCi.getcurrvalue():
                return
            debug = "inside fix method\n"
            self.logger.log(LogPriority.DEBUG, debug)
            created = False
            self.iditerator = 0
            success = True
            self.detailedresults = ""
            debug = ""
            # 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.getostype() != "Mac OS X":
                if self.ph.manager == "zypper":
                    openssh = "openssh"
                else:
                    openssh = "openssh-server"
                if not self.ph.check(openssh):
                    debug = "openssh-server is not installed in fix\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    if self.ph.checkAvailable(openssh):
                        debug = "openssh-server is not available in fix\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        if not self.ph.install(openssh):
                            debug = "Unable to install openssh-server\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            self.rulesuccess = False
                            return
                        else:
                            cmd = self.ph.getRemove() + openssh
                            event = {
                                "eventtype": "commandstring",
                                "command": cmd
                            }
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.statechglogger.recordchgevent(myid, event)
                            self.detailedresults += "Installed openssh-server\n"
                            self.editor = KVEditorStonix(
                                self.statechglogger, self.logger, "conf",
                                self.path, self.tpath, self.ssh, "present",
                                "space")
                            self.editor.report()
                    else:
                        debug += "openssh-server not available to install\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        self.rulesuccess = False
                        return
            if not os.path.exists(self.path):
                createFile(self.path, self.logger)
                created = True
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": self.path}
                self.statechglogger.recordchgevent(myid, event)
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             "conf", self.path, self.tpath,
                                             self.ssh, "present", "space")
                self.editor.report()

            if os.path.exists(self.path):
                print("path exists\n")
                if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(self.path, [0, 0, 0o644], self.logger,
                                        self.statechglogger, myid):
                            debug += "Unable to set Permissions \
    for: " + self.editor.getPath() + "\n"
                            success = False
                    else:
                        if not setPerms(self.path, [0, 0, 0o644], self.logger):
                            success = False

                if self.editor.fixables:
                    print(("editor has fixables and they are " +
                           str(self.editor.fixables) + "\n"))
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor.setEventID(myid)
                    if self.editor.fix():
                        debug += "kveditor fix ran successfully\n"
                        if self.editor.commit():
                            debug += "kveditor commit ran successfully\n"

                            os.chown(self.path, 0, 0)
                            os.chmod(self.path, 0o644)
                            if re.search("linux", self.environ.getosfamily()):
                                resetsecon(self.path)

                        else:
                            debug += "Unable to complete kveditor commit\n"
                            success = False
                    else:
                        debug += "Unable to complete kveditor fix\n"
                        success = False

                self.rulesuccess = success
            if debug:
                self.logger.log(LogPriority.DEBUG, debug)
        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
Example #8
0
class PasswordExpiration(Rule):

    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 42
        self.rulename = "PasswordExpiration"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.iditerator = 0
        self.guidance = ["2.3.1.7"]
        self.applicable = {'type': 'black', 'family': ['darwin']}
        self.universal = "#The following lines were added by stonix\n"
        datatype = 'bool'
        key = 'PASSWORDEXPIRATION'
        instructions = "To disable this rule set the value of " + \
            "PASSWORDEXPIRATION to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.libusercreate = False
        self.libuserinstall = False
        self.useraddcreate = False
        self.logindefcreate = False
        self.fixable, self.shadow = True, True
        self.editor1, self.editor2 = "", ""
        self.fixusers = []

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

    def report(self):
        try:
            self.detailedresults = ""
            self.ch = CommandHelper(self.logger)
            self.lockedpwds = '^\*LK\*|^!|^\*|^x$'
            if self.environ.getosfamily() == "linux":
                self.ph = Pkghelper(self.logger, self.environ)
                self.specs = {"PASS_MAX_DAYS": "180",
                              "PASS_MIN_DAYS": "1",
                              "PASS_MIN_LEN": "8",
                              "PASS_WARN_AGE": "28"}
                if self.ph.manager in ("apt-get", "zypper"):
                    # apt-get systems do not set min length in the same file
                    # as other systems(login.defs)
                    del self.specs["PASS_MIN_LEN"]

                self.shadowfile = "/etc/shadow"
                self.logdeffile = "/etc/login.defs"
                self.useraddfile = "/etc/default/useradd"
                self.libuserfile = "/etc/libuser.conf"
                self.compliant = self.reportLinux()
            elif self.environ.getosfamily() == "solaris":
                self.specs = {"PASSLENGTH": "8",
                              "MINWEEKS": "1",
                              "MAXWEEKS": "26",
                              "WARNWEEKS": "4"}
                self.shadowfile = "/etc/shadow"
                self.logdeffile = "/etc/default/passwd"
                self.compliant = self.reportSolaris()
            elif self.environ.getosfamily() == "freebsd":
                self.specs = {"warnpassword": "******",
                              "minpasswordlen": "8",
                              "passwordtime": "180d"}
                self.shadowfile = "/etc/master.passwd"
                self.loginfile = "/etc/login.conf"
                self.compliant = self.reportFreebsd()
        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 reportLinux(self):
        compliant1 = self.checklogindefs()
        compliant2 = self.chkShadow()
        compliant3 = self.chkUserAdd()
        compliant4 = self.checklibuser()
        if compliant1 and compliant2 and compliant3 and compliant4:
            return True
        else:
            return False

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

    def reportSolaris(self):
        compliant1 = self.checklogindefs()
        compliant2 = self.chkShadow()
        if compliant1 and compliant2:
            return True
        else:
            return False

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

    def reportFreebsd(self, specs):
        compliant1 = self.chkPasswd()
        compliant2 = self.chkLogin(specs)
        if compliant1 and compliant2:
            return True
        else:
            return False

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

    def checklogindefs(self):
        '''report method for various distros of linux and solaris'''
        compliant = True
        debug = ""
        if not os.path.exists(self.logdeffile):
            compliant = False
            self.detailedresults += self.logdeffile + " file does not exist\n"
        elif not checkPerms(self.logdeffile, [0, 0, 0o644], self.logger):
            compliant = False
            self.detailedresults += self.logdeffile + " does not have " + \
                "the correct permissions. Expected 644, found " + \
                str(getOctalPerms(self.logdeffile)) + ".\n"
        tmpfile = self.logdeffile + ".tmp"
        self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                      "conf", self.logdeffile, tmpfile,
                                      self.specs, "present", "space")
        if not self.editor1.report():
            self.detailedresults += self.logdeffile + " does not " + \
                "contain the correct contents\n"
            debug = self.logdeffile + " doesn't contain the correct " + \
                "contents\n"
            self.logger.log(LogPriority.DEBUG, debug)
            compliant = False

        return compliant

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

    def chkShadow(self):
        debug = ""
        compliant = True
        if os.path.exists(self.shadowfile):
            if self.ph.manager == "apt-get":
                statdata = os.stat(self.shadowfile)
                mode = stat.S_IMODE(statdata.st_mode)
                retval = getUserGroupName(self.shadowfile)
                if retval[0] != "root" or retval[1] != "shadow":
                    compliant = False
                    self.detailedresults += self.shadowfile + " ownership " + \
                        "is not correct (either owner is not root, or " + \
                        "group is not shadow).\n"
                if mode != 0o640:
                    compliant = False
                    self.detailedresults += self.shadowfile + " does not have " + \
                        "the correct permissions. Expected 640, found " + \
                        str(getOctalPerms(self.shadowfile)) + ".\n"
            elif not checkPerms(self.shadowfile, [0, 0, 0o400], self.logger) and \
                 not checkPerms(self.shadowfile, [0, 0, 0], self.logger):
                compliant = False
                self.detailedresults += self.shadowfile + " does not have " + \
                    "the correct permissions. Expected 400 or 0, found " + \
                    str(getOctalPerms(self.shadowfile)) + ".\n"
            contents = readFile(self.shadowfile, self.logger)
            if self.environ.getosfamily() == "solaris" or \
               self.environ.getosfamily() == "linux":
                if self.environ.getosfamily() == "linux":
                    whichid = "/usr/bin/id"
                elif self.environ.getosfamily() == "solaris":
                    whichid = "/usr/xpg4/bin/id"
                for line in contents:
                    badacct = False
                    debug = ""
                    if re.search("^\#", line) or re.match("^\s*$", line):
                        continue
                    if re.search(":", line):
                        field = line.split(":")
                        cmd = [whichid, "-u", field[0]]
                        self.ch.executeCommand(cmd)
                        output = self.ch.getOutputString().strip()
                        error = self.ch.getError()
                        if error:
                            continue
                        if output:
                            if output.isdigit():
                                uid = int(output)
                            else:
                                uid = 100
                        else:
                            continue
                        try:
                            if uid >= 500 and not re.search(self.lockedpwds,
                                                            field[1]):
                                for i in [3, 4, 5, 6]:
                                    if field[i]:
                                        val = field[i]
                                        if val.isdigit():
                                            field[i] = int(field[i])
                                        elif i == 6:
                                            field[i] = 99
                                        else:
                                            field[i] = 0
                                    elif i == 6:
                                        field[i] = 99
                                    else:
                                        field[i] = 0
                                if field[3] != 1 or field[3] == "":
                                    compliant = False
                                    self.detailedresults += "Shadow file: " + \
                                        "Minimum age is not equal to 1\n"
                                    badacct = True
                                if field[4] > 180 or field[4] == "":
                                    compliant = False
                                    self.detailedresults += "Shadow file: " + \
                                        "Expiration is not 180 or less\n"
                                    badacct = True
                                if field[5] != 28 or field[5] == "":
                                    compliant = False
                                    self.detailedresults += "Shadow file: " + \
                                        "Password expiration warnings are " + \
                                        "not set to 28 days\n"
                                    badacct = True
                                if field[6] != 35 or field[6] == "":
                                    compliant = False
                                    self.detailedresults += "Shadow file: " + \
                                        "Account lock is not set to 35 days\n"
                                    badacct = True
                        except IndexError:
                            compliant = False
                            debug = traceback.format_exc()
                            debug += ' Index out of range\n'
                            badacct = True
                        if debug:
                            self.logger.log(LogPriority.DEBUG, debug)
                    if badacct:
                        self.fixusers.append(field[0])
            if self.environ.getosfamily() == 'freebsd':
                for line in contents:
                    debug = ""
                    if re.search("^\#", line) or re.match('^\s*$', line):
                        continue
                    if re.search(':', line):
                        field = line.split(':')
                        message = Popen(['/usr/bin/id', '-u', field[0]],
                                        stderr=PIPE, stdout=PIPE, shell=False)
                        uid = message.stdout.readline()
                        uid = uid.strip()
                        message.stdout.close()
                        if uid.isdigit():
                            uid = int(uid)
                        else:
                            uid = 100
                        try:
                            if uid >= 500 and not re.search(self.lockedpwds,
                                                            field[1]):
                                for i in [5, 6]:
                                    if field[i]:
                                        val = field[i]
                                        if not val.isdigit():
                                            field[i] = 0
                                    else:
                                        field[i] = 0

                                if int(field[5]) > 180 or field[5] == "":
                                    self.shadow = False
                                    compliant = False
                                    debug += "expiration is not 180 or less"
                                if int(field[6]) != 1 or field[6] == "":
                                    self.shadow = False
                                    compliant = False
                                    debug += "Account lock is not set to 1"
                        except IndexError:
                            self.shadow = False
                            compliant = False
                            debug = traceback.format_exc()
                            debug += ' Index out of range'
                            self.logger.log(LogPriority.DEBUG, debug)
                        if debug:
                            self.logger.log(LogPriority.DEBUG, debug)
        else:
            self.detailedresults += self.shadowfile + " does not exist\n"
            compliant = False
        debug = "chkShadow method is returning " + str(compliant) + \
            " compliance\n"
        self.logger.log(LogPriority.DEBUG, debug)
        return compliant

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

    def chkUserAdd(self):
        compliant = True
        debug = ""
        if not os.path.exists(self.useraddfile):
            self.detailedresults += self.useraddfile + " file does not exist\n"
            compliant = False
        else:
            if not checkPerms(self.useraddfile, [0, 0, 0o600], self.logger):
                compliant = False
                self.detailedresults += self.useraddfile + " does not have " + \
                    "the correct permissions. Expected 600, found " + \
                    str(getOctalPerms(self.useraddfile)) + ".\n"
            contents = readFile(self.useraddfile, self.logger)
            found = False
            valcorrect = True
            for line in contents:
                if re.search("^\#", line) or re.match('^\s*$', line):
                    continue
                if re.search('^INACTIVE', line.strip()) and re.search('=',
                                                                      line):
                    found = True
                    temp = line.split('=')
                    if int(temp[1].strip()) <= -1 or int(temp[1].strip()) > 35:
                        valcorrect = False
                        break
            if not found:
                compliant = False
                self.detailedresults += "INACTIVE key was not found in " + \
                    self.useraddfile + "\n"
            if found and not valcorrect:
                compliant = False
                self.detailedresults += "INACTIVE key was found in " + \
                    self.useraddfile + ", but value is incorrect\n"
        debug += "chkUserAdd method is returning " + str(compliant) + \
            " compliance\n"
        if debug:
            self.logger.log(LogPriority.DEBUG, debug)
        return compliant

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

    def checklibuser(self):
        '''Private method to check the password hash algorithm settings in
        libuser.conf.
        @author: dwalker


        :returns: bool

        '''
        compliant = True
        '''check if libuser is intalled'''
        if not self.ph.check("libuser"):
            '''if not, check if available'''
            if self.ph.checkAvailable("libuser"):
                self.detailedresults += "libuser available but not installed\n"
                return False
            else:
                '''not available, not a problem'''
                return True
        '''create a kveditor for file if it exists, if not, we do it in
        the setlibuser method inside the fix'''
        if os.path.exists(self.libuserfile):
            data = {"userdefaults": {"LU_SHADOWMAX": "",
                                     "LU_SHADOWMIN": "",
                                     "LU_SHADOWWARNING": "",
                                     "LU_UIDNUMBER": "",
                                     "LU_SHADOWINACTIVE": "",
                                     "LU_SHADOWEXPIRE": ""}}
            datatype = "tagconf"
            intent = "notpresent"
            tmppath = self.libuserfile + ".tmp"
            self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                          datatype, self.libuserfile,
                                          tmppath, data, intent, "openeq")
            if not self.editor2.report():
                debug = "/etc/libuser.conf doesn't contain the correct " + \
                    "contents\n"
                self.detailedresults += "/etc/libuser.conf doesn't " + \
                    "contain the correct contents\n"
                self.logger.log(LogPriority.DEBUG, debug)
                compliant = False
            if not checkPerms(self.libuserfile, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions are incorrect on " + \
                    self.libuserfile + "\n"
                compliant = False
        else:
            self.detailedresults += "Libuser installed but libuser " + \
                "file doesn't exist\n"
            compliant = False
        return compliant


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

    def chkLogin(self):
        compliant = True
        if os.path.exists(self.loginfile):
            if not checkPerms(self.loginfile, [0, 0, 0o644], self.logger):
                compliant = False
                self.detailedresults += self.libuserfile + " does not have " + \
                    "the correct permissions. Expected 644, found " + \
                    str(getOctalPerms(self.libuserfile)) + ".\n"
            contents = readFile(self.loginfile, self.logger)
            iterator1 = 0
            for line in contents:
                if re.search("^#", line) or re.match('^\s*$', line):
                    iterator1 += 1
                elif re.search('^default:\\\\$', line.strip()):
                    found = True
                    temp = contents[iterator1 + 1:]
                    length2 = len(temp) - 1
                    iterator2 = 0
                    for line2 in temp:
                        if re.search('^[^:][^:]*:\\\\$', line2):
                            contents2 = temp[:iterator2]
                            break
                        elif iterator2 < length2:
                            iterator2 += 1
                        elif iterator2 == length2:
                            contents2 = temp[:iterator2]
                    break
                else:
                    iterator1 += 1
            if contents2:
                for key in self.Fspecs:
                    found = False
                    for line in contents2:
                        if re.search("^#", line) or re.match('^\s*$', line):
                            continue
                        elif re.search('^:' + key, line.strip()):
                            if re.search('=', line):
                                temp = line.split('=')
                                if re.search(str(self.Fspecs[key]) +
                                             '(:\\\\|:|\\\\|\s)',
                                             temp[1]):
                                    found = True
                                    continue
                                else:
                                    found = False
                                    break
                    if not found:
                        compliant = False
            return compliant
        else:
            self.detailedresults += self.loginfile + "does not exist. " + \
                "Please note that the fix for this rule will not attempt " + \
                "to create this file.\n"
            compliant = False
        debug = "chkLogin method is returning " + (compliant) + " compliance\n"
        self.logger.log(LogPriority.DEBUG, debug)
        return 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()
            if self.environ.getosfamily() == "solaris":
                self.rulesuccess = self.fixSolaris()
            if self.environ.getosfamily() == "freebsd":
                self.rulesuccess = self.fixFreebsd()
        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 fixLinux(self):
        success1 = self.fixLogDef(self.specs)
        success2 = self.fixShadow()
        success3 = self.fixUserAdd()
        success4 = self.setlibuser()
        if success1 and success2 and success3 and success4:
            return True
        else:
            return False

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

    def fixSolaris(self):
        success1 = self.fixLogDef()
        success2 = self.fixShadow()
        if success1 and success2:
            return True
        else:
            return False

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

    def fixFreebsd(self):
        success1 = self.fixPasswd()
        success2 = self.fixLogin()
        if success1 and success2:
            return True
        else:
            return False

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

    def fixLogDef(self, specs):
        success = True
        debug = ""
        if not os.path.exists(self.logdeffile):
            if createFile(self.logdeffile, self.logger):
                self.logindefcreate = True
                setPerms(self.logdeffile, [0, 0, 0o644], self.logger)
                tmpfile = self.logdeffile + ".tmp"
                self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.logdeffile, tmpfile,
                                              specs, "present", "space")
            else:
                self.detailedresults += "Was not able to create " + \
                    self.logdeffile + " file\n"
                success = False
        if self.logindefcreate:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation",
                     "filepath": self.logdeffile}
            self.statechglogger.recordchgevent(myid, event)
        elif not checkPerms(self.logdeffile, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.logdeffile, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                debug += "permissions not correct on: " + \
                    self.logdeffile + "\n"
                success = False
        if self.editor1.fixables or self.editor1.removeables:
            if not self.logindefcreate:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor1.setEventID(myid)
            if not self.editor1.fix():
                debug += "fixLogDef editor.fix did not complete successfully\n"
                success = False
            elif not self.editor1.commit():
                debug += "fixLogDef editor.commit did not complete successfully\n"
                success = False
            os.chown(self.logdeffile, 0, 0)
            os.chmod(self.logdeffile, 0o644)
            resetsecon(self.logdeffile)
        if debug:
            self.logger.log(LogPriority.DEBUG, debug)
        return success

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

    def fixShadow(self):
        success = True
        if not os.path.exists(self.shadowfile):
            self.detailedresults += self.shadowfile + "does not exist. \
Will not perform fix on shadow file\n"
            return False
        if self.fixusers:
            contents = readFile(self.shadowfile, self.logger)

            if self.ph.manager == "apt-get":
                perms = [0, 42, 0o640]
            else:
                perms = [0, 0, 0o400]
            if not checkPerms(self.shadowfile, perms, self.logger) and \
               not checkPerms(self.shadowfile, [0, 0, 0], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                setPerms(self.shadowfile, perms, self.logger,
                         self.statechglogger, myid)

            tmpdate = strftime("%Y%m%d")
            tmpdate = list(tmpdate)
            date = tmpdate[0] + tmpdate[1] + tmpdate[2] + tmpdate[3] + "-" + \
                tmpdate[4] + tmpdate[5] + "-" + tmpdate[6] + tmpdate[7]
            for user in self.fixusers:
                cmd = ["chage", "-d", date, "-m", "1", "-M", "180", "-W", "28",
                       "-I", "35", user]
                self.ch.executeCommand(cmd)

            # We have to do some gymnastics here, because chage writes directly
            # to /etc/shadow, but statechglogger expects the new contents to
            # be in a temp file.
            newContents = readFile(self.shadowfile, self.logger)
            shadowTmp = "/tmp/shadow.stonixtmp"
            createFile(shadowTmp, self.logger)
            writeFile(shadowTmp, "".join(newContents) + "\n", self.logger)
            writeFile(self.shadowfile, "".join(contents) + "\n", self.logger)
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'conf',
                     'filepath': self.shadowfile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.shadowfile, shadowTmp,
                                                 myid)
            shutil.move(shadowTmp, self.shadowfile)
            os.chmod(self.shadowfile, perms[2])
            os.chown(self.shadowfile, perms[0], perms[1])
            resetsecon(self.shadowfile)
        return success

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

    def fixUserAdd(self):
        success = True
        if not os.path.exists(self.useraddfile):
            if createFile(self.useraddfile, self.logger):
                self.useraddcreate = True
                setPerms(self.useraddfile, [0, 0, 0o600], self.logger)
            else:
                self.detailedresults += self.useraddfile + \
                    " could not be created\n"
                success = False
        if self.useraddcreate:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation",
                     "filepath": self.useraddfile}
            self.statechglogger.recordchgevent(myid, event)

        if not checkPerms(self.useraddfile, [0, 0, 0o600], self.logger):
            if not self.useraddcreate:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(self.useraddfile, [0, 0, 0o600],
                                self.logger, self.statechglogger, myid):
                    self.detailedresults += "Could not set permissions on " + \
                        self.useraddfile
                    success = False
        tempstring = ""
        contents = readFile(self.useraddfile, self.logger)
        found = False
        for line in contents:
            if re.search("^\#", line) or re.match('^\s*$', line):
                tempstring += line
                continue
            if re.search("^INACTIVE", line.strip()):
                if re.search("=", line):
                    temp = line.split("=")
                    if int(temp[1].strip()) <= -1 or \
                       int(temp[1].strip()) > 35:
                        continue
                    else:
                        found = True
                        tempstring += line
                else:
                    continue
            elif re.search("^" + self.universal, line.strip()):
                continue
            else:
                tempstring += line
        if not found:
            tempstring += "INACTIVE=35\n"
        tmpfile = self.useraddfile + ".tmp"
        if not writeFile(tmpfile, tempstring, self.logger):
            return False
        if not self.useraddcreate:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'conf',
                     'filepath': self.useraddfile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.useraddfile, tmpfile,
                                                 myid)
        shutil.move(tmpfile, self.useraddfile)
        os.chown(self.useraddfile, 0, 0)
        os.chmod(self.useraddfile, 0o600)
        resetsecon(self.useraddfile)
        return success

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

    def setlibuser(self):
        success = True
        debug = ""
        created = False
        data = {"userdefaults": {"LU_SHADOWMAX": "",
                                 "LU_SHADOWMIN": "",
                                 "LU_SHADOWWARNING": "",
                                 "LU_UIDNUMBER": "",
                                 "LU_SHADOWINACTIVE": "",
                                 "LU_SHADOWEXPIRE": ""}}
        '''check if installed'''
        if not self.ph.check("libuser"):
            '''if not installed, check if available'''
            if self.ph.checkAvailable("libuser"):
                '''if available, install it'''
                if not self.ph.install("libuser"):
                    self.detailedresults += "Unable to install libuser\n"
                    return False
                else:
                    '''since we're just now installing it we know we now
                    need to create the kveditor'''
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    comm = self.ph.getRemove()
                    event = {"eventtype": "commandstring",
                             "command": comm}
                    self.statechglogger.recordchgevent(myid, event)
                    datatype = "tagconf"
                    intent = "notpresent"
                    tmppath = self.libuserfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, datatype,
                                                  self.libuserfile, tmppath,
                                                  data, intent, "openeq")
                    self.editor2.report()
            else:
                return True
        if not os.path.exists(self.libuserfile):
            if not createFile(self.libuserfile, self.logger):
                self.detailedresults += "Unable to create libuser file\n"
                debug = "Unable to create the libuser file\n"
                self.logger.log(LogPriority.DEBUG, debug)
                return False
            created = True
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation",
                     "filepath": self.libuserfile}
            self.statechglogger.recordchgevent(myid, event)
            tmppath = self.libuserfile + ".tmp"
            self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                          "tagconf", self.libuserfile,
                                          tmppath, data,
                                          "notpresent", "openeq")
            self.editor2.report()
        if not checkPerms(self.libuserfile, [0, 0, 0o644], self.logger):
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(self.libuserfile, [0, 0, 0o644],
                                self.logger, self.statechglogger, myid):
                    self.detailedresults += "Could not set permissions on " + \
                        self.libuserfile
                    success = False
            elif not setPerms(self.libuserfile, [0, 0, 0o644], self.logger):
                success = False
                self.detailedresults += "Unable to set the " + \
                        "permissions on " + self.libuserfile + "\n"
        if self.editor2.removeables:
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor2.setEventID(myid)
            if self.editor2.fix():
                if self.editor2.commit():
                    debug += "/etc/libuser.conf has been corrected\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    os.chown(self.libuserfile, 0, 0)
                    os.chmod(self.libuserfile, 0o644)
                    resetsecon(self.libuserfile)
                else:
                    self.detailedresults += "/etc/libuser.conf " + \
                        "couldn't be corrected\n"
                    success = False
            else:
                self.detailedresults += "/etc/libuser.conf couldn't " + \
                    "be corrected\n"
                success = False
        return success

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

    def fixLogin(self):
        success = True
        tempstring = ""
        debug = ""
        if not os.path.exists(self.loginfile):
            self.detailedresults = self.loginfile + "does not exist. \
Will not perform fix on useradd file\n"
            return False
        if not checkPerms(self.loginfile, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.loginfile, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                success = False
        contents = readFile(self.loginfile, self.logger)
        iterator1 = 0
        for line in contents:
            if re.search("^#", line) or re.match('^\s*$', line):
                iterator1 += 1
            elif re.search('^default:\\\\$', line.strip()):
                contents1 = contents[:iterator1 + 1]
                temp = contents[iterator1 + 1:]
                length2 = len(temp) - 1
                iterator2 = 0
                for line2 in temp:
                    if re.search('^[^:][^:]*:\\\\$', line2):
                        contents3 = temp[iterator2:]
                        contents2 = temp[:iterator2]
                        break
                    elif iterator2 < length2:
                        iterator2 += 1
                    elif iterator2 == length2:
                        contents2 = temp[:iterator2]
                break
            else:
                iterator1 += 1
        if contents2:
            for key in self.Fspecs:
                iterator = 0
                found = False
                for line in contents2:
                    if re.search("^#", line) or re.match('^\s*$', line):
                        iterator += 1
                        continue
                    elif re.search('^:' + key, line.strip()):
                        if re.search('=', line):
                            temp = line.split('=')
                            if re.search(str(self.Fspecs[key]) +
                                         '(:\\\\|:|\\\\|\s)',
                                         temp[1]):
                                iterator += 1
                                found = True
                            else:
                                contents2.pop(iterator)
                    else:
                        iterator += 1
                if not found:
                    contents2.append('\t' + key + '=' + str(self.Fspecs[key]) +
                                     ':\\\\\n')
        final = []
        for line in contents1:
            final.append(line)
        for line in contents2:
            final.append(line)
        for line in contents3:
            final.append(line)
        for line in final:
            tempstring += line
        debug += "tempstring to be written to: " + self.loginfile + "\n"
        self.logger.log(LogPriority.DEBUG, debug)
        tmpfile = self.loginfile + ".tmp"
        if writeFile(tmpfile, tempstring, self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'conf',
                     'filepath': self.loginfile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.loginfile, tmpfile, myid)
            shutil.move(tmpfile, self.loginfile)
            os.chown(self.loginfile, 0, 0)
            os.chmod(self.loginfile, 0o644)
            resetsecon(self.loginfile)
        else:
            success = False
        return success