Ejemplo n.º 1
0
class InstallVLock(Rule):
    '''This class installs the vlock package to enable screen locking
    vlock is the package name on opensuse 15+, debian, ubuntu
    kbd is the package name on opensuse 42.3-, rhel, fedora, centos (contains vlock package)
    
    references:
    https://pkgs.org/download/vlock
    https://access.redhat.com/discussions/3543671


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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 121
        self.rulename = "InstallVLock"
        self.mandatory = True
        self.rootrequired = True
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 2.3.5.6"]
        self.applicable = {'type': 'white', 'family': ['linux', 'freebsd']}

        # Configuration item instantiation
        datatype = 'bool'
        key = 'INSTALLVLOCK'
        instructions = "To disable installation of the command line " + \
            "screen lock program vlock set the value of INSTALLVLOCK to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.sethelptext()

    def set_pkg(self):
        '''set package name based on distro'''

        majorver = self.environ.getosmajorver()

        if self.ph.manager in ["yum", "dnf"]:
            self.pkg = "kbd"
        elif bool(self.ph.manager == "zypper" and majorver == "15"):
            self.pkg = "kbd"
        else:
            self.pkg = "vlock"

    def report(self):
        '''Perform a check to see if package is already installed.
        If so, there is  no need to run Fix method


        :returns: self.compliant

        :rtype: bool
@author: Derek T Walker

        '''

        try:

            self.detailedresults = ""
            self.ph = Pkghelper(self.logger, self.environ)
            self.ch = CommandHelper(self.logger)
            self.compliant = True

            self.set_pkg()

            if not self.ph.check(self.pkg):
                self.compliant = False
                self.detailedresults += "\nvlock Package is NOT installed"
            else:
                self.detailedresults += "\nvlock Package is installed"

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

    def fix(self):
        '''The fix method will apply the required settings to the system.
        self.rulesuccess will be updated if the rule does not succeed.
        Attempt to install Vlock, record success or failure in event
        logger.


        :returns: self.rulesuccess

        :rtype: bool
@author: Derek T Walker

        '''

        try:

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

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

            # Clear out event history so only the latest fix is recorded
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            undocmd = self.ph.getRemove()

            if not self.ph.install(self.pkg):
                self.rulesuccess = False
                self.detailedresults += "\nFailed to install vlock package"
            else:
                undocmd += self.pkg
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "comm", "command": undocmd}
                self.statechglogger.recordchgevent(myid, event)
                self.detailedresults += "\nvlock Package was installed successfully"

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 2
0
class RestrictMounting(Rule):
    '''Class help text'''

    def __init__(self, config, enviro, logger, statechglogger):
        """
        Constructor
        """

        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 112
        self.rulename = "RestrictMounting"
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.sethelptext()

        # Configuration item instantiation
        datatype1 = "bool"
        key1 = "RESTRICTCONSOLEACCESS"
        instructions1 = "To restrict console device access, set " + \
                       "RESTRICTCONSOLEACCESS to True."
        default1 = False
        self.consoleCi = self.initCi(datatype1, key1, instructions1, default1)

        datatype2 = "bool"
        key2 = "DISABLEAUTOFS"
        instructions2 = "To disable dynamic NFS mounting through the " + \
                       "autofs service, set DISABLEAUTOFS to True."
        default2 = False
        self.autofsCi = self.initCi(datatype2, key2, instructions2, default2)

        datatype3 = "bool"
        key3 = "DISABLEGNOMEAUTOMOUNT"
        instructions3 = "To disable the GNOME desktop environment from " + \
                       "automounting devices and removable media, set " + \
                       "DISABLEGNOMEAUTOMOUNT to True."
        default3 = False
        self.gnomeCi = self.initCi(datatype3, key3, instructions3, default3)

        self.guidance = ["NSA 2.2.2.1", "NSA 2.2.2.3", "NSA 2.2.2.4",
                         "CCE 3685-5", "CCE 4072-5", "CCE 4231-7",
                         "CCE-RHEL7-CCE-TBD 2.2.2.6"]
        self.applicable = {"type": "white",
                           "family": ["linux"]}
        self.iditerator = 0
        self.gsettings = "/usr/bin/gsettings"
        self.gconftool = "/usr/bin/gconftool-2"
        self.dbuslaunch = "/usr/bin/dbus-launch"

    def report(self):
        ''' '''

        self.automountMedia = True
        self.automountDrives = True
        self.sec_console_perms1 = "/etc/security/console.perms.d/50-default.perms"
        self.sec_console_perms2 = "/etc/security/console.perms"
        self.console_perms_temppath = self.sec_console_perms2 + ".stonixtmp"
        self.data = {"<console>": "tty[0-9][0-9]* vc/[0-9][0-9]* :0\.[0-9] :0",
                     "<xconsole>": "0\.[0-9] :0"}
        self.autofspkg = "autofs"
        self.autofssvc = "autofs"
        self.ph = Pkghelper(self.logdispatch, self.environ)
        self.sh = ServiceHelper(self.environ, self.logdispatch)
        self.ch = CommandHelper(self.logdispatch)
        self.compliant = True
        self.detailedresults = ""

        try:

            if os.path.exists(self.sec_console_perms1):
                current_config = readFile(self.sec_console_perms1, self.logger)
                for line in current_config:
                    if re.search("^<[x]?console>", line, re.M):
                        self.compliant = False
                        self.detailedresults += self.sec_console_perms1 + " contains unrestricted device access\n"
                        break

            if os.path.exists(self.sec_console_perms2):
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger, "conf", self.sec_console_perms2, self.console_perms_temppath, self.data, "present", "closedeq")
                if not self.editor2.report():
                    self.compliant = False
                    self.detailedresults += self.sec_console_perms2 + " does not contain the correct values\n"

            if self.ph.check(self.autofspkg):
                if self.sh.auditService(self.autofssvc, _="_"):
                    self.compliant = False
                    self.detailedresults += "autofs is installed and enabled\n"

            if os.path.exists(self.gsettings):
                automountOff = False
                autorunNever = False
                cmd = [self.gsettings, "get", "org.gnome.desktop.media-handling",
                       "automount"]
                self.ch.executeCommand(cmd)
                if re.search("false", self.ch.getOutputString()):
                    automountOff = True

                cmd = [self.gsettings, "get", "org.gnome.desktop.media-handling",
                       "autorun-never"]
                self.ch.executeCommand(cmd)
                if re.search("true", self.ch.getOutputString()):
                    autorunNever = True
                    debug = "autorun-never is enabled"
                    self.logger.log(LogPriority.DEBUG, debug)

                self.automountOff = automountOff
                self.autorunNever = autorunNever

                if not automountOff or not autorunNever:
                    self.compliant = False
                    self.detailedresults += "GNOME automounting is enabled\n"

            # check for gnome automounting
            if os.path.exists(self.gconftool):
                cmd = [self.gconftool, "-R", "/desktop/gnome/volume_manager"]

                if os.path.exists("/desktop/gnome/volume_manager"):
                    self.ch.executeCommand(cmd)
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        self.compliant = False
                        self.detailedresults += "\nFailed to read gnome volume manager config"
    
                    output = self.ch.getOutputString()
    
                    if re.search("automount_media.*false", output):
                        self.automountMedia = False
                    if re.search("automount_drives.*false", output):
                        self.automountDrives = False

                else:
                    self.automountMedia = False
                    self.automountDrives = False
                mylist = [self.automountMedia, self.automountDrives]

                if any(mylist):
                    self.compliant = False
                    self.detailedresults += "GNOME automounting is enabled\n"

            # reset these directories to be owned by their respective users
            dirs = ''
            if os.path.exists('/run/user'):
                dirs = os.listdir('/run/user')

            if dirs:
                for d in dirs:
                    # check if the directory is an integer representing a uid
                    if re.search('^([+-]?[1-9]\d*|0)$', d, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG, "Found UID directory")
                        try:
                            os.chown('/run/user/' + d + '/dconf/user', int(d), int(d))
                        except Exception as errmsg:
                            self.logger.log(LogPriority.DEBUG, str(errmsg))
                            continue
            else:
                self.logger.log(LogPriority.DEBUG, "no directories in /run/user")

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

        return self.compliant

    def fix(self):
        ''' '''

        self.detailedresults = ""
        self.iditerator = 0
        self.rulesuccess = True
        success = True
        consoleaccess = self.consoleCi.getcurrvalue()
        autofs = self.autofsCi.getcurrvalue()
        gnomeautomount = self.gnomeCi.getcurrvalue()
        mylist = [consoleaccess, autofs, gnomeautomount]

        try:

            # if none of the CIs are enabled, skip fix
            if not any(mylist):
                self.detailedresults += "\nNone of the CI's were enabled. Nothing was done."
                self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.rulesuccess

            # clear event list data
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            # if restrict console access CI is enabled, restrict console access
            if self.consoleCi.getcurrvalue():
                if os.path.exists(self.sec_console_perms1):
                    tmpfile = self.sec_console_perms1 + ".stonixtmp"
                    defaultPerms = open(self.sec_console_perms1, "r").read()
                    defaultPerms = re.sub("(<[x]?console>)", r"#\1", defaultPerms)
                    if writeFile(tmpfile, defaultPerms, self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "conf", "filepath": self.sec_console_perms1}
                        self.statechglogger.recordchgevent(myid, event)
                        self.statechglogger.recordfilechange(self.sec_console_perms1, tmpfile, myid)
                        os.rename(tmpfile, self.sec_console_perms1)
                        resetsecon(self.sec_console_perms1)
                    else:
                        success = False
                        self.detailedresults += "Problem writing new contents to " + \
                                   "temporary file"
                if os.path.exists(self.sec_console_perms2):
                    self.editor = KVEditorStonix(self.statechglogger, self.logger, "conf", self.sec_console_perms2, self.console_perms_temppath, self.data, "present", "closedeq")
                    self.editor.report()
                    if self.editor.fixables:
                        if self.editor.fix():
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.editor.setEventID(myid)
                            if self.editor.commit():
                                debug = self.sec_console_perms2 + "'s contents have been " \
                                    + "corrected\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                resetsecon(self.sec_console_perms2)
                            else:
                                debug = "kveditor commit not successful\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                success = False
                                self.detailedresults += self.sec_console_perms2 + \
                                    " properties could not be set\n"
                        else:
                            debug = "kveditor fix not successful\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                            self.detailedresults += self.sec_console_perms2 + \
                                " properties could not be set\n"

            # if autofs CI is enabled, disable autofs
            if self.autofsCi.getcurrvalue():
                if self.ph.check(self.autofspkg) and \
                   self.sh.auditService(self.autofssvc, _="_"):
                    if self.sh.disableService(self.autofssvc, _="_"):
                        debug = "autofs service successfully disabled\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "servicehelper", "servicename":
                                 self.autofssvc, "startstate": "enabled",
                                 "endstate": "disabled"}
                        self.statechglogger.recordchgevent(myid, event)
                    else:
                        success = False
                        debug = "Unable to disable autofs service\n"
                        self.logger.log(LogPriority.DEBUG, debug)

            returnCode = 0
            if self.gnomeCi.getcurrvalue():
                if os.path.exists(self.gsettings):
                    # gsettings requires a D-Bus session bus in order to make
                    # any changes. This is because the dconf daemon must be
                    # activated using D-Bus.
                    if not os.path.exists(self.dbuslaunch):
                        self.ph.install("dbus-x11")
                    
                    if os.path.exists(self.dbuslaunch):
                        if not self.automountOff:
                            cmd = [self.dbuslaunch, self.gsettings, "set",
                                   "org.gnome.desktop.media-handling",
                                   "automount", "false"]
                            self.ch.executeCommand(cmd)
                            returnCode = self.ch.getReturnCode()

                            if not returnCode:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, 
                                               self.rulenumber)
                                event = {"eventtype": "comm", "command":
                                        ["dbus-launch", "gsettings", "set",
                                         "org.gnome.desktop.media-handling",
                                         "automount", "true"]}
                                self.statechglogger.recordchgevent(myid, event)

                        if not self.autorunNever:
                            cmd = [self.dbuslaunch, "gsettings", "set",
                                   "org.gnome.desktop.media-handling",
                                   "autorun-never", "true"]
                            self.ch.executeCommand(cmd)
                            returnCode += self.ch.getReturnCode()

                            if not self.ch.getReturnCode():
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                event = {"eventtype": "comm", "command":
                                        [self.dbuslaunch, self.gsettings, "set",
                                         "org.gnome.desktop.media-handling",
                                         "autorun-never", "false"]}
                                self.statechglogger.recordchgevent(myid, event)
                    else:
                        success = False
                        debug = "Unable to disable GNOME automounting: " + \
                                "dbus-x11 is not installed"
                        self.logger.log(LogPriority.DEBUG, debug)

                if os.path.exists(self.gconftool):
                    if self.automountMedia:
                        cmd = [self.gconftool, "--direct", "--config-source",
                               "xml:readwrite:/etc/gconf/gconf.xml.mandatory",
                               "--type", "bool", "--set",
                               "/desktop/gnome/volume_manager/automount_media",
                               "false"]
                        self.ch.executeCommand(cmd)
                        returnCode = self.ch.getReturnCode()

                        if not returnCode:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "comm", "command":
                                     [self.gconftool, "--direct",
                                      "--config-source", "xml:readwrite:" +
                                      "/etc/gconf/gconf.xml.mandatory",
                                      "--type", "bool", "--set",
                                      "/desktop/gnome/volume_manager/" +
                                      "automount_media", "true"]}
                            self.statechglogger.recordchgevent(myid, event)

                    if self.automountDrives:
                        cmd = [self.gconftool, "--direct", "--config-source",
                               "xml:readwrite:/etc/gconf/gconf.xml.mandatory",
                               "--type", "bool", "--set",
                               "/desktop/gnome/volume_manager/automount_drives",
                               "false"]
                        self.ch.executeCommand(cmd)
                        returnCode += self.ch.getReturnCode()

                        if not self.ch.getReturnCode():
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "comm", "command":
                                     [self.gconftool, "--direct",
                                      "--config-source", "xml:readwrite:" +
                                      "/etc/gconf/gconf.xml.mandatory",
                                      "--type", "bool", "--set",
                                      "/desktop/gnome/volume_manager/" +
                                      "automount_drives",
                                      "true"]}
                            self.statechglogger.recordchgevent(myid, event)

                if returnCode:
                    success = False
                    self.detailedresults += "Fix failed to disable GNOME automounting\n"

            # reset these directories to be owned by their respective users
            dirs = ''
            if os.path.exists('/run/user'):
                dirs = os.listdir('/run/user')

            if dirs:
                for d in dirs:
                    # check if the directory is an integer representing a uid
                    if re.search('^([+-]?[1-9]\d*|0)$', d, re.IGNORECASE):
                        self.logger.log(LogPriority.DEBUG, "Found UID directory")
                        try:
                            os.chown('/run/user/' + d + '/dconf/user', int(d), int(d))
                        except Exception as errmsg:
                            self.logger.log(LogPriority.DEBUG, str(errmsg))
                            continue
            else:
                self.logger.log(LogPriority.DEBUG, "no directories in /run/user")

            self.rulesuccess = success

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 3
0
class ConfigureMACPolicy(Rule):
    """The ConfigureMACPolicy class configures either selinux or apparmor
    depending on the os platform.
    @change: Derek Walker - created two config items, one for enable/disable, and
        another for whether the user wants to use permissive or enforcing

    """

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

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

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

    def report(self):
        """

        :return: 
        """

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

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

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

        try:

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

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

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

        return self.compliant

    def set_selinux_conf_path(self):
        """

        :return:
        """

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

    def reportSelinux(self):
        """

        :return:
        """

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

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

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

        self.set_selinux_conf_path()

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

        return compliant

    def reportApparmor(self):
        """

        :return:
        """

        compliant = True

        aa_enabled = "Yes"

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

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

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

        return compliant

    def fix(self):
        """

        :return: 
        """

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

        try:

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

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

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

        return self.rulesuccess

    def fixSelinux(self):
        """

        :return:
        """

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

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

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

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

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

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

        return success

    def fixApparmor(self):
        """

        :return:
        """

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

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

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

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

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

            os.rename(tmp_grub_file, grub_file)

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

        return success
Ejemplo n.º 4
0
class SystemAccounting(Rule):
    """
    """
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 9
        self.rulename = 'SystemAccounting'
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.rootrequired = True
        self.sethelptext()
        self.guidance = ['CIS 2.4', 'cce-3992-5']
        self.applicable = {
            'type': 'white',
            'family': 'linux',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # set up configuration item for this rule
        datatype = 'bool'
        key = 'SYSTEMACCOUNTING'
        instructions = "This is an optional rule and is disabled by default, due to the significant load it can place on the system when enabled. To enable system accounting, set the value of SYSTEMACCOUNTING to True."
        default = False
        self.ci = self.initCi(datatype, key, instructions, default)

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

    def _set_paths(self):
        """

        """

        self.sysstat_package = "sysstat"
        self.sysstat_service_file = ""
        sysstat_service_locs = [
            "/usr/lib/systemd/system/sysstat.service",
            "/lib/systemd/system/sysstat.service", "/etc/init.d/sysstat"
        ]
        for ss in sysstat_service_locs:
            if os.path.isfile(ss):
                self.sysstat_service_file = ss
                break
        self.accton = "/usr/sbin/accton"
        self.acct_file = "/var/account/acct"
        self.cron_file = "/etc/cron.d/sysstat"

        self.sa1 = ""
        sa1_locs = [
            "/usr/lib64/sa/sa1", "/usr/local/lib64/sa/sa1",
            "/usr/lib/sysstat/sa1"
        ]
        for sl in sa1_locs:
            if os.path.isfile(sl):
                self.sa1 = sl
                break

        self.sa2 = ""
        sa2_locs = [
            "/usr/lib64/sa/sa2", "/usr/local/lib64/sa/sa2",
            "/usr/lib/sysstat/sa2"
        ]
        for sl in sa2_locs:
            if os.path.isfile(sl):
                self.sa2 = sl
                break

        self.sysstat_service_contents = """# /usr/lib/systemd/system/sysstat.service
# (C) 2012 Peter Schiffer (pschiffe <at> redhat.com)
#
# sysstat-10.1.7 systemd unit file:
#     Insert a dummy record in current daily data file.
#     This indicates that the counters have restarted from 0.

[Unit]
Description=Resets System Activity Logs

[Service]
Type=oneshot
RemainAfterExit=yes
User=root
ExecStart=""" + self.sa1 + """ --boot

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

        self.sysstat_cron_contents = """# Run system activity accounting tool every 60 minutes
*/60 * * * * root """ + self.sa1 + """ 1 1
# Generate a daily summary of process accounting at 23:53
53 23 * * * root """ + self.sa2 + """ -A"""

    def _report_configuration(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        if self.ostype == "Mac OS X":
            self.conf_file = ""
            conf_files = ["/etc/rc.conf", "/etc/rc.common"]
            for cf in conf_files:
                if os.path.isfile(cf):
                    self.conf_file = cf
                    break
            tmpfile = self.conf_file + ".stonixtmp"

            config_data = {"accounting_enable": "YES"}

            self.conf_editor = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.conf_file, tmpfile,
                                              config_data, "present",
                                              "closedeq")
            if not self.conf_editor.report():
                compliant = False
        else:

            if not os.path.isfile(self.sysstat_service_file):
                compliant = False
                self.detailedresults += "\nSystem accounting service file is missing"
            else:
                f = open(self.sysstat_service_file, "r")
                contents = f.read()
                f.close()
                if self.sysstat_service_file != "/etc/init.d/sysstat":
                    if contents != self.sysstat_service_contents:
                        compliant = False
                        self.detailedresults += "\nSystem accounting service file has incorrect contents"

        if os.path.isfile("/etc/default/sysstat"):
            f = open("/etc/default/sysstat", "r")
            contents = f.read()
            f.close()
            if not re.search('ENABLED="true"', contents):
                compliant = False
                self.detailedresults += "\n/etc/default/sysstat file has incorrect contents"

        return compliant

    def _report_installation(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        if self.ostype != "Mac OS X":
            if not self.ph.check(self.sysstat_package):
                compliant = False
                self.detailedresults += "\nSystem accounting package is not installed"

        return compliant

    def _report_schedule(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        if self.ostype == "Mac OS X":
            if not os.path.isfile(self.acct_file):
                compliant = False
                self.detailedresults += "\nSystem accounting is not enabled on this system"
        else:
            if not os.path.isfile(self.cron_file):
                compliant = False
            else:
                f = open(self.cron_file, "r")
                contents = f.read()
                f.close()
                if contents != self.sysstat_cron_contents:
                    self.compliant = False
                    self.detailedresults += "\nSystem account cron job has incorrect contents"

        return compliant

    def report(self):
        """

        :return: compliant
        :rtype: bool
        """

        self.detailedresults = ""
        self.compliant = True

        try:

            if not self._report_installation():
                self.compliant = False
            else:
                if not self._report_configuration():
                    self.compliant = False
                if not self._report_schedule():
                    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 _fix_installation(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        if self.ostype != "Mac OS X":
            if not self.ph.install(self.sysstat_package):
                success = False
                self.logger.log(LogPriority.DEBUG,
                                "Failed to install sysstat package")

        return success

    def _fix_configuration(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        if self.ostype == "Mac OS X":
            if not self.conf_editor.fix():
                success = False
                self.logger.log(LogPriority.DEBUG, "kveditor failed to fix()")
            elif not self.conf_editor.commit():
                success = False
                self.logger.log(LogPriority.DEBUG,
                                "kveditor failed to commit()")
        else:
            try:
                if self.sysstat_service_file != "/etc/init.d/sysstat":
                    f = open(self.sysstat_service_file, "w")
                    f.write(self.sysstat_service_contents)
                    f.close()
            except:
                success = False

        if os.path.isfile("/etc/default/sysstat"):
            default_sysstat_contents = """# 
# Default settings for /etc/init.d/sysstat, /etc/cron.d/sysstat 
# and /etc/cron.daily/sysstat files 
# 

# Should sadc collect system activity information? Valid values 
# are 'true' and 'false'. Please do not put other values, they 
# will be overwritten by debconf! 
ENABLED="true"
"""
            f = open("/etc/default/sysstat", "w")
            f.write(default_sysstat_contents)
            f.close()

        return success

    def _fix_schedule(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        try:

            if self.ostype == "Mac OS X":
                if not os.path.isdir("/var/account"):
                    os.mkdir("/var/account", 0o755)
                open(self.acct_file, "a").close()
                self.ch.executeCommand(self.accton + " " + self.acct_file)
            else:
                f = open(self.cron_file, "w")
                f.write(self.sysstat_cron_contents)
                f.close()
                os.chown(self.cron_file, 0, 0)
                os.chmod(self.cron_file, 0o644)
        except:
            success = False

        return success

    def fix(self):
        """

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

        self.detailedresults = ""
        self.rulesuccess = True

        try:

            if not self._fix_installation():
                self.rulesuccess = False
            else:
                self._set_paths()
            if not self._fix_configuration():
                self.rulesuccess = False
            if not self._fix_schedule():
                self.rulesuccess = False

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

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

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

            self.euid = self.environ.geteuid()

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

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

        self.detailedresults = ""
        self.compliant = True

        try:

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

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

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

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

            self.compliant = compliant

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

        return self.compliant

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

        :return: installed
        :rtype: bool
        """

        self.screen_pkg = ""
        installed = True

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

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

        return installed

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

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

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

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

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

        """

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

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

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

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

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

        return compliant

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

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

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

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

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

        """

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

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

        return self.rulesuccess

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

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

        """

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

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

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

            elif not self.correctFile(kdefile, username):

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

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

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

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

            cmd = ""

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

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

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

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

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

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

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

        :param filehandle: string
        :returns: bool

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

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

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

        """
        success = True

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

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

        return success

    def undo(self):
        self.detailedresults += "This rule cannot be reverted\n"
        self.rulesuccess = False
        self.formatDetailedResults("undo", self.rulesuccess,
                                   self.detailedresults)
Ejemplo n.º 6
0
class SecurePOPIMAP(Rule):
    '''classdocs'''

    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 141
        self.rulename = 'SecurePOPIMAP'
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {'type': 'black',
                           'family': ['darwin']}
        self.guidance = ['NSA(3.17)', 'cce-4384-4', '3887-7', '4530-2', '4547-6', '4552-6', '4371-1', '4410-7']

        data1 = 'bool'
        key1 = 'DISABLEPOPIMAP'
        instructions1 = 'To prevent POP/IMAP services from being disabled entirely, set the value of DisablePOPIMAP to False.'
        default1 = True
        self.disableci = self.initCi(data1, key1, instructions1, default1)

        data2 = 'bool'
        key2 = 'SECUREPOPIMAP'
        instructions2 = 'To securely configure POP/IMAP services, set the value of SecurePOPIMAP to True.'
        default2 = False
        self.secureci = self.initCi(data2, key2, instructions2, default2)

        data3 = 'string'
        key3 = 'RequireProtocols'
        instructions3 = 'If this system will be operating as a mail server, fill in the required/used protocols below. Please use a space-delimited list. Valid entries are limited to: imap, imaps, pop3, pop3s'
        default3 = ''
        self.reqprotocols = self.initCi(data3, key3, instructions3, default3)

        self.localization()

    def localization(self):
        '''determine which type of OS we are running on and set up class variables accordingly


        :returns: void
        @author: Breen Malmberg

        '''

        self.initobjs()

        self.setcommon()

        if self.pkgh.manager not in ['apt-get', 'yum', 'zypper', 'dnf']:
            self.logger.log(LogPriority.DEBUG, "Could not identify OS type or OS not supported!")

        if self.pkgh.manager == 'apt-get':
            self.setdebian()
        if self.pkgh.manager == 'yum':
            self.setredhat()
        if self.pkgh.manager == 'dnf':
            self.setredhat()
        if self.pkgh.manager == 'zypper':
            self.setsuse()

        pass

    def initobjs(self):
        '''initialize objects needed/used by other methods within this class


        :returns: void
        @author: Breen Malmberg

        '''

        try:

            # if you add class variables here, be sure to also add
            # checks for them, in the checkinitobjs() method
            self.pkgh = Pkghelper(self.logger, self.environ)
            self.svch = ServiceHelper(self.environ, self.logger)
            self.cmdh = CommandHelper(self.logger)
            self.debian = False
            self.suse = False
            self.redhat = False
            self.pkgdict = {}
            self.confpathdict = {}
            self.servicename = ''
            self.osdetected = False

        except Exception:
            raise

    def setcommon(self):
        '''set variables which are common to all platforms


        :returns: void
        @author: Breen Malmberg

        '''

        self.detailedresults = ""

        self.reqprots = ""

        self.protocollist = ['imap', 'imaps', 'pop3', 'pop3s']

    def setdebian(self):
        '''set debian specific variables


        :returns: void
        @author: Breen Malmberg

        '''

        self.debian = True
        self.osdetected = True
        self.pkgdict = {'dovecot-core': False,
                        'dovecot-pop3d': False,
                        'dovecot-imapd': False}

        # the following dictionary is of the format:
        # {configfilepath1: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2},
        # configfilepath2: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2}
        # }
        self.confpathdict = {'/etc/dovecot/dovecot.conf': {'protocols =': 'protocols = ' + str(self.reqprots) + '\n',
                                       'disable_plaintext_auth =': 'disable_plaintext_auth = yes\n',
                                       'login_process_per_connection =': 'login_process_per_connection = yes\n',
                                       'mail_drop_priv_before_exec =': 'mail_drop_priv_before_exec = yes\n',
                                       'login_trusted_networks =': '#login_trusted_networks =\n'}}
        self.servicename = 'dovecot'

    def setredhat(self):
        '''set redhat sepcific variables


        :returns: void
        @author: Breen Malmberg

        '''

        self.redhat = True
        self.osdetected = True
        self.pkgdict = {'dovecot': False}

        # the following dictionary is of the format:
        # {configfilepath1: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2},
        # configfilepath2: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2}
        # }
        self.confpathdict = {'/etc/dovecot/dovecot.conf': {'protocols =': 'protocols = ' + str(self.reqprots) + '\n',
                                       'disable_plaintext_auth =': 'disable_plaintext_auth = yes\n',
                                       'login_process_per_connection =': 'login_process_per_connection = yes\n',
                                       'mail_drop_priv_before_exec =': 'mail_drop_priv_before_exec = yes\n',
                                       'login_trusted_networks =': '#login_trusted_networks =\n'}}
        self.servicename = 'dovecot'

    def setsuse(self):
        '''set suse specific variables


        :returns: void
        @author: Breen Malmberg

        '''

        self.suse = True
        self.osdetected = True
        self.pkgdict = {'dovecot': False}

        # the following dictionary is of the format:
        # {configfilepath1: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2},
        # configfilepath2: {partialmatch1: fullreplacement1,
        #                    partialmatch2: fullreplacement2}
        # }
        self.confpathdict = {'/etc/dovecot/dovecot.conf': {'protocols =': 'protocols = ' + str(self.reqprots) + '\n',
                                       'disable_plaintext_auth =': 'disable_plaintext_auth = yes\n',
                                       'login_process_per_connection =': 'login_process_per_connection = yes\n',
                                       'mail_drop_priv_before_exec =': 'mail_drop_priv_before_exec = yes\n',
                                       'login_trusted_networks =': '#login_trusted_networks =\n'}}
        self.servicename = 'dovecot'

    def getFileContents(self, filepath):
        '''

        :param filepath: 

        '''

        filecontents = []

        try:

            if not isinstance(filepath, str):
                self.logger.log(LogPriority.DEBUG, "Parameter filepath must be of type: str")
            if not filepath:
                self.logger.log(LogPriority.DEBUG, "Parameter filepath must not be blank")

            if not os.path.exists(filepath):
                self.logger.log(LogPriority.DEBUG, "Specified filepath not found")
            else:
                f = open(filepath, 'r')
                filecontents = f.readlines()
                f.close()

            if not filecontents:
                self.logger.log(LogPriority.DEBUG, "Specified file had no contents")

        except Exception:
            raise
        return filecontents

    def searchContents(self, regex, contents):
        '''

        :param regex: 
        :param contents: 

        '''

        retval = False

        try:

            if not isinstance(regex, str):
                self.logger.log(LogPriority.DEBUG, "Parameter regex must be of type: str")
                retval = False
                return retval
            if not isinstance(contents, list):
                self.logger.log(LogPriority.DEBUG, "Parameter contents must be of type: list")
                retval = False
                return retval

            if not regex:
                self.logger.log(LogPriority.DEBUG, "regex must not be blank")
                retval = False
                return retval
            if not contents:
                self.logger.log(LogPriority.DEBUG, "contents must not be blank")
                retval = False
                return retval

            for line in contents:
                if re.search(regex, line):
                    retval = True

        except Exception:
            raise
        return retval

    def fixContents(self, fixdict, filepath, contents):
        '''

        :param fixdict: 
        :param filepath: 
        :param contents: 

        '''

        retval = True

        if not isinstance(fixdict, dict):
            self.logger.log(LogPriority.DEBUG, "Parameter fixdict must be of type: dict")
            retval = False
            return retval
        if not isinstance(filepath, str):
            self.logger.log(LogPriority.DEBUG, "Parameter filepath must be of type: str")
            retval = False
            return retval
        if not isinstance(contents, list):
            self.logger.log(LogPriority.DEBUG, "Parameter contents must be of type: list")
            retval = False
            return retval

        if not fixdict:
            self.logger.log(LogPriority.DEBUG, "Parameter fixdict must not be empty")
            retval = False
            return retval
        if not filepath:
            self.logger.log(LogPriority.DEBUG, "Parameter filepath must not be blank")
            retval = False
            return retval
        if not contents:
            self.logger.log(LogPriority.DEBUG, "Parameter contents must not be empty")
            retval = False
            return retval

        contentdict = {}
        for item in fixdict:
            contentdict[item] = False

        tempfilepath = filepath + '.stonixtmp'

        for line in contents:
            for partialmatch in fixdict:
                if re.search(partialmatch, line):
                    contents = [c.replace(line, fixdict[partialmatch]) for c in contents]
                    contentdict[partialmatch] = True

        for item in contentdict:
            if not contentdict[item]:
                contents.append('\n' + fixdict[item])

        tf = open(tempfilepath, 'w')
        tf.writelines(contents)
        tf.close()

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

        self.statechglogger.recordchgevent(myid, event)
        self.statechglogger.recordfilechange(filepath, tempfilepath, myid)

        os.rename(tempfilepath, filepath)

    def report(self):
        '''return true if all check actions report compliant
        return false if one or more check actions reports not compliant


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        self.compliant = True
        self.detailedresults = ''
        self.logger.log(LogPriority.DEBUG, "Entering SecurePOPIMAP.report()...")

        try:

            self.reqprots = self.reqprotocols.getcurrvalue()

            if self.suse:
                self.setsuse()
            if self.debian:
                self.setdebian()
            if self.redhat:
                self.setredhat()

            self.logger.log(LogPriority.DEBUG, "Checking init objects...")
            if not self.checkinitobjs():
                self.logger.log(LogPriority.DEBUG, 'One or more class properties were not initialized or set correctly.')
                self.rulesuccess = False
                self.compliant = False
                self.formatDetailedResults("report", self.compliant, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.compliant
            self.logger.log(LogPriority.DEBUG, "Finished checking init objects. all were OK")

            # disable stuff
            if self.disableci.getcurrvalue():
                self.logger.log(LogPriority.DEBUG, "The disable POP/IMAP option is set. Checking if POP/IMAP is disabled...")
                self.logger.log(LogPriority.DEBUG, "Checking for running service...")
                if not self.checksvc():
                    self.compliant = False
                    self.detailedresults += "\nRule is not compliant because: The " + str(self.servicename) + " service is either running or enabled."
                self.logger.log(LogPriority.DEBUG, "Service check finished.")

                self.logger.log(LogPriority.DEBUG, "Checking packages...")
                if not self.checkpkgs(False):
                    self.compliant = False
                    self.detailedresults += "\nRule is not compliant because one or more of the packages is still installed."
                self.logger.log(LogPriority.DEBUG, "Package check finished.")

            # secure stuff
            elif self.secureci.getcurrvalue():
                self.logger.log(LogPriority.DEBUG, "The secure POP/IMAP option is set. Checking if POP/IMAP is secured...")
                if not self.reqprots:
                    self.detailedresults += '\nRequired protocols were not specified. Cannot securely configure POP/IMAP without them.'
                    self.compliant = False
                slist = self.reqprots.split()
                if slist:
                    for prot in slist:
                        if prot.strip() != '' and prot.strip() not in self.protocollist:
                            self.detailedresults += "\nRule is not compliant because: The specified protocol: " + str(prot) + " is not a valid protocol."
                            self.compliant = False
                else:
                    self.detailedresults += '\nUnable to read protocol list. Please use a space-delimited list when specifying your required protocols.'
                self.logger.log(LogPriority.DEBUG, "The secure POP/IMAP option is set. Checking if POP/IMAP is secured...")
                if self.checkpkgs(True):
                    self.logger.log(LogPriority.DEBUG, "Required packages are installed.")
                    self.logger.log(LogPriority.DEBUG, " Checking file configuration...")
                    if not self.checkconfig():
                        self.compliant = False
                    self.logger.log(LogPriority.DEBUG, "File configuration check finished.")
                else:
                    self.logger.log(LogPriority.DEBUG, "One or more required packages are not installed.")
                    self.compliant = False
                    self.detailedresults += "\nRule is not compliant because one or more of the required packages are not installed."

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

    def checkinitobjs(self):
        '''validate each class property and object to be used in this class before it is used
        return True if each object is properly assigned/initialized
        return False if not


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            if not self.pkgh:
                retval = False
                self.logger.log(LogPriority.DEBUG, "The package helper object was not initialized correctly")
            if not self.svch:
                retval = False
                self.logger.log(LogPriority.DEBUG, "The service helper object was not initialized correctly")
            if not self.cmdh:
                retval = False
                self.logger.log(LogPriority.DEBUG, "The command helper object was not initialized correctly")
            if not self.osdetected:
                retval = False
                self.logger.log(LogPriority.DEBUG, "Unable to determine OS type or package manager")
            if not self.servicename:
                retval = False
                self.logger.log(LogPriority.DEBUG, "servicename variable was not set")
            if not self.pkgdict:
                retval = False
                self.logger.log(LogPriority.DEBUG, "pgkdict variable was not set")
            if not self.confpathdict:
                retval = False
                self.logger.log(LogPriority.DEBUG, "confpathdict variable was not set")

        except AttributeError:
            retval = False
            self.logger.log(LogPriority.DEBUG, "One or more of the class variables are undefined")
            return retval
        return retval

    def checksvc(self):
        '''check to see if the dovecot service is enabled or running
        return False if it is either running or enabled
        if the service is neither running nor enabled, return True


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True
        enabled = False
        running = False

        try:

            if self.svch.auditService(self.servicename, _="_"):
                enabled = True
                self.detailedresults += '\nThe ' + str(self.servicename) + ' service is still enabled'
            if enabled:
                self.detailedresults += "\nThere are service(s) which need to be disabled"
            if self.svch.isRunning(self.servicename, _="_"):
                running = False
                self.detailedresults += '\nThe ' + str(self.servicename) + ' service is still running'
            if running:
                self.detailedresults += "\nThere are service(s) which need to be stopped"

            if enabled | running:
                retval = False

        except Exception:
            raise
        return retval

    def checkpkgs(self, desired):
        '''check compliance of packages portion of rule
        
        if desired, check if all required packages are installed:
            True if yes, False if no
        
        if not desired, check if any packages are installed:
            True if no, False if yes

        :param desired: 
        :returns: retval
        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            if not desired:
                for pkg in self.pkgdict:
                    if self.pkgh.check(pkg):
                        self.pkgdict[pkg] = True
                        retval = False
            else:
                for pkg in self.pkgdict:
                    if not self.pkgh.check(pkg):
                        self.pkgdict[pkg] = False
                        retval = False

        except Exception:
            raise
        return retval

    def checkconfig(self):
        '''verify configuration state of file(s)


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True
        contents = []

        try:

            for path in self.confpathdict:
                contents = self.getFileContents(path)
                if contents:
                    for confitem in self.confpathdict[path]:
                        if not self.searchContents(str(self.confpathdict[path][confitem]), contents):
                            retval = False
                            self.detailedresults += "\nRequired configuration option: " + str(self.confpathdict[path][confitem]) + " was not found in file: " + str(path)

                else:
                    self.logger.log(LogPriority.DEBUG, "Unable to check contents of file: " + str(path))

                if retval:
                    self.detailedresults += "\nAll required configuration options have been found in file: " + str(path)

            if retval:
                self.detailedresults += "\nAll required configuration options have been found in all required configuration files"

        except Exception:
            raise
        return retval

    def fix(self):
        '''run each fix action and get the success results of each one
        return True if all fix actions succeeded
        return False if not


        :returns: fixsuccess

        :rtype: bool
@author: Breen Malmberg

        '''

        fixsuccess = True
        self.detailedresults = ''
        self.iditerator = 0

        try:

            if self.disableci.getcurrvalue():
                self.detailedresults += '\nYou have selected the DisablePOPIMAP option. It will now be disabled/removed from this system.'
                if not self.turnoffsvc():
                    fixsuccess = False
                if not self.removePackages():
                    fixsuccess = False
            elif self.secureci.getcurrvalue():
                self.detailedresults += '\nYou have selected the SecurePOPIMAP option. It will now be installed (if it is not already installed) and then securely configured.'
                if not self.installPackages():
                    fixsuccess = False
                if not self.configurefiles():
                    fixsuccess = False

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

    def turnoffsvc(self):
        '''disable dovecot service if it is enabled
        return True if dovecot service was successfully disabled or is not enabled
        return False if otherwise


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        self.logger.log(LogPriority.DEBUG, "Attempting to disable service: " + str(self.servicename))

        retval = True

        try:

            self.svch.disableService(self.servicename, _="_")

            if self.svch.auditService(self.servicename, _="_"):
                retval = False
                self.logger.log(LogPriority.DEBUG, "Service is still enabled after executing disableservice!")

            if self.svch.isRunning(self.servicename, _="_"):
                retval = False
                self.logger.log(LogPriority.DEBUG, "Service is still running after executing disableservice!")

            if retval:
                self.logger.log(LogPriority.DEBUG, "Successfully disabled service: " + str(self.servicename))
            else:
                self.logger.log(LogPriority.DEBUG, "Failed to disable service: " + str(self.servicename))

        except Exception:
            raise
        return retval

    def removePackages(self):
        '''Remove all packages in self.pkgdict, which are currently installed
        return True if all installed packages were successfully removed
        return False if not


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            for pkg in self.pkgdict:
                if self.pkgdict[pkg]:
                    if not self.pkgh.remove(pkg):
                        retval = False
                        self.detailedresults += '\nFailed to remove package: ' + str(pkg)
                    else:
                        self.pkgdict[pkg] = False

            if not retval:
                self.logger.log(LogPriority.DEBUG, "Failed to remove packages")
            else:
                self.logger.log(LogPriority.DEBUG, "Successfully removed all packages")

        except Exception:
            raise
        return retval

    def configurefiles(self):
        '''set the correct configuration options within the dovecot configuration file(s)


        :returns: void
        @author: Breen Malmberg

        '''

        try:

            for path in self.confpathdict:
                contents = self.getFileContents(path)
                self.fixContents(self.confpathdict[path], path, contents)

        except Exception:
            raise

    def installPackages(self):
        '''install all necessary dovecot packages


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        self.logger.log(LogPriority.DEBUG, "Attempting to install all necessary packages...")

        try:

            for pkg in self.pkgdict:
                if not self.pkgdict[pkg]:
                    if not self.pkgh.install(pkg):
                        retval = False
                        self.detailedresults += '\nFailed to install package ' + str(pkg)
                        self.logger.log(LogPriority.DEBUG, "Failed to install package: " + str(pkg))

            if not retval:
                self.logger.log(LogPriority.DEBUG, "Failed to install one or more required packages. Please check your connection to the network and ensure that your package repositories are correctly configured.")
            else:
                self.logger.log(LogPriority.DEBUG, "Successfully installed all required packages")

        except Exception:
            raise
        return retval
Ejemplo n.º 7
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
Ejemplo n.º 8
0
class InstallBanners(RuleKVEditor):
    '''Install and configure warning banners, to be displayed at startup.'''
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """

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

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

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

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

        try:

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

        except Exception:
            raise

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


        '''

        self.gnome2 = True
        self.gnome2bannertext = GDMWARNINGBANNER

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

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

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

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

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

        try:

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

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

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

            self.ch.executeCommand(cmd2)

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

        except Exception:
            raise
        return retval

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


        '''

        self.gnome3 = True
        self.gnome3bannertext = GDM3WARNINGBANNER

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


        '''

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

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

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

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


        '''

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

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


        '''

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

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


        '''

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

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

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

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


        '''

        self.mac = True
        self.bannertext = WARNINGBANNER

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

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

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


        '''

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

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


        '''

        try:

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

                self.setlinuxcommon()

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

        except Exception:
            raise

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

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

        '''

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

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

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

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

        '''

        retval = True
        tmpPath = filepath + ".stonixtmp"

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

        try:

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

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

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

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

        except Exception:
            retval = False
            raise
        return retval

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

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

        '''

        retval = True
        appends = []

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

        try:

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

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

        except Exception:
            self.rulesuccess = False
            raise
        return retval

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

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

        '''

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

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

        self.setFileContents(filepath, contents)

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

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

        '''

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

        try:

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

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

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

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

@author: Breen Malmberg

        '''

        retval = True

        try:

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

        except Exception:
            raise
        return retval

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


        :returns: self.compliant

        :rtype: boolean

@author Breen Malmberg

        '''

        self.compliant = True
        self.detailedresults = ""

        try:

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

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

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


        :returns: g3ver

        :rtype: float
@author: Breen Malmberg

        '''

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

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

        compliant = True

        try:

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

            if not self.reportcommon():
                compliant = False

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

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

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

        except Exception:
            raise
        return compliant

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

        compliant = True

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

        return compliant

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

        compliant = True

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

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

        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

        try:

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

        except Exception:
            raise
        return retval

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

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

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

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


        :returns: success

        :rtype: boolean

@author Breen Malmberg

        '''

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

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

        try:

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

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean

@author: Breen Malmberg

        '''

        success = True

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

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

        try:

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

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

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

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

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

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

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

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

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True
        contentlines = []

        try:

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

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


        :returns: success

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

        '''

        success = True

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

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

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:
            if not RuleKVEditor.fix(self, True):
                success = False
            if not self.setFileContents(self.ftpwelcomefile, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.ftpwelcomefile)
            if not self.setFileContents(self.policybanner, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.policybanner)
        except Exception:
            raise
        return success
Ejemplo n.º 9
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
Ejemplo n.º 10
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
Ejemplo n.º 11
0
class ConfigureNetworkTime(Rule):
    """The ConfigureNetworkTime class specifies network time servers and enables the appropriate service"""
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 96
        self.rulename = 'ConfigureNetworkTime'
        self.formatDetailedResults("initialize")
        self.logger = logger
        self.sethelptext()
        self.mandatory = True
        self.rootrequired = True
        self.guidance = [
            'CIS', 'NSA(3.10.2)', 'CCE-4134-3', 'CCE-4385-1', 'CCE-4032-9',
            'CCE-4424-8', 'CCE-3487-6'
        ]

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

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

        self.libc = getLibc()

    def _set_paths(self):
        """

        """

        self.time_package = "chrony"

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

    def _test_connection(self, hostname):
        """

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

        reachable = False

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

        return reachable

    def _report_install(self):
        """

        :return: installed
        :rtype: bool
        """

        installed = True

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

        return installed

    def _report_conf(self):
        """

        :return: configured
        :rtype: bool
        """

        configured = True

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

        tmpfile = self.time_conf_file + ".stonixtmp"

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

        return configured

    def _report_linux(self):
        """

        :return: compliant
        :rtype: bool
        """

        self._set_paths()

        compliant = True

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

        return compliant

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

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

        self.detailedresults = ""

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

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

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

        self.compliant = True

        try:

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

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

        return self.compliant

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


        :return: configured
        :rtype:
        """

        # defaults
        configured = True
        usingnetworktime = False
        timeserverfound = False

        try:

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

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

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

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

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

        except Exception:
            raise
        return configured

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

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

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

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

        try:

            if self.ci.getcurrvalue():

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

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

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

    def _fix_linux(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

        return success

    def _fix_install(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

        return success

    def _fix_conf(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

        return success

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

        parseoutput1 = []
        parseoutput2 = []
        fixresult = True

        try:

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

            try:

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

            except KeyError:
                originaltimestate = "off"

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

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

            try:

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

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

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

        except:
            raise

        return fixresult
Ejemplo n.º 12
0
class ConfigureProcessAccounting(Rule):
    '''Class docs'''
    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 = 97
        self.rulename = "ConfigureProcessAccounting"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.guidance = ["CCE-RHEL7-CCE-TBD 3.2.15"]
        self.applicable = {"type": "white", "family": ["linux"]}

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

        self.ph = Pkghelper(self.logger, self.environ)
        self.sh = ServiceHelper(self.environ, self.logger)

    def report(self):
        '''


        :returns: self.compliant

        :rtype: bool
@author: Eric Ball
@change: Breen Malmberg - 04/09/2019 - doc string added; method refactor;
        added debug logging

        '''

        self.compliant = True
        self.detailedresults = ""
        self.packages = ["psacct", "acct"]

        try:

            if not any(self.ph.check(p) for p in self.packages):
                self.compliant = False
                self.detailedresults += "\nsystem accounting package is not installed"

            if not any(self.sh.auditService(p) for p in self.packages):
                self.compliant = False
                self.detailedresults += "\nsystem accounting service is not enabled"

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

    def fix(self):
        '''


        :returns: self.rulesuccess

        :rtype: bool
@author: Eric Ball
@change: Breen Malmberg - 04/09/2019 - doc string added; method refactor;
        added debug logging

        '''

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

        try:

            if not self.ci.getcurrvalue():
                self.rulesuccess = False
                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)

            for p in self.packages:
                if self.ph.install(p):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "pkghelper",
                        "pkgname": p,
                        "startstate": "removed",
                        "endstate": "installed"
                    }
                    self.statechglogger.recordchgevent(myid, event)
            if self.iditerator == 0:
                self.rulesuccess = False
                self.detailedresults += "\nFailed to install system accounting package"

            for p in self.packages:
                if self.ph.check(p):
                    self.sh.enableService(p)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "servicehelper",
                        "servicename": p,
                        "startstate": "disabled",
                        "endstate": "enabled"
                    }
                    self.statechglogger.recordchgevent(myid, event)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Ejemplo n.º 13
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
Ejemplo n.º 14
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
Ejemplo n.º 15
0
    def undo(self):
        """
        Undo changes made by this rule. Some rules may not be undone.
        self.rulesuccess will be updated if the rule does not succeed.

        @author D. Kennel & D. Walker
        """
        # pass
        if not self.environ.geteuid() == 0:
            self.detailedresults = "Root access required to revert changes."
            self.formatDetailedResults("undo", None, self.detailedresults)
        try:
# This must be implemented later once the parameter option or observable
# pattern for taking control of self.detailedresults refresh is implemented
# see artf30937 : self.detailedresults through application flow for details
            self.detailedresults = ""
            undosuccessful = True
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            if not eventlist:
                self.formatDetailedResults("undo", None, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return undosuccessful
            #eventlist.reverse()
            for entry in eventlist:
                try:
                    event = self.statechglogger.getchgevent(entry)
                    if event["eventtype"] == "perm":
                        perms = event["startstate"]
                        os.chmod(event["filepath"], perms[2])
                        os.chown(event["filepath"], perms[0], perms[1])

                    elif event["eventtype"] == "conf":
                        self.statechglogger.revertfilechanges(event["filepath"],
                                                              entry)

                    elif event["eventtype"] == "comm" or \
                         event["eventtype"] == "commandstring":
                        ch = CommandHelper(self.logdispatch)
                        command = event["command"]
                        ch.executeCommand(command)
                        if ch.getReturnCode() != 0:
                            self.detailedresults = "Couldn't run the " + \
                                "command to undo\n"
                            self.logdispatch.log(LogPriority.DEBUG,
                                                 self.detailedresults)

                    elif event["eventtype"] == "creation":
                        try:
                            os.remove(event["filepath"])
                        except OSError as oser:
                            if oser.errno == 21:
                                try:
                                    os.rmdir(event["filepath"])
                                except OSError as oserr:
                                    if oserr.errno == 39:
                                        self.logdispatch.log(LogPriority.DEBUG, "Cannot remove file path: " + str(event["filepath"]) + " because it is a non-empty directory.")
                            elif oser.errno == 2:
                                self.logdispatch.log(LogPriority.DEBUG, "Cannot remove file path: " + str(event["filepath"]) + " because it does not exist.")

                    elif event["eventtype"] == "deletion":
                        self.statechglogger.revertfiledelete(event["filepath"])

                    elif event["eventtype"] == "pkghelper":
                        ph = Pkghelper(self.logdispatch, self.environ)
                        if event["startstate"] == "installed":
                            ph.install(event["pkgname"])
                        elif event["startstate"] == "removed":
                            ph.remove(event["pkgname"])
                        else:
                            self.detailedresults = 'Invalid startstate for ' \
                                + 'eventtype "pkghelper". startstate should ' \
                                + 'either be "installed" or "removed"\n'
                            self.logdispatch.log(LogPriority.ERROR,
                                                 self.detailedresults)

                    elif event["eventtype"] == "servicehelper":
                        sh = ServiceHelper(self.environ, self.logdispatch)
                        if event["startstate"] == "enabled":
                            sh.enableservice(event["servicename"])
                        elif event["startstate"] == "disabled":
                            sh.disableservice(event["servicename"])
                        else:
                            self.detailedresults = 'Invalid startstate for ' \
                                + 'eventtype "servicehelper". startstate ' + \
                                'should either be "enabled" or "disabled"\n'
                            self.logdispatch.log(LogPriority.ERROR,
                                                 self.detailedresults)
                except(IndexError, KeyError):
                    self.detailedresults = "EventID " + entry + " not found"
                    self.logdispatch.log(LogPriority.DEBUG,
                                         self.detailedresults)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            undosuccessful = False
            self.detailedresults = traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, [self.rulename + ".undo",
                                                     self.detailedresults])
        self.formatDetailedResults("undo", undosuccessful,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return undosuccessful
Ejemplo n.º 16
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
Ejemplo n.º 17
0
    def undo(self):
        """
        Undo changes made by this rule. Some rules may not be undone.
        self.rulesuccess will be updated if the rule does not succeed.

        @author D. Kennel & D. Walker
        """
        # pass
        if not self.environ.geteuid() == 0:
            self.detailedresults = "Root access required to revert changes."
            self.formatDetailedResults("undo", None, self.detailedresults)
        try:
            # This must be implemented later once the parameter option or observable
            # pattern for taking control of self.detailedresults refresh is implemented
            # see artf30937 : self.detailedresults through application flow for details
            self.detailedresults = ""
            undosuccessful = True
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            if not eventlist:
                self.formatDetailedResults("undo", None, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return undosuccessful
            #eventlist.reverse()
            for entry in eventlist:
                try:
                    event = self.statechglogger.getchgevent(entry)
                    if event["eventtype"] == "perm":
                        perms = event["startstate"]
                        os.chmod(event["filepath"], perms[2])
                        os.chown(event["filepath"], perms[0], perms[1])

                    elif event["eventtype"] == "conf":
                        self.statechglogger.revertfilechanges(
                            event["filepath"], entry)

                    elif event["eventtype"] == "comm" or \
                         event["eventtype"] == "commandstring":
                        ch = CommandHelper(self.logdispatch)
                        command = event["command"]
                        ch.executeCommand(command)
                        if ch.getReturnCode() != 0:
                            self.detailedresults = "Couldn't run the " + \
                                "command to undo\n"
                            self.logdispatch.log(LogPriority.DEBUG,
                                                 self.detailedresults)

                    elif event["eventtype"] == "creation":
                        try:
                            os.remove(event["filepath"])
                        except OSError as oser:
                            if oser.errno == 21:
                                try:
                                    os.rmdir(event["filepath"])
                                except OSError as oserr:
                                    if oserr.errno == 39:
                                        self.logdispatch.log(
                                            LogPriority.DEBUG,
                                            "Cannot remove file path: " +
                                            str(event["filepath"]) +
                                            " because it is a non-empty directory."
                                        )
                            elif oser.errno == 2:
                                self.logdispatch.log(
                                    LogPriority.DEBUG,
                                    "Cannot remove file path: " +
                                    str(event["filepath"]) +
                                    " because it does not exist.")

                    elif event["eventtype"] == "deletion":
                        self.statechglogger.revertfiledelete(event["filepath"])

                    elif event["eventtype"] == "pkghelper":
                        ph = Pkghelper(self.logdispatch, self.environ)
                        if event["startstate"] == "installed":
                            ph.install(event["pkgname"])
                        elif event["startstate"] == "removed":
                            ph.remove(event["pkgname"])
                        else:
                            self.detailedresults = 'Invalid startstate for ' \
                                + 'eventtype "pkghelper". startstate should ' \
                                + 'either be "installed" or "removed"\n'
                            self.logdispatch.log(LogPriority.ERROR,
                                                 self.detailedresults)

                    elif event["eventtype"] == "servicehelper":
                        sh = ServiceHelper(self.environ, self.logdispatch)
                        if event["startstate"] == "enabled":
                            sh.enableservice(event["servicename"])
                        elif event["startstate"] == "disabled":
                            sh.disableservice(event["servicename"])
                        else:
                            self.detailedresults = 'Invalid startstate for ' \
                                + 'eventtype "servicehelper". startstate ' + \
                                'should either be "enabled" or "disabled"\n'
                            self.logdispatch.log(LogPriority.ERROR,
                                                 self.detailedresults)
                except (IndexError, KeyError):
                    self.detailedresults = "EventID " + entry + " not found"
                    self.logdispatch.log(LogPriority.DEBUG,
                                         self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            undosuccessful = False
            self.detailedresults = traceback.format_exc()
            self.logdispatch.log(
                LogPriority.ERROR,
                [self.rulename + ".undo", self.detailedresults])
        self.formatDetailedResults("undo", undosuccessful,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return undosuccessful
Ejemplo n.º 18
0
class EnablePAEandNX(Rule):
    '''Install PAE Kernel on Supported 32-bit x86 Systems. If the system is 32-bit and also supports the PAE
    and NX features, the kernel-PAE package should be installed to enable XD or NX support.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        # set up constructor and class variables
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.logger = logger
        self.environ = environ
        self.statechglogger = statechglogger
        self.rulenumber = 87
        self.rulename = "EnablePAEandNX"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.guidance = ["CCE-RHEL7-CCE-TBD 2.2.4.4.1"]
        self.sethelptext()
        self.applicable = {'type': 'white', 'family': 'linux'}

        # set up CI
        datatype = "bool"
        key = "ENABLEPAEANDNX"
        instructions = "If you want to prevent this rule from running, set the value of EnablePAEandNX to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.initobjs()

    def initobjs(self):
        '''initialize helper objects
        @author: Breen Malmberg


        '''

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

    def report(self):
        '''Run report actions for EnablePAEandNX


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        # set default variables for this method
        systemOS = ""
        systemARCH = 0
        self.detailedresults = ""
        self.compliant = True
        self.package = ""

        try:

            # get value of other variables to be used in this method
            systemOS = self.getSystemOS()
            systemARCH = self.getSystemARCH()
            self.package = self.getSysPackage(systemOS)

            # check if required utility exists; log warning if not
            if not os.path.exists("/proc/cpuinfo"):
                self.logger.log(
                    LogPriority.WARNING,
                    "Unable to verify presence of required system utility /proc/cpuinfo"
                )

            # check for presence of pae cpu flag as well as pae kernel package
            if not self.checkPAE(self.package):
                self.compliant = False

            # check for presence of nx cpu flag
            if not self.checkNX():
                self.compliant = False

            # if system architecture is not 32-bit, disregard previous; inform user
            if systemARCH == 64:
                self.compliant = True
                self.detailedresults = "This system is 64-bit and this rule only applies to 32-bit systems."

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

    def getSystemOS(self):
        '''return the name of the OS


        :returns: osname

        :rtype: string
@author: Breen Malmberg

        '''

        return self.environ.getostype()

    def getSystemARCH(self):
        '''return the architecture of the system (32 or 64 bit)


        :returns: sysARCH

        :rtype: int
@author: Breen Malmberg

        '''

        # set default variables for this method
        sysARCH = 0
        command = ["uname", "-a"]

        try:

            self.ch.executeCommand(command)
            output = self.ch.getOutput()

            # check if the system is 64-bit or not
            for line in output:
                if re.search("x86_64", line):
                    sysARCH = 64

            # if x86_64 flag not found, set system arch as 32-bit
            if sysARCH == 0:
                sysARCH = 32

        except Exception:
            raise

        return sysARCH

    def getSysPackage(self, systemos):
        '''return name of pae kernel package for this specific OS

        :param systemos: string The name of the system's operating system
        @author: Breen Malmberg
        :returns: packagename
        :rtype: string

        '''

        packagename = ""

        self.logger.log(LogPriority.DEBUG,
                        "Getting system-specific package name...")

        if not systemos:
            self.logger.log(
                LogPriority.DEBUG,
                "Required parameter: systemos was passed as blank")
        if not isinstance(systemos, str):
            self.logger.log(
                LogPriority.DEBUG,
                "Required parameter: systemos was passed as incorrect data type. Required data type: string"
            )
        else:
            self.logger.log(LogPriority.DEBUG,
                            "System info was: " + str(systemos))

        # set default variables for this method
        defaultpackagename = "kernel-PAE"
        syspkgdict = {
            "redhat": "kernel-PAE",
            "red hat": "kernel-PAE",
            "centos": "kernel-PAE",
            "cent os": "kernel-PAE",
            "fedora": "kernel-PAE",
            "debian": "linux-image-686-pae",
            "ubuntu": "linux-generic-pae",
            "opensuse": "kernel-pae",
            "suse": "kernel-pae"
        }

        try:

            self.logger.log(LogPriority.DEBUG, "Determining system os name...")

            for opsys in syspkgdict:
                if re.search(opsys, systemos.lower()):
                    self.logger.log(LogPriority.DEBUG,
                                    "System os name is: " + str(opsys))
                    packagename = syspkgdict[opsys]
                    self.logger.log(
                        LogPriority.DEBUG,
                        "System-specific package name is: " + str(packagename))

        except KeyError:
            # if systemos does not exist in syspkgdict, log debug and use default packagename
            packagename = defaultpackagename
            self.logger.log(
                LogPriority.DEBUG,
                "Unable to determine system-specific package name. Defaulting to "
                + str(defaultpackagename))
        except Exception:
            raise

        if not packagename:
            packagename = defaultpackagename

        return packagename

    def checkPAE(self, package):
        '''check for the presence of the kernel-PAE package as well as the CPU pae flag

        :param package: string The name of the kernel PAE package as it appears to this specific system's OS
        @author: Breen Malmberg
        :returns: retval
        :rtype: bool

        '''

        # set default variables for this method
        retval = True
        paeflag = False
        paepkg = False
        command = "cat /proc/cpuinfo | grep flags"
        osname = ""
        osver = ""
        checkpkg = True

        try:

            osname = self.environ.getostype()
            osver = self.environ.getosver()

            # do not check for existence of a pae package
            # on ubuntu 16 32 bit because it is built into
            # the default kernel and as a result there is no
            # separate pae kernel package to install
            if re.search("Ubuntu", osname, re.IGNORECASE):
                if re.search("16\.", osver, re.IGNORECASE):
                    checkpkg = False
                    paepkg = True

            if not package:
                self.detailedresults += "\nNo package was specified. No package check will be performed. Assuming: not installed."
                self.logger.log(LogPriority.DEBUG,
                                "Required parameter: package was empty")
            elif not isinstance(package, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "Required parameter: package was not passed as the correct data type. Type required: string"
                )
                self.detailedresults += "\nNo package was specified. No package check will be performed. Assuming: not installed."
            else:
                if checkpkg:
                    # check for presence of kernel PAE package
                    if self.pkg.check(package):
                        paepkg = True

            self.ch.executeCommand(command)
            output = self.ch.getOutput()

            # check for presence of CPU pae flag
            for line in output:
                if re.search("pae", line):
                    paeflag = True

            # let the user know what is wrong
            if not paepkg:
                self.detailedresults += "\nThe kernel pae package is not installed."
            if not paeflag:
                self.detailedresults += "\nThe pae CPU flag was not found."

            retval = bool(paeflag and paepkg)

        except Exception:
            raise

        return retval

    def checkNX(self):
        '''check for the presence of the CPU nx flag


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        # set default variables for this method
        retval = False
        command = "cat /proc/cpuinfo | grep flags"

        try:

            self.ch.executeCommand(command)
            output = self.ch.getOutput()

            # check for presence of CPU nx flag
            for line in output:
                if re.search("nx", line):
                    retval = True

            # let the user know what is wrong
            if not retval:
                self.detailedresults += "\nThe CPU nx flag was not found."

        except Exception:
            raise

        return retval

    def fix(self):
        '''Run fix actions for EnablePAEandNX


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

        success = True
        self.detailedresults = ""

        try:

            # only run fix actions if the system is 32-bit and the CI is enabled
            if self.ci.getcurrvalue():
                if self.getSystemARCH() == 32:

                    # attempt to install the kernel pae package; inform user if this fails
                    if not self.pkg.install(self.package):
                        success = False
                        self.detailedresults += "\nUnable to install package: " + str(
                            self.package)

                else:
                    # inform the user if the fix actions do not apply because the system is 64-bit
                    self.detailedresults += "\nThis rule only applies to 32-bit systems. This system is 64-bit."

            else:
                # inform the user if the fix actions will not be run because the CI was not enabled
                self.detailedresults += "\nCI for this rule not enabled. Nothing was done."

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