示例#1
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
示例#2
0
class DisableCamera(Rule):

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 150
        self.rulename = "DisableCamera"
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.rulesuccess = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = ["CIS 1.2.6"]
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.logger = logger
        self.iditerator = 0
        datatype = 'bool'
        key = 'DISABLECAMERA'
        instructions = "To disable the built-in iSight camera, set the value of DISABLECAMERA to True."
        default = False
        self.ci = self.initCi(datatype, key, instructions, default)
        self.setvars()

    def setvars(self):
        self.camprofile = ""
        baseconfigpath = "/Applications/stonix4mac.app/Contents/" + \
                             "Resources/stonix.app/Contents/MacOS/" + \
                             "stonix_resources/files/"
        self.camprofile = baseconfigpath + "stonix4macCameraDisablement.mobileconfig"
        # basetestpath = "/Users/username/stonix/src/stonix_resources/files/"
        # self.camprofile = basetestpath + "stonix4macCameraDisablement.mobileconfig"
        if not os.path.exists(self.camprofile):
            self.logger.log(LogPriority.DEBUG, "Could not locate appropriate camera disablement profile\n")
            self.camprofile = ""

    def report(self):
        '''check for the existence of the AppleCameraInterface driver in the
        output of kexstat. Report non-compliant if found. Report compliant
        if not found.
        :returns: self.compliant
        :rtype: bool
        @author: Breen Malmberg
        @change: dwalker - ??? - ???
        @change: Breen Malmberg - 1/19/2017 - minor doc string edit; minor refactor
        @change: dwalker 10/3/2017 updated to check for a profile value
        '''
        try:
            self.detailedresults = ""
            self.compliant = True
            if not self.camprofile:
                self.detailedresults += "Could not locate the appropriate camera disablement profile for your system.\n"
                self.compliant = False
                self.formatDetailedResults("report", self.compliant, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.compliant
            if os.path.exists(self.camprofile):
                cameradict = {"com.apple.applicationaccess": {"allowCamera": {"val": "0",
                                                                              "type": "bool",
                                                                              "accept": "",
                                                                              "result": False}}}
                self.cameditor = KVEditorStonix(self.statechglogger, self.logger,
                                                "profiles", self.camprofile, "",
                                                cameradict, "", "")
                if not self.cameditor.report():
                    self.detailedresults += "iSight camera is not disabled\n"
                    self.compliant = False
            else:
                self.detailedresults += self.camprofile + " doesn't exist\n"
                self.compliant = False
            self.detailedresults += "Due to a Mac OS issue, having multiple camera profiles " + \
                                    "installed with conflicting values may allow any camera, " + \
                                    "internal or external to still function. If a rule is coming " + \
                                    "back compliant but your camera is still working, check your " + \
                                    "installed profiles and remove any other restriction based profiles.\n"
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                                   " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        '''run kextunload on the AppleCameraInterface driver to
        unload it and disable the iSight camera.
        return True if the command succeeds. return False if
        the command fails.
        :returns: success
        :rtype: bool
        @author: Breen Malmberg
        @change: dwalker - ??? - ???
        @change: Breen Malmberg - 1/19/2017 - minor doc string edit; minor refactor
        @change: dwalker 10/3/2017 updated to check for a profile value
        '''

        try:
            success = True
            self.detailedresults = ""
            # only run the fix actions if the CI has been enabled
            if not self.ci.getcurrvalue():
                self.detailedresults += "Configuration item was not enabled\n"
                self.rulesuccess = False
                self.formatDetailedResults("report", self.rulesuccess, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.rulesuccess
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            if not self.cameditor.report():
                if self.cameditor.fix():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.cameditor.setEventID(myid)
                    if not self.cameditor.commit():
                        success = False
                        self.detailedresults += "Unable to install profile\n"
                        self.logdispatch.log(LogPriority.DEBUG, "Kveditor commit failed")
                else:
                    success = False
                    self.detailedresults += "Unable to install profile\n"
                    self.logdispatch.log(LogPriority.DEBUG, "Kveditor fix failed")
            else:
                self.detailedresults += "Camera disablement profile was already installed.\n"
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                                   " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", success, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
示例#3
0
    def fix_sysctl(self):
        '''set the systemd configuration setting fs.suid_dumpable
        to 0


        :returns: success

        :rtype: bool
@author: ???

        '''

        success = True

        # manually writing key and value to /etc/sysctl.conf
        sysctl = "/etc/sysctl.conf"
        created = False
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                success = False
                debug = "Unable to create " + sysctl + "\n"
                self.logger.log(LogPriority.DEBUG, debug)
        if os.path.exists(sysctl):
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
            tmpfile = sysctl + ".tmp"
            editor = KVEditorStonix(self.statechglogger, self.logger, "conf",
                                    sysctl, tmpfile, {"fs.suid_dumpable": "0"},
                                    "present", "openeq")
            if not editor.report():
                if editor.fixables:
                    # If we did not create the file, set an event ID for the
                    # KVEditor's undo event
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        editor.setEventID(myid)
                    if not editor.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not editor.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                        if not setPerms(sysctl, [0, 0, 0o644], self.logger):
                            self.detailedresults += "Could not set permissions on " + \
                                                    self.path + "\n"
                            success = False
                    resetsecon(sysctl)

        # using sysctl -w command
        self.logger.log(LogPriority.DEBUG,
                        "Configuring /etc/sysctl fs.suid_dumpable directive")
        self.ch.executeCommand("/sbin/sysctl -w fs.suid_dumpable=0")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            self.detailedresults += "Failed to set core dumps variable suid_dumpable to 0\n"
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)
        else:
            self.logger.log(LogPriority.DEBUG,
                            "Re-reading sysctl configuration from files")
            self.ch.executeCommand("/sbin/sysctl -q -e -p")
            retcode2 = self.ch.getReturnCode()
            if retcode2 != 0:
                success = False
                self.detailedresults += "Failed to load new sysctl configuration from config file\n"
                errmsg2 = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errmsg2)
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                command = "/sbin/sysctl -w fs.suid_dumpable=1"
                event = {"eventtype": "commandstring", "command": command}
                self.statechglogger.recordchgevent(myid, event)
        return success
示例#4
0
    def fixBootMode(self):
        success = True
        if self.initver == "systemd":
            cmd = ["/bin/systemctl", "set-default",
                   "multi-user.target"]
            if not self.ch.executeCommand(cmd):
                success = False
                self.detailedresults += '"systemctl set-default ' \
                    + 'multi-user.target" did not succeed\n'
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                commandstring = "/bin/systemctl set-default " + \
                                "graphical.target"
                event = {"eventtype": "commandstring",
                         "command": commandstring}
                self.statechglogger.recordchgevent(myid, event)

        elif self.initver == "debian":
            dmlist = ["gdm", "gdm3", "lightdm", "xdm", "kdm"]
            for dm in dmlist:
                cmd = ["update-rc.d", "-f", dm, "disable"]
                if not self.ch.executeCommand(cmd):
                    self.detailedresults += "Failed to disable desktop " + \
                        "manager " + dm
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "servicehelper",
                             "servicename": dm,
                             "startstate": "enabled",
                             "endstate": "disabled"}
                    self.statechglogger.recordchgevent(myid, event)

        elif self.initver == "ubuntu":
            ldmover = "/etc/init/lightdm.override"
            tmpfile = ldmover + ".tmp"
            created = False
            if not os.path.exists(ldmover):
                createFile(ldmover, self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": ldmover}
                self.statechglogger.recordchgevent(myid, event)
                created = True
            writeFile(tmpfile, "manual\n", self.logger)
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": ldmover}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange(ldmover, tmpfile, myid)
            os.rename(tmpfile, ldmover)
            resetsecon(ldmover)

            grub = "/etc/default/grub"
            if not os.path.exists(grub):
                createFile(grub, self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": grub}
                self.statechglogger.recordchgevent(myid, event)
            tmppath = grub + ".tmp"
            data = {"GRUB_CMDLINE_LINUX_DEFAULT": '"quiet"'}
            editor = KVEditorStonix(self.statechglogger, self.logger,
                                    "conf", grub, tmppath, data,
                                    "present", "closedeq")
            editor.report()
            if editor.fixables:
                if editor.fix():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    editor.setEventID(myid)
                    debug = "kveditor fix ran successfully\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    if editor.commit():
                        debug = "kveditor commit ran successfully\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    else:
                        error = "kveditor commit did not run " + \
                                "successfully\n"
                        self.logger.log(LogPriority.ERROR, error)
                        success = False
                else:
                    error = "kveditor fix did not run successfully\n"
                    self.logger.log(LogPriority.ERROR, error)
                    success = False
            cmd = "update-grub"
            self.ch.executeCommand(cmd)

        else:
            inittab = "/etc/inittab"
            tmpfile = inittab + ".tmp"
            if os.path.exists(inittab):
                initText = open(inittab, "r").read()
                initre = r"id:\d:initdefault:"
                if re.search(initre, initText):
                    initText = re.sub(initre, "id:3:initdefault:",
                                      initText)
                    writeFile(tmpfile, initText, self.logger)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": inittab}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(inittab,
                                                         tmpfile, myid)
                    os.rename(tmpfile, inittab)
                    resetsecon(inittab)
                else:
                    initText += "\nid:3:initdefault:\n"
                    writeFile(tmpfile, initText, self.logger)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": inittab}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(inittab,
                                                         tmpfile, myid)
                    os.rename(tmpfile, inittab)
                    resetsecon(inittab)
            else:
                self.detailedresults += inittab + " not found, no other " + \
                    "init system found. If you are using a supported " + \
                    "Linux OS, please report this as a bug\n"
        return success
示例#5
0
class BootSecurity(Rule):
    """The Boot Security rule configures the system to run a job at system boot
    time that handles turning off potential vulnerability points such as: wifi,
    bluetooth, microphones, and cameras.

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

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

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

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

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

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

        self.set_paths()

    def set_paths(self):
        """

        """

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

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

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

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

        compliant = True

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

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

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

        try:

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

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

        except:
            raise

        return compliant

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

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

        compliant = True

        try:

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

        except:
            raise

        return compliant

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

        :return:
        """

        compliant = True

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

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

        try:

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

        except:
            raise

        return compliant

    def report_boot_fips(self):
        """

        :return:
        """

        found_fips = False
        compliant = True

        try:

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

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

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

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

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

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

        except:
            raise

        return compliant

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

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

        self.detailedresults = ""
        self.compliant = True

        try:

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

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

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

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

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

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

        return self.compliant

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

        """

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

        try:

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

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

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

        except:
            raise

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

        """

        success = True

        try:

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

        except:
            raise

        return success

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

        """

        success = True

        try:

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

        except:
            raise

        return success

    def fix_boot_fips(self):
        """

        :return:
        """

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

        try:

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

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

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

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

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

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

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

                self.ch.executeCommand(self.grub_updater_cmd)

        except:
            raise

        return success

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

        :return:
        """

        success = True

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

        tmpfile = self.grub_file + ".stonixtmp"

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

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

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

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

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

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

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

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

        return success

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

        :return: luks_encrypted
        :rtype: bool
        """

        luks_encrypted = False

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

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

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

        try:

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

        except:
            raise

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

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

        return luks_encrypted

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

        :return:
        """

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

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

        try:

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

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

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

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

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

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

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

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

        except:
            raise

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

        :return: success
        :rtype: bool
        """

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

        try:

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

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

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

        except:
            raise

        return success

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

        """

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

        try:

            if self.bootci.getcurrvalue():

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

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

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

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

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

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

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

        return self.rulesuccess
示例#6
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
示例#7
0
class ShellTimeout(Rule):

    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 120
        self.rulename = "ShellTimeout"
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.sethelptext()
        datatype = "bool"
        key = "SHELLTIMEOUT"
        instructions = "To disable this rule set the value of " + \
                       "SHELLTIMEOUT to False."
        default = False
        self.ci = self.initCi(datatype, key, instructions, default)

        self.guidance = ["NSA 2.3.5.5", "CCE 3689-7", "CCE 3707-7"]
        self.applicable = {"type": "white",
                           "family": ["linux"]}
        self.iditerator = 0
        self.created = False

    def report(self):
        try:
            self.path1 = "/etc/profile.d/tmout.sh"
            self.data1 = {"TMOUT": "900"}
            self.data2 = {"readonly": "TMOUT", "export": "TMOUT"}
            self.path2 = "/etc/profile.d/autologout.csh"
            self.cshData = "set -r autologout 15"
            compliant = True
            self.detailedresults = ""

            if os.path.exists(self.path1):
                # Shell scripts in profile.d do not require +x, so they can
                # be either 0755 (0755) or 0644 (0644)
                if not checkPerms(self.path1, [0, 0, 0o755], self.logger) and \
                   not checkPerms(self.path1, [0, 0, 0o644], self.logger):
                    compliant = False
                    self.detailedresults += self.path1 + \
                        " permissions incorrect\n"
                self.tmppath1 = self.path1 + ".tmp"
                self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.path1,
                                              self.tmppath1, self.data1,
                                              "present", "closedeq")
                kveReport1 = self.editor1.report()
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.path1,
                                              self.tmppath1, self.data2,
                                              "present", "space")
                kveReport2 = self.editor2.report()
                if not kveReport1 or not kveReport2:
                    compliant = False
                    self.detailedresults += self.path1 + \
                        " does not contain the correct values\n"
            else:
                compliant = False
                self.detailedresults += self.path1 + " does not exist\n"

            if os.path.exists(self.path2):
                if not checkPerms(self.path2, [0, 0, 0o755], self.logger) and \
                   not checkPerms(self.path2, [0, 0, 0o644], self.logger):
                    compliant = False
                    self.detailedresults += self.path2 + \
                        " permissions incorrect\n"
                cshText = open(self.path2, "r").read()
                if not re.search(self.cshData, cshText):
                    compliant = False
                    self.detailedresults += self.path2 + \
                        " does not contain the correct values\n"
            else:
                compliant = False
                self.detailedresults += self.path2 + " does not exist\n"

            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):
        try:
            if not self.ci.getcurrvalue():
                self.detailedresults += "CI not enabled\n"
            else:
                success = True
                self.detailedresults = ""
                self.iditerator = 0
                eventlist = self.statechglogger.findrulechanges(self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)

                if not os.path.exists(self.path1):
                    createFile(self.path1, self.logger)
                    self.created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation",
                             "filepath": self.path1}
                    self.statechglogger.recordchgevent(myid, event)

                self.tmppath = self.path1 + ".tmp"
                self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.path1, self.tmppath,
                                              self.data1, "present",
                                              "closedeq")
                self.editor1.report()
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", self.path1, self.tmppath,
                                              self.data2, "present", "space")
                self.editor2.report()

                if self.editor1.fixables or self.editor2.fixables:
                    if self.editor1.fix():
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                        if self.editor1.commit():
                            debug = self.path1 + "'s contents have been " + \
                                "corrected\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            resetsecon(self.path1)
                        else:
                            debug = "kveditor commit not successful\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                            self.detailedresults += self.path1 + \
                                " properties could not be set\n"
                    else:
                        debug = "kveditor fix not successful\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                        self.detailedresults += self.path1 + \
                            " properties could not be set\n"
                    if self.editor2.fix():
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor2.setEventID(myid)
                        if self.editor2.commit():
                            debug = self.path1 + "'s contents have been " + \
                                "corrected\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            resetsecon(self.path1)
                        else:
                            debug = "kveditor commit not successful\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                            self.detailedresults += self.path1 + \
                                " properties could not be set\n"
                    else:
                        debug = "kveditor fix not successful\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                        self.detailedresults += self.path1 + \
                            " properties could not be set\n"
                if not checkPerms(self.path1, [0, 0, 0o755], self.logger) and \
                   not checkPerms(self.path1, [0, 0, 0o644], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.path1, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
                        self.detailedresults += "Could not set permissions " + \
                            "for " + self.path1 + "\n"

                if not os.path.exists(self.path2):
                    createFile(self.path2, self.logger)
                    self.created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation",
                             "filepath": self.path2}
                    self.statechglogger.recordchgevent(myid, event)
                writeFile(self.path2, self.cshData, self.logger)
                if not checkPerms(self.path2, [0, 0, 0o755], self.logger) and \
                   not checkPerms(self.path2, [0, 0, 0o644], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.path2, [0, 0, 0o644],
                                    self.logger, self.statechglogger, myid):
                        success = False
                        self.detailedresults += "Could not set permissions " + \
                            "for " + self.path2 + "\n"

                self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
示例#8
0
class DisableIPV6(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 123
        self.rulename = "DisableIPV6"
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 2.5.3.1"]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

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

        self.iditerator = 0
        self.created = False
        self.created2 = False
        # self.editor1: sysctl file editor
        # self.editor2: network file editor
        # self.editor3: sshd file editor
        self.editor1, self.editor2, self.editor3 = "", "", ""
        self.sh = ServiceHelper(self.environ, self.logger)
        self.sethelptext()

    def report(self):
        try:
            self.detailedresults = ""
            if self.environ.getosfamily() == "linux":
                self.ph = Pkghelper(self.logger, self.environ)
                self.compliant = self.reportLinux()
            elif self.environ.getosfamily() == "freebsd":
                self.compliant = self.reportFree()
            elif self.environ.getostype() == "Mac OS X":
                self.compliant = self.reportMac()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

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

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

            if self.environ.getosfamily() == "linux":
                self.rulesuccess = self.fixLinux()
            elif self.environ.getosfamily() == "freebsd":
                self.rulesuccess = self.fixFree()
            elif self.environ.getosfamily() == "darwin":
                self.rulesuccess = self.fixMac()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def reportMac(self):
        self.ifaces = []
        compliant = True
        self.cmdhelper = CommandHelper(self.logger)
        cmd = ["/usr/sbin/networksetup", "-listallnetworkservices"]
        if not self.cmdhelper.executeCommand(cmd):
            self.detailedresults += "Unable to run " + \
                "networksetup -listallnetworkservices command\n"
            return False
        output = self.cmdhelper.getOutput()
        for item in output:
            item = item.strip()
            cmd = ["/usr/sbin/networksetup", "-getinfo", item]
            if not self.cmdhelper.executeCommand(cmd):
                self.detailedresults += "Unable to run " + \
                    "networksetup -getinfo command\n"
                return False
            output2 = self.cmdhelper.getOutput()
            for item2 in output2:
                if re.search("^IPv6:", item2):
                    check = item2.split(":")
                    if check[1].strip() != "Off":
                        self.detailedresults += "IPV6 is not turned off " + \
                            "for " + item + " interface\n"
                        self.ifaces.append(item)
                        compliant = False
        return compliant

    def fixMac(self):
        if self.ifaces:
            for item in self.ifaces:
                cmd = ["/usr/sbin/networksetup", "setv6off", item]
                if not self.cmdhelper.executeCommand(cmd):
                    self.rulesuccess = False
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        'eventtype':
                        'comm',
                        'command':
                        ['/usr/sbin/networksetup', 'setv6automatic', item]
                    }
                    self.statechglogger.recordchgevent(myid, event)
        return self.rulesuccess

    def reportFree(self):
        compliant = True
        self.editor1, self.editor2 = "", ""

        directives1 = {
            "ipv6_network_interfaces": "none",
            "ipv6_activate_all_interfaces": "NO",
            "ip6addrctl_enable": "NO",
            "ip6addrctl_policy": "NO"
        }
        directives2 = {
            "net.ipv6.conf.all.disable_ipv6": "1",
            "net.ipv6.conf.default.disable_ipv6": "1",
            "net.ipv6.conf.lo.disable_ipv6": "1"
        }

        path1 = "/etc/rc.conf"
        path2 = "/etc/sysctl.conf"
        tmpfile1 = "/etc/rc.conf.tmp"
        tmpfile2 = "/etc/sysctl.conf.tmp"

        # try and create /etc/rc.conf if doesn't exist
        if not os.path.exists(path1):
            if not createFile(path1, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path1 + ", so this file will not be configured, " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 420], self.logger):
                compliant = False
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path1, tmpfile1, directives1,
                                          "present", "closedeq")
            if not self.editor1.report():
                compliant = False

        # try and create /etc/sysctl.conf if doesn't exist
        if not os.path.exists(path2):
            if not createFile(path2, self.logger):
                compliant = False
                self.detailedresults += "Unable to create the file: " + \
                    path2 + " so this file will not be configured " + \
                    "resulting in failed compliance\n"

        if os.path.exists(path2):
            if not checkPerms(path2, [0, 0, 384], self.logger):
                compliant = False
            self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", path2, tmpfile2, directives2,
                                          "present", "closedeq")
            if not self.editor2.report():
                compliant = False
        else:
            compliant = False

        cmdhelper = CommandHelper(self.logger)
        cmd = ["/sbin/ifconfig", "-a"]
        if not cmdhelper.executeCommand(cmd):
            return False
        output = cmdhelper.getOutput()
        for line in output:
            line = line.strip()
            if re.search("^nd6", line):
                if not re.search("(.)*IFDISABLED(.)*", line):
                    compliant = False
        return compliant

    def fixFree(self):
        # debug messages are used for developers, self.detailedresults
        # are used for the users information
        path1 = "/etc/rc.conf"
        path2 = "/etc/sysctl.conf"
        success = True
        debug = ""
        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 420], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(self.path, [0, 0, 420], self.logger,
                                self.statechglogger, myid):
                    success = False
            if self.editor1:
                if self.editor1.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        debug += "Kveditor unable to correct file: " + \
                            path1 + "\n"
                        self.detailedresults += "Unable to correct " + path1 + \
                            "\n"
                        success = False
                    elif not self.editor1.commit():
                        self.detailedresults += "Unable to correct " + path1 + \
                            "\n"
                        debug += "commit for kveditor1 was not successful\n"
                        success = False
            else:
                debug += "Editor2 was never created so path didn't exist \
and/or wasn't able to be created\n"

                success = False
        else:
            self.detailedresults += path1 + " doesn't exist!\n"
            debug += path1 + " doesn't exist, unble to fix file\n"
            success = False

        if os.path.exists(path2):
            # check permissions on /etc/sysctl.conf
            if not checkPerms(path2, [0, 0, 384], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)

                # set permissions if wrong
                if not setPerms(self.path, [0, 0, 384, self.logger],
                                self.statechglogger, myid):
                    success = False
            # check if editor is present
            if self.editor2:
                if self.editor2.fixables():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        debug += "Kveditor unable to correct file: " + \
                            path2 + "\n"
                        self.detailedresults += "Unable to correct " + path2 + \
                            "\n"
                        success = False
                    elif not self.editor2.commit():
                        self.detailedresults += "Unable to correct " + path2 + \
                            "\n"
                        debug += "commit for kveditor2 was not successful\n"
                        success = False
            else:
                debug += "Editor2 was never created so path didn't exist \
and/or wasn't able to be created\n"

                success = False
        else:
            self.detailedresults += path2 + " doesn't exist!\n"
            debug += path2 + " doesn't exist, unble to fix file\n"
            success = False

        # restart the network
        ch = CommandHelper(self.logger)
        cmd = ["/etc/rc.d/netif", "restart"]
        if not ch.executeCommand(cmd):
            self.detaileresults += "Unable to restart network\n"
            success = False
        return success

    def reportLinux(self):
        self.ch = CommandHelper(self.logger)
        compliant = True
        netwrkfile = ""
        ifacefile = ""
        self.modprobefiles = []
        #self.modprobeOK = False
        self.modprobeOK = True
        sysctl = "/etc/sysctl.conf"
        self.interface = {"IPV6INIT": "no", "NETWORKING_IPV6": "no"}
        self.sysctls = {
            "net.ipv6.conf.all.disable_ipv6": "1",
            "net.ipv6.conf.default.disable_ipv6": "1"
        }
        self.modprobes = {
            "options": ["ipv6 disable=1"],
            "install": ["ipv6 /bin/true"]
        }

        # stig portion, check netconfig file for correct contents
        if self.ph.manager == "apt-get":
            nfspkg = "nfs-common"
        else:
            nfspkg = "nfs-utils.x86_64"
        if self.ph.check(nfspkg):
            if os.path.exists("/etc/netconfig"):
                item1 = "udp6 tpi_clts v inet6 udp - -"
                item2 = "tcp6 tpi_cots_ord v inet6 tcp - -"
                contents = readFile("/etc/netconfig", self.logger)
                for line in contents:
                    line = re.sub("\s+", " ", line.strip())
                    if re.search(item1, line) or re.search(item2, line):
                        self.detailedresults += "/etc/netconfig file contains " + \
                            "lines we don't want present\n"
                        compliant = False

        # "ifconfig" has been deprecated on Debian9 and some otherd distros
        # so use "ip addr" instead
        # Here we check if the system is giving out ipv6 ip addresses
        if os.path.exists("/sbin/ifconfig"):
            cmd = ["/sbin/ifconfig"]
        else:
            cmd = ["/sbin/ip", "addr"]

        if not self.ch.executeCommand(cmd):
            compliant = False
        else:
            output = self.ch.getOutput()
            for line in output:
                if re.search("^inet6", line.strip()):
                    self.detailedresults += "inet6 exists in the " + \
                        "ifconfig output\n"
                    compliant = False
                    break

        # check for ipv6 address in hostname file
        if os.path.exists("/etc/hosts"):
            contents = readFile("/etc/hosts", self.logger)
            for line in contents:
                if re.search("^#", line) or re.match("^\s*$", line):
                    continue
                if re.search(":", line):
                    compliant = False

        # check compliancy of /etc/sysctl.conf file
        if not os.path.exists(sysctl):
            self.detailedresults += "File " + sysctl + " does not exist\n"
            compliant = False
        else:
            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile,
                                          self.sysctls, "present", "openeq")
            if not self.editor1.report():
                self.detailedresults += "/etc/sysctl file doesn't contain \
                    the correct contents\n"

                compliant = False
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions for " + sysctl + \
                    "are incorrect\n"
                compliant = False

        # in addition to checking /etc/sysctl.conf contents we need to
        # also check sysctl compliancy using the sysctl command
        for key in self.sysctls:
            self.ch.executeCommand("/sbin/sysctl " + key)
            retcode = self.ch.getReturnCode()
            output = self.ch.getOutputString()
            errmsg = output + self.ch.getErrorString()
            if retcode != 0:
                if re.search("unknown key", errmsg):
                    continue
                else:
                    self.detailedresults += "Failed to get value " + key + " using sysctl command\n"
                    errmsg = self.ch.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errmsg)
                    compliant = False
            else:
                actualoutput = output.strip()
                expectedoutput = key + " = " + self.sysctls[key]
                if actualoutput != expectedoutput:
                    compliant = False
                    self.detailedresults += "\nsysctl output has " + \
                                            "incorrect value: expected " + \
                                            expectedoutput + ", found " + \
                                            actualoutput + "\n"
        # check files inside modprobe.d directory for correct contents
        if os.path.exists("/etc/modprobe.d/"):
            modprobefiles = glob.glob("/etc/modprobe.d/*")
            for modfile in modprobefiles:
                tmpfile = ""
                modprobekveditor = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", modfile,
                                                  tmpfile, self.modprobes,
                                                  "present", "space")
                if modprobekveditor.report():
                    self.modprobeOK = True
                    break
            if not self.modprobeOK:
                self.detailedresults += "Didn't find desired contents inside files " + \
                    "within /etc/modprobe.d/"
                compliant = False
        else:
            # system isn't using loadable kernel modules, not an issue
            self.modprobeOK = True

        # Check for existence of interface and network files to be configured
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            if not os.path.exists(ifacefile):
                ifacefile = ""
            netwrkfile = "/etc/sysconfig/network"
            if not os.path.exists(netwrkfile):
                netwrkfile = ""
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
            if not os.path.exists(ifacefile):
                ifacefile = ""

        # go through interface directory and check each interface file
        # for correct contents
        if ifacefile:
            dirs = glob.glob(ifacefile + '*')
            for loc in dirs:
                contents = []
                if re.search('^' + ifacefile + 'ifcfg', loc):
                    if not checkPerms(loc, [0, 0, 0o644], self.logger):
                        compliant = False
                    contents = readFile(loc, self.logger)
                    if contents:
                        for key in self.interface:
                            found = False
                            for line in contents:
                                if re.search("^#", line) or re.match(
                                        "^\s*$", line):
                                    continue
                                if re.search("^" + key, line):
                                    if re.search("=", line):
                                        temp = line.split("=")
                                        if temp[1].strip(
                                        ) == self.interface[key]:
                                            found = True
                                            continue
                                        else:
                                            found = False
                                            break
                                    else:
                                        compliant = False
                                        self.detailedresults += loc + \
                                            " file in bad format\n"
                            if not found:
                                self.detailedresults += "contents of " + \
                                    loc + " file is wrong\n"
                                compliant = False
                                break
                            else:
                                continue
                    else:
                        compliant = False

        # check network file for correct contents
        if netwrkfile:
            if os.path.exists(netwrkfile):
                if not checkPerms(netwrkfile, [0, 0, 0o644], self.logger):
                    compliant = False
                tmpfile = netwrkfile + ".tmp"
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", netwrkfile, tmpfile,
                                              self.interface, "present",
                                              "closedeq")
                if not self.editor2.report():
                    self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"

                    compliant = False
            else:
                self.detailedresults += netwrkfile + " doesn't exist\n"
                compliant = False

        # This subpart is only for apt-get based systems
        # sshd needs the inet directive for ipv6 disablement
        if self.ph.manager == "apt-get":
            data = {"AddressFamily": "inet"}
            kvtype = "conf"
            path = "/etc/ssh/sshd_config"
            tmpPath = path + ".tmp"
            intent = "present"
            configtype = "space"
            self.editor3 = KVEditorStonix(self.statechglogger, self.logger,
                                          kvtype, path, tmpPath, data, intent,
                                          configtype)
            if not self.editor3.report():
                self.detailedresults += "/etc/ssh/ssdh_config doesn't \
contain the correct contents\n"

                compliant = False
        return compliant

    def fixLinux(self):
        '''
        @change: dkennel removed extraneous arg from setperms call on 864
        '''
        universal = "#The following lines were added by stonix\n"
        debug = ""
        success = True
        ifacefile = ""
        netwrkfile = ""
        sysctl = "/etc/sysctl.conf"
        blacklistfile = "/etc/modprobe.d/stonix-blacklist.conf"

        # STIG portion, correct netconfig file
        if self.ph.manager == "apt-get":
            nfspkg = "nfs-common"
        else:
            nfspkg = "nfs-utils.x86_64"
        # if package not installed, no need to configure it
        if self.ph.check(nfspkg):
            if os.path.exists("/etc/netconfig"):
                filestring = ""
                # we want to make sure the following two lines don't
                # appear in the netconfig file
                item1 = "udp6 tpi_clts v inet6 udp - -"
                item2 = "tcp6 tpi_cots_ord v inet6 tcp - -"
                contents = readFile("/etc/netconfig", self.logger)
                for line in contents:
                    templine = re.sub("\s+", " ", line.strip())
                    # if we find the lines, skip them thus leaving them out of
                    # of the rewrite
                    if re.search(item1, templine) or re.search(
                            item2, templine):
                        continue
                    else:
                        filestring += line
                tmpfile = "/etc/netconfig.tmp"
                if not writeFile(tmpfile, filestring, self.logger):
                    success = False
                else:
                    # record event, rename file, set perms
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": "/etc/netconfig"}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(
                        "/etc/netconfig", tmpfile, myid)
                    os.rename(tmpfile, "/etc/netconfig")
                    os.chown("/etc/netconfig", 0, 0)
                    os.chmod("/etc/netconfig", 420)
                    resetsecon("/etc/netconfig")

        # remove any ipv6 addresses from /etc/hosts file
        if os.path.exists("/etc/hosts"):
            contents = readFile("/etc/hosts", self.logger)
            tempstring = ""
            tmpfile = "/etc/hosts.tmp"
            for line in contents:
                if re.search("^#", line) or re.match("^\s*$", line):
                    tempstring += line
                    continue
                elif re.search(":", line):
                    tempstring += "#" + line
                else:
                    tempstring += line
            if writeFile(tmpfile, tempstring, self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": "/etc/hosts"}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange("/etc/hosts", tmpfile,
                                                     myid)
                os.rename(tmpfile, "/etc/hosts")
                os.chown("/etc/hosts", 0, 0)
                os.chmod("/etc/hosts", 420)
                resetsecon("/etc/hosts")
            else:
                success = False
                debug = "Unable to write to file /etc/hosts\n"
                self.logger.log(LogPriority.DEBUG, debug)

        # fix sysctl / tuning kernel parameters
        # manually write key value pairs to /etc/sysctl.conf
        created = False
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                success = False
                debug = "Unable to create " + sysctl + "\n"
                self.logger.log(LogPriority.DEBUG, debug)
        if os.path.exists(sysctl):
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False

            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile,
                                          self.sysctls, "present", "openeq")
            if not self.editor1.report():
                if self.editor1.fixables:
                    # If we did not create the file, set an event ID for the
                    # KVEditor's undo event
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor1.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                        if not setPerms(self.path, [0, 0, 0o644], self.logger):
                            self.detailedresults += "Could not set permissions on " + \
                                                    self.path + "\n"
                            success = False
                    resetsecon(sysctl)

        # here we also check the output of the sysctl command for each key
        # to cover all bases
        for key in self.sysctls:
            if self.ch.executeCommand("/sbin/sysctl " + key):
                output = self.ch.getOutputString().strip()
                errmsg = output + self.ch.getErrorString()
                if re.search("unknown key", errmsg):
                    continue
                if not re.search(self.sysctls[key] + "$", output):
                    undovalue = output[-1]
                    self.ch.executeCommand("/sbin/sysctl -q -e -w " + key +
                                           "=" + self.sysctls[key])
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        success = False
                        self.detailedresults += "Failed to set " + key + " = " + self.sysctls[
                            key] + "\n"
                        errmsg = self.ch.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errmsg)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        command = "/sbin/sysctl -q -e -w " + key + "=" + undovalue
                        event = {
                            "eventtype": "commandstring",
                            "command": command
                        }
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Unable to get value for " + key + "\n"
                success = False
        # at the end do a print and ignore any key errors to ensure
        # the new values are read into the kernel
        self.ch.executeCommand("/sbin/sysctl -q -e -p")
        retcode2 = self.ch.getReturnCode()
        if retcode2 != 0:
            success = False
            self.detailedresults += "Failed to load new sysctl configuration from config file\n"
            errmsg2 = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg2)

        # We never found the correct contents in any of the modprobe.d files
        # so we're going to created the stonix-blacklist file
        # this file is used in other rules
        if not self.modprobeOK:
            created = False
            tmpfile = blacklistfile + ".tmp"
            modprobekveditor = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", blacklistfile, tmpfile,
                                              self.modprobes, "notpresent",
                                              "space")
            if not os.path.exists(blacklistfile):
                # create the file and record the event as file creation
                if createFile(blacklistfile, self.logger):
                    created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "creation",
                        "filepath": blacklistfile
                    }
                    self.statechglogger.recordchgevent(myid, event)
            if os.path.exists(blacklistfile):
                if not modprobekveditor.report():
                    if not modprobekveditor.fix():
                        success = False
                        self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"
                    else:
                        # if the file was created, then we already recorded an event
                        # for that, so this step would get skipped
                        if not created:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            modprobekveditor.setEventID(myid)
                        if not modprobekveditor.commit():
                            success = False
                            self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"

        # fix ifcfg (interface) files
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            netwrkfile = "/etc/sysconfig/network"
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
        if ifacefile:
            if os.path.exists(ifacefile):
                dirs = glob.glob(ifacefile + "*")
                if dirs:
                    for loc in dirs:
                        interface = {"IPV6INIT": "no", "NETWORKING_IPV6": "no"}
                        interface2 = {
                            "IPV6INIT": "no",
                            "NETWORKING_IPV6": "no"
                        }
                        found = False
                        tempstring = ""
                        if re.search('^' + ifacefile + 'ifcfg', loc):
                            filename = loc
                            tmpfile = filename + ".tmp"
                            contents = readFile(filename, self.logger)
                            if not checkPerms(filename, [0, 0, 420],
                                              self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                if not setPerms(filename, [0, 0, 420],
                                                self.logger,
                                                self.statechglogger, myid):
                                    debug = "Unable to set permissions on " + \
                                        filename + "\n"
                                    self.logger.log(LogPriority.DEBUG, debug)
                                    success = False
                            for key in interface:
                                found = False
                                for line in contents:
                                    if re.search("^#", line) or \
                                       re.match("^\s*$", line):
                                        continue
                                    if re.search("^" + key, line):
                                        if re.search("=", line):
                                            temp = line.split("=")
                                            if temp[1].strip(
                                            ) == interface[key]:
                                                if found:
                                                    continue
                                                found = True
                                            else:
                                                contents.remove(line)
                                if found:
                                    del interface2[key]
                            for line in contents:
                                tempstring += line
                            tempstring += universal
                            for key in interface2:
                                tempstring += key + "=" + interface2[key] + \
                                    "\n"
                            if not writeFile(tmpfile, tempstring, self.logger):
                                success = False
                                debug = "Unable to write to file " + loc + "\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {'eventtype': 'conf', 'filepath': filename}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(
                                filename, tmpfile, myid)
                            os.rename(tmpfile, filename)
                            os.chown(filename, 0, 0)
                            os.chmod(filename, 420)
                            resetsecon(filename)
            elif not os.path.exists(ifacefile) and ifacefile != "":
                # will not attempt to create the interface files
                debug = "interface directory which holds interface \
                files, doesn't exist, stonix will not attempt to make this \
                directory or the files contained therein"

                success = False
                self.logger.log(LogPriority.DEBUG, debug)

        # fix network file if it exists
        if netwrkfile:
            if not os.path.exists(netwrkfile):
                if not createFile(netwrkfile, self.logger):
                    debug = "Unable to create " + netwrkfile + "file\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
                else:
                    if not checkPerms(netwrkfile, [0, 0, 420], self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(netwrkfile, [0, 0, 420], self.logger,
                                        self.statechglogger, myid):
                            debug = "Unable to set permissions on " + \
                                    netwrkfile + "\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                    tmpfile = netwrkfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf",
                                                  netwrkfile, tmpfile,
                                                  self.interface, "present",
                                                  "closedeq")
                    if not self.editor2.report():
                        self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"

            if self.editor2:
                if self.editor2.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor2.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    os.chown(netwrkfile, 0, 0)
                    os.chmod(netwrkfile, 420)
                    resetsecon(netwrkfile)

        # fix sshd_config file for apt-get systems if ssh is installed
        if self.ph.manager == "apt-get":
            if not os.path.exists("/etc/ssh/sshd_config"):
                msg = "/etc/ssh/ssd_config doesn\'t exist.  This could mean ssh \
    is not installed or the file has been inadvertantly deleted.  Due to the \
    complexity of this file stonix will not attempt to create this file"

                self.logger.log(LogPriority.DEBUG, msg)
                success = False
            else:
                if not checkPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                  self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                    self.logger, self.statechglogger, myid):
                        success = False
                        debug = "Unable to set permissions on " + \
                            "/etc/ssh/sshd_config\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                if self.editor3:
                    if self.editor3.fixables:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor3.setEventID(myid)
                        if not self.editor3.fix():
                            success = False
                            debug = "Unable to complete kveditor fix method" + \
                                "for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        elif not self.editor3.commit():
                            success = False
                            debug = "Unable to complete kveditor commit " + \
                                "method for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        os.chown("/etc/ssh/sshd_config", 0, 0)
                        os.chmod("/etc/ssh/sshd_config", 420)
                        resetsecon("/etc/ssh/sshd_config")
        return success
示例#9
0
class SecureSNMP(Rule):
    '''classdocs'''

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 144
        self.rulename = 'SecureSNMP'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = ['NSA 3.20', 'CCE 4540-1']
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        datatype = 'bool'
        key = 'DISABLESNMP'
        instructions = "If there is a mission-critical need for hosts at" + \
                       "this site to be remotely monitored by a SNMP " + \
                       "tool, then prevent the disabling and removal " + \
                       "of SNMP by setting the value of DisableSNMP " + \
                       "to False."
        default = True
        self.disablesnmp = self.initCi(datatype, key, instructions, default)

        datatype2 = 'bool'
        key2 = 'CONFIGURESNMP'
        instructions2 = "To configure SNMP on this system, make sure " + \
                        "you have the value for DisableSNMP set to " + \
                        "False, and set the value of ConfigureSNMP to True."
        default2 = True
        self.configuresnmp = self.initCi(datatype2, key2, instructions2,
                                         default2)

        self.snmpdconflocations = ['/etc/snmp/conf/snmpd.conf',
                                   '/etc/snmp/conf/snmp.conf',
                                   '/etc/snmp/snmpd.conf',
                                   '/etc/snmp/snmp.conf']
        self.snmpv3directives = {'defContext': 'none',
                                 'defVersion': '3',
                                 'defAuthType': 'SHA',
                                 'defSecurityLevel': 'authNoPriv'}
# add any other possible snmp configuration file paths from the environment
# variable SNMPCONFPATH
# .get does not throw keyerror but instead returns None if env doesn't exist
        snmpconfpathstring = os.environ.get('SNMPCONFPATH')

        if snmpconfpathstring:
            snmpconfpathlist = snmpconfpathstring.split()
            for path in snmpconfpathlist:
                path = str(path).strip()
                self.snmpdconflocations.append(path)

    def report(self):
        '''Determine which report method(s) to run and run them


        :returns: bool
        @author bemalmbe

        '''

        # defaults
        self.detailedresults = ""

        try:

            if self.environ.getostype() == 'Mac OS X':
                self.compliant = self.reportmac()
                self.formatDetailedResults("report", self.compliant,
                                           self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.compliant
            compliant = True
            self.svchelper = ServiceHelper(self.environ, self.logger)
            self.pkghelper = Pkghelper(self.logger, self.environ)

            if self.disablesnmp.getcurrvalue():
                if not self.reportDisableSNMP():
                    compliant = False

            if self.configuresnmp.getcurrvalue():
                if not self.reportConfigureSNMP():
                    compliant = False

            self.compliant = compliant

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

    def reportmac(self):
        '''@author: Breen Malmberg'''

        configured = True

        try:

            self.cmdhelper = CommandHelper(self.logger)
            filepath = '/System/Library/LaunchDaemons/org.net-snmp.snmpd.plist'
            if not os.path.exists(filepath):
                configured = False
                self.detailedresults += '\nrequired plist configuration file not found: ' + filepath
            cmd = '/usr/bin/defaults read ' + filepath + ' Disabled'
            self.cmdhelper.executeCommand(cmd)
            output = self.cmdhelper.getOutputString()
            errout = self.cmdhelper.getErrorString()
            if errout:
                configured = False
                self.detailedresults += '\nunable to execute defaults read command, or \"Disabled\" key does not exist'
            else:
                if not re.search('^1', output):
                    configured = False
                    self.detailedresults += '\nsnmpd is not yet disabled'

        except Exception:
            raise
        return configured

    def reportDisableSNMP(self):
        '''Determine whether SNMP service is disabled and uninstalled


        :returns: bool
        @author bemalmbe

        '''

        # defaults
        secure = False
        svcenabled = False
        pkginstalled = False

        try:

            svcenabled = self.svchelper.auditService('snmpd', _="_")

            pkginstalled = self.pkghelper.check('net-snmpd')

            if not svcenabled and not pkginstalled:
                secure = True

            return secure

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

    def reportConfigureSNMP(self):
        '''Determine whether the SNMP service is securely configured


        :returns: bool
        @author bemalmbe

        '''

        # defaults
        kvintent = 'present'
        kvconftype = 'conf'
        kvtype = 'space'
        secure = True

        # check to make sure perms on conf files are 640
        # check to make sure ownership of conf files is root:root
        # check to make sure conf files are not using weak or default community
        # string and that they are not using anything other than version 3
        # security model as per NSA guidance

        try:

            if self.reportDisableSNMP():
                return True

            for location in self.snmpdconflocations:
                if os.path.exists(location):
                    perms = getOctalPerms(location)
                    if perms != 640:
                        secure = False

                    kvpath = location
                    kvtmppath = location + '.stonixtmp'

                    self.kvosnmp = KVEditorStonix(self.statechglogger,
                                                  self.logger, kvtype, kvpath,
                                                  kvtmppath,
                                                  self.snmpv3directives,
                                                  kvintent, kvconftype)

                    kvosnmpretval = self.kvosnmp.report()
                    if not kvosnmpretval:
                        secure = False

                    f = open(location, 'r')
                    contentlines = f.readlines()
                    f.close()

                    for line in contentlines:
                        if re.search('^group', line):
                            line = line.split()
                            if line[2] in ['v1', 'v2', 'v2c']:
                                secure = False
                                self.detailedresults += '''You are currently using an outdated security model for your SNMP configuration. Please update to model 3.'''

                    for line in contentlines:
                        if re.search('^com2sec', line):
                            line = line.split()
                            if line[3] in ['public', 'community']:
                                secure = False
                                self.detailedresults += '''You are currently using a default or weak community string.'''

                    for line in contentlines:
                        if re.search('^access', line):
                            line = line.split()
                            if line[3] in ['any', 'v1', 'v2', 'v2c']:
                                secure = False
                                self.detailedresults += '''You are currently using an outdated security model for your SNMP configuration. Please update to model 3.'''

                            if line[4] == 'noauth':
                                secure = False
                                self.detailedresults += '''You are currently not requiring authentication for SNMP. This is an unsecure practice. Please change to authNoPriv or authPriv.'''

            return secure

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

    def fix(self):
        '''Determine which fix method(s) to run and run them
        
        @author bemalmbe


        '''

        self.detailedresults = ""
        self.rulesuccess = True

        try:

            if self.environ.getostype() == 'Mac OS X':
                if self.disablesnmp.getcurrvalue() or self.configuresnmp.getcurrvalue():
                    self.rulesuccess = self.fixmac()
                    self.formatDetailedResults("fix", self.rulesuccess,
                                               self.detailedresults)
                    self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                    return self.rulesuccess

            if self.disablesnmp.getcurrvalue():
                self.fixDisableSNMP()

            if self.configuresnmp.getcurrvalue():
                self.fixConfigureSNMP()

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

    def fixmac(self):
        '''@author: Breen Malmberg'''

        success = True

        try:

            defaults = '/usr/bin/defaults '
            operation = 'write '
            filepath = '/System/Library/LaunchDaemons/org.net-snmp.snmpd.plist '
            key = 'DISABLED'
            val = ' -bool true'

            cmd = defaults + operation + filepath + key + val

            self.cmdhelper.executeCommand(cmd)
            errout = self.cmdhelper.getErrorString()
            if errout:
                success = False
                self.detailedresults += '\ncould not set Disabled key to true in org.net-snmp.snmpd.plist'

        except Exception:
            raise
        return success

    def fixDisableSNMP(self):
        '''Disable the SNMP service and uninstall the package for it
        
        @author bemalmbe


        '''

        try:

            if not self.reportDisableSNMP():

                self.svchelper.disableService('snmpd', _="_")

                self.pkghelper.remove('net-snmpd')

        except AttributeError:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            raise

    def fixConfigureSNMP(self):
        '''Securely configure the SNMP service. This option should be used instead
        of disabling SNMP only if there is a mission-critical need for the
        SNMP service to operate in the environment.
        
        @author bemalmbe


        '''

# set auth type to SHA, security model version to 3, and security level to
# authNoPriv set permissions for the SNMP conf file to 640
# change owner and group of the SNMP conf file to root and root
# admin must set up security on version 3 themselves because it is
# account-based security and they must set up their own account(s)

        try:

            myid = '0144001'

            self.kvosnmp.setEventID(myid)

            self.kvosnmp.fix()
            self.kvosnmp.commit()

            for location in self.snmpdconflocations:
                if os.path.exists(location):
                    os.chmod(location, 0o640)
                    os.chown(location, 0, 0)

        except (KeyError, OSError):
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            raise
示例#10
0
class SecureIPV6(Rule):

    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 124
        self.rulename = "SecureIPV6"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        datatype = "bool"
        key = "SECUREIPV6"
        instructions = '''To disable this rule set the value of SECUREIPV6 to \
False.'''
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.guidance = ["NSA 2.5.3.2", "CCE 4269-7", "CCE 4291-1",
                         "CCE 4313-3", "CCE 4198-8", "CCE 3842-2",
                         "CCE 4221-8", "CCE 4137-6", "CCE 4159-0",
                         "CCE 3895-0", "CCE 4287-9", "CCE 4058-4",
                         "CCE 4128-5"]
        self.applicable = {'type': 'white',
                           'family': ['linux'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.iditerator = 0
        # self.editor1: sysctl file editor
        # self.editor2: network file editor
        self.editor1, self.editor2 = "", ""
        self.ch = CommandHelper(self.logger)

    def report(self):
        try:
            self.detailedresults = ""
            if self.environ.getosfamily() == "linux":
                self.compliant = self.reportLinux()
            if self.environ.getosfamily() == "freebsd":
                self.compliant = self.reportFree()
            if self.environ.getosfamily() == "darwin":
                self.compliant = self.reportMac()
            elif self.environ.getosfamily() == "solaris":
                self.compliant = self.reportSol()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant
###############################################################################
    def reportMac(self):
        '''check the values of the directives, specified in self.directives
        check that self.path (/private/etc/sysctl.conf) exists
        check that the permissions and ownership on file sysctl.conf
        are 0o600 and 0,0


        :returns: compliant

        :rtype: bool
@author: dwalker
@change: Breen Malmberg - 1/10/2017 - added doc string; try/except;
        fixed perms for file sysctl.conf (should be 0o600; was 420)

        '''

        compliant = True

        self.editor = ""
        self.path = "/private/etc/sysctl.conf"
        self.tmpPath = self.path + ".tmp"
        sysctl = "/usr/sbin/sysctl"
        self.directives = {"net.inet6.ip6.forwarding": "0",
                           "net.inet6.ip6.maxifprefixes": "1",
                           "net.inet6.ip6.maxifdefrouters": "1",
                           "net.inet6.ip6.maxfrags": "0",
                           "net.inet6.ip6.maxfragpackets": "0",
                           "net.inet6.ip6.neighborgcthresh": "1024",
                           "net.inet6.ip6.use_deprecated": "0",
                           "net.inet6.ip6.hdrnestlimit": "0",
                           "net.inet6.ip6.only_allow_rfc4193_prefixes": "1",
                           "net.inet6.ip6.dad_count": "0",
                           "net.inet6.icmp6.nodeinfo": "0",
                           "net.inet6.icmp6.rediraccept": "1",
                           "net.inet6.ip6.maxdynroutes": "0"}
        self.fixables = {}

        try:

            self.cmdhelper = CommandHelper(self.logger)

            for directive in self.directives:
                cmd = [sysctl, "-n", directive]
                if self.cmdhelper.executeCommand(cmd):
                    output = self.cmdhelper.getOutputString().strip()
                    if output != self.directives[directive]:
                        self.detailedresults += "The value for " + directive + \
                            " is not " + self.directives[directive] + ", it's " + \
                            output + "\n"
                        compliant = False
                        self.fixables[directive] = self.directives[directive]
                else:
                    error = self.cmdhelper.getErrorString()
                    self.detailedresults += "There was an error running the " + \
                        "the command " + cmd + "\n"
                    self.logger.log(LogPriority.DEBUG, error)
                    self.fixables[directive] = self.directives[directive]
                    compliant = False
            if not os.path.exists(self.path):
                compliant = False
            else:
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                                 "conf", self.path, self.tmpPath,
                                                 self.directives, "present",
                                                 "closedeq")
                if not self.editor.report():
                    compliant = False
                    self.detailedresults += "Didn't find the correct contents " + \
                        "inside " + self.path + "\n"
                if not checkPerms(self.path, [0, 0, 0o600], self.logger):
                    compliant = False

        except Exception:
            raise

        return compliant

###############################################################################
    def reportLinux(self):
        netwrkfile = ""
        ifacefile = ""
        sysctl = "/etc/sysctl.conf"
        compliant = True
        self.interface1 = {"IPV6_AUTOCONF": "no"}
        self.interface2 = {"IPV6_PRIVACY": "rfc3041"}
        self.sysctls = {"net.ipv6.conf.default.router_solicitations": "0",
                   "net.ipv6.conf.default.accept_ra_rtr_pref": "0",
                   "net.ipv6.conf.default.accept_ra_pinfo": "0",
                   "net.ipv6.conf.default.accept_ra_defrtr": "0",
                   "net.ipv6.conf.default.autoconf": "0",
                   "net.ipv6.conf.default.dad_transmits": "0",
                   "net.ipv6.conf.default.max_addresses": "1",
                   "net.ipv6.conf.default.accept_ra": "0",
                   "net.ipv6.conf.default.accept_redirects": "0"}
        self.ph = Pkghelper(self.logger, self.environ)

        # check compliancy of /etc/sysctl.conf file
        if not os.path.exists(sysctl):
            compliant = False
            self.detailedresults += sysctl + " file doesn't exist\n"
        else:
            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile, self.sysctls,
                                          "present", "openeq")
            if not self.editor1.report():
                self.detailedresults += "/etc/sysctl file doesn't contain \
                    the correct contents\n"
                compliant = False
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions for " + sysctl + \
                                        "are incorrect\n"
                compliant = False

        # in addition to checking /etc/sysctl.conf contents we need to
        # also check sysctl compliancy using the sysctl command
        for key in self.sysctls:
            self.ch.executeCommand("/sbin/sysctl " + key)
            retcode = self.ch.getReturnCode()
            output = self.ch.getOutputString()
            errmsg = output + self.ch.getErrorString()
            if retcode != 0:
                if re.search("unknown key", errmsg):
                    continue
                else:
                    self.detailedresults += "Failed to get value of " + key + " key with sysctl command\n"
                    self.logger.log(LogPriority.DEBUG, errmsg)
                    compliant = False
            else:
                if output.strip() != key + " = " + self.sysctls[key]:
                    compliant = False
                    self.detailedresults += "sysctl output has incorrect value: " + \
                        output + "\n"

        # set the appropriate files based on the system
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            if not os.path.exists(ifacefile):
                ifacefile = ""
            netwrkfile = "/etc/sysconfig/network"
            if not os.path.exists(netwrkfile):
                netwrkfile = ""
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network"
            if not os.path.exists(ifacefile):
                ifacefile = ""

        # Check contents of network file
        if netwrkfile:
            if os.path.exists(netwrkfile):
                if not checkPerms(netwrkfile, [0, 0, 0o644], self.logger):
                    compliant = False
                tmpfile = netwrkfile + ".tmp"
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", netwrkfile, tmpfile,
                                              self.interface1, "present", "closedeq")
                if not self.editor2.report():
                    self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"
                    compliant = False
            else:
                self.detailedresults += netwrkfile + " doesn't exist\n"
                compliant = False

        if ifacefile:
            dirs = glob.glob(ifacefile + "*")
            for loc in dirs:
                contents = []
                if re.search("^" + ifacefile + "ifcfg", loc):
                    if not checkPerms(loc, [0, 0, 0o644], self.logger):
                        compliant = False
                    contents = readFile(loc, self.logger)
                    if contents:
                        for key in self.interface2:
                            found = False
                            iterator = 0
                            for line in contents:
                                if re.search("^#", line) or re.match("^\s*$",
                                                                     line):
                                    continue
                                if re.search("^" + key, line):
                                    if re.search("=", line):
                                        temp = line.split("=")
                                        if temp[1].strip() == self.interface2[key]:
                                            found = True
                                            continue
                                        else:
                                            found = False
                                            break
                                    else:
                                        compliant = False
                                        self.detailedresults += loc + \
                                            " file in bad format\n"
                            if not found:
                                self.detailedresults += "contents of " + \
                                    loc + " file is wrong\n"
                                compliant = False
                                break
                            else:
                                continue
                    else:
                        compliant = False
        return compliant

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

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

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

            if self.environ.getosfamily() == "linux":
                self.rulesuccess = self.fixLinux()
            elif self.environ.getosfamily() == "freebsd":
                self.rulesuccess = self.fixFree()
            elif self.environ.getosfamily() == "darwin":
                self.rulesuccess = self.fixMac()
            elif self.environ.getosfamily() == "solaris":
                self.detailedresults = "Solaris systems require a manual fix"
                self.logger.log(LogPriority.INFO, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
###############################################################################
    def fixMac(self):
        '''use the sysctl command to write directives
        create the sysctl.conf file if needed
        set permissions and ownership of sysctl.conf file
        to 0o600 and 0,0


        :returns: success

        :rtype: bool
@author: dwalker
@change: Breen Malmberg - 1/10/2017 - added doc string; try/except;
        fixed perms for file sysctl.conf (should be 0o600; was 420)

        '''

        success = True
        created = False

        try:

            if self.fixables:
                sysctl = "/usr/sbin/sysctl"
                for directive in self.fixables:
                    cmd = [sysctl, "-w", directive + "=" + self.fixables[directive]]
                    if not self.cmdhelper.executeCommand(cmd):
                        error = self.cmdhelper.getErrorString()
                        self.detailedresults += "There was an error running " + \
                        "the command " + cmd + "\n"
                        self.logger.log(LogPriority.DEBUG, error)
                        success = False
            if not os.path.exists(self.path):
                if 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)
                else:
                    return False
            if not self.editor:
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                                 "conf", self.path, self.tmpPath,
                                                 self.directives, "present",
                                                 "closedeq")
                if not self.editor.report():
                    if self.editor.fix():
                        if not self.editor.commit():
                            success = False
                            self.detailedresults += "KVEditor commit to " + \
                                self.path + " was not successful\n"
                    else:
                        success = False
                        self.detailedresults += "KVEditor fix of " + self.path + \
                            " was not successful\n"
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor.setEventID(myid)
                if self.editor.fix():
                    if not self.editor.commit():
                        success = False
                        self.detailedresults += "KVEditor commit to " + \
                            self.path + " was not successful\n"
                else:
                    success = False
                    self.detailedresults += "KVEditor fix of " + self.path + \
                        " was not successful\n"
            if not checkPerms(self.path, [0, 0, 0o600], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.path, [0, 0, 0o600], self.logger,
                                self.statechglogger, myid):
                        self.detailedresults += "Could not set permissions" + \
                            " on " + self.path + "\n"
                        success = False
                else:
                    if not setPerms(self.path, [0, 0, 0o600], self.logger):
                        self.detailedresults += "Could not set permissions" + \
                            " on " + self.path + "\n"
                        success = False

        except Exception:
            raise

        return success

###############################################################################
    def fixLinux(self):
        universal = "#The following lines were added by stonix\n"
        debug = ""
        success = True
        ifacefile = ""
        netwrkfile = ""
        sysctl = "/etc/sysctl.conf"
        interface = {"IPV6_AUTOCONF": "no"}
        interface2 = {"IPV6_PRIVACY": "rfc3041"}
#                     "IPV6_DEFAULTGW": self.gateway,
#                     "IPV6ADDR":self.ipaddr}
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            netwrkfile = "/etc/sysconfig/network"
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
        created = False

        # fix sysctl / tuning kernel parameters
        # manually write key value pairs to /etc/sysctl.conf
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation",
                         "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Could not create file " + sysctl + \
                                        "\n"
                success = False
        if os.path.exists(sysctl):
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile, self.sysctls,
                                          "present", "openeq")
            if not self.editor1.report():
                if self.editor1.fixables:
                    # If we did not create the file, set an event ID for the
                    # KVEditor's undo event to record the file write
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor1.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    # permissions on file are incorrect
                    if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                        if not setPerms(sysctl, [0, 0, 0o644], self.logger):
                            self.detailedresults += "Could not set permissions on " + \
                                                    sysctl + "\n"
                            success = False
                    resetsecon(sysctl)

        # here we also check the output of the sysctl command for each key
        # to cover all bases
        for key in self.sysctls:
            if self.ch.executeCommand("/sbin/sysctl " + key):
                output = self.ch.getOutputString().strip()
                errmsg = output + self.ch.getErrorString()
                if re.search("unknown key", errmsg):
                    continue
                if not re.search(self.sysctls[key] + "$", output):
                    undovalue = output[-1]
                    self.ch.executeCommand("/sbin/sysctl -q -e -w " + key + "=" + self.sysctls[key])
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        success = False
                        self.detailedresults += "Failed to set " + key + " = " + self.sysctls[key] + "\n"
                        errmsg = self.ch.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errmsg)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        command = "/sbin/sysctl -q -e -w " + key + "=" + undovalue
                        event = {"eventtype": "commandstring",
                                 "command": command}
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Unable to get value for " + key + "\n"
                success = False
        # at the end do a print and ignore any key errors to ensure
        # the new values are read into the kernel
        self.ch.executeCommand("/sbin/sysctl -q -e -p")
        retcode2 = self.ch.getReturnCode()
        if retcode2 != 0:
            success = False
            self.detailedresults += "Failed to load new sysctl configuration from config file\n"
            errmsg2 = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg2)

        # correct the network file if it exists
        if netwrkfile:
            created = False
            if not os.path.exists(netwrkfile):
                if not createFile(netwrkfile, self.logger):
                    success = False
                    debug = "Unable to create " + netwrkfile + " file\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation",
                             "filepath": netwrkfile}
                    self.statechglogger.recordchgevent(myid, event)
                    tmpfile = netwrkfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                                  "conf", netwrkfile, tmpfile,
                                                  self.interface1, "present", "closedeq")
                    self.editor2.report()
            if os.path.exists(netwrkfile):
                if not checkPerms(netwrkfile, [0, 0, 0o644], self.logger):
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(netwrkfile, [0, 0, 0o644], self.logger,
                                        self.statechglogger, myid):
                            success = False
                if self.editor2:
                    if self.editor2.fixables:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor2.setEventID(myid)
                        if not self.editor2.fix():
                            success = False
                        elif not self.editor2.commit():
                            success = False
                        os.chown(netwrkfile, 0, 0)
                        os.chmod(netwrkfile, 0o644)
                        resetsecon(netwrkfile)
        if ifacefile:
            if os.path.exists(ifacefile):
                dirs = glob.glob(ifacefile + "*")
                if dirs:
                    for loc in dirs:
                        interface2 = {"IPV6_PRIVACY": "rfc3041"}
#                                       "IPV6_DEFAULTGW": self.gateway,
#                                       "IPV6ADDR":self.ipaddr}
                        interface3 = {"IPV6_PRIVACY": "rfc3041"}
#                                       "IPV6_DEFAULTGW": self.gateway,
#                                       "IPV6ADDR":self.ipaddr}
                        found = False
                        tempstring = ""
                        if re.search('^' + ifacefile + 'ifcfg', loc):
                            filename = loc
                            tmpfile = filename + ".tmp"
                            contents = readFile(filename, self.logger)
                            if not checkPerms(filename, [0, 0, 0o644],
                                              self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                if not setPerms(filename, [0, 0, 0o644],
                                                self.logger,
                                                self.statechglogger, myid):
                                    return False
                            for key in interface2:
                                found = False
                                for line in contents:
                                    if re.search("^#", line) or \
                                            re.match("^\s*$", line):
                                        continue
                                    if re.search("^" + key, line):
                                        if re.search("=", line):
                                            temp = line.split("=")
                                            if temp[1].strip() == \
                                                    interface2[key]:
                                                if found:
                                                    continue
                                                found = True
                                            else:
                                                contents.remove(line)
                                if found:
                                    del interface3[key]
                            for line in contents:
                                tempstring += line
                            tempstring += universal
                            for key in interface3:
                                tempstring += key + "=" + interface3[key] + \
                                    "\n"
                            if not writeFile(tmpfile, tempstring, self.logger):
                                return False
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {'eventtype': 'conf',
                                     'filepath': filename}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(filename,
                                                                 tmpfile, myid)
                            os.rename(tmpfile, filename)
                            os.chown(filename, 0, 0)
                            os.chmod(filename, 0o644)
                            resetsecon(filename)
            elif not os.path.exists(ifacefile) and ifacefile != "":
                # will not attempt to create the interface files
                self.detailedresults += "Interface directory which holds interface \
                files, doesn't exist. Stonix will not attempt to make this \
                directory or the files contained therein."
                success = False
        return success
示例#11
0
    def fixLinux(self):
        '''
        @change: dkennel removed extraneous arg from setperms call on 864
        '''
        universal = "#The following lines were added by stonix\n"
        debug = ""
        success = True
        ifacefile = ""
        netwrkfile = ""
        sysctl = "/etc/sysctl.conf"
        blacklistfile = "/etc/modprobe.d/stonix-blacklist.conf"

        # STIG portion, correct netconfig file
        if self.ph.manager == "apt-get":
            nfspkg = "nfs-common"
        else:
            nfspkg = "nfs-utils.x86_64"
        # if package not installed, no need to configure it
        if self.ph.check(nfspkg):
            if os.path.exists("/etc/netconfig"):
                filestring = ""
                # we want to make sure the following two lines don't
                # appear in the netconfig file
                item1 = "udp6 tpi_clts v inet6 udp - -"
                item2 = "tcp6 tpi_cots_ord v inet6 tcp - -"
                contents = readFile("/etc/netconfig", self.logger)
                for line in contents:
                    templine = re.sub("\s+", " ", line.strip())
                    # if we find the lines, skip them thus leaving them out of
                    # of the rewrite
                    if re.search(item1, templine) or re.search(
                            item2, templine):
                        continue
                    else:
                        filestring += line
                tmpfile = "/etc/netconfig.tmp"
                if not writeFile(tmpfile, filestring, self.logger):
                    success = False
                else:
                    # record event, rename file, set perms
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": "/etc/netconfig"}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(
                        "/etc/netconfig", tmpfile, myid)
                    os.rename(tmpfile, "/etc/netconfig")
                    os.chown("/etc/netconfig", 0, 0)
                    os.chmod("/etc/netconfig", 420)
                    resetsecon("/etc/netconfig")

        # remove any ipv6 addresses from /etc/hosts file
        if os.path.exists("/etc/hosts"):
            contents = readFile("/etc/hosts", self.logger)
            tempstring = ""
            tmpfile = "/etc/hosts.tmp"
            for line in contents:
                if re.search("^#", line) or re.match("^\s*$", line):
                    tempstring += line
                    continue
                elif re.search(":", line):
                    tempstring += "#" + line
                else:
                    tempstring += line
            if writeFile(tmpfile, tempstring, self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": "/etc/hosts"}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange("/etc/hosts", tmpfile,
                                                     myid)
                os.rename(tmpfile, "/etc/hosts")
                os.chown("/etc/hosts", 0, 0)
                os.chmod("/etc/hosts", 420)
                resetsecon("/etc/hosts")
            else:
                success = False
                debug = "Unable to write to file /etc/hosts\n"
                self.logger.log(LogPriority.DEBUG, debug)

        # fix sysctl / tuning kernel parameters
        # manually write key value pairs to /etc/sysctl.conf
        created = False
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                success = False
                debug = "Unable to create " + sysctl + "\n"
                self.logger.log(LogPriority.DEBUG, debug)
        if os.path.exists(sysctl):
            if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False

            tmpfile = sysctl + ".tmp"
            self.editor1 = KVEditorStonix(self.statechglogger, self.logger,
                                          "conf", sysctl, tmpfile,
                                          self.sysctls, "present", "openeq")
            if not self.editor1.report():
                if self.editor1.fixables:
                    # If we did not create the file, set an event ID for the
                    # KVEditor's undo event
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor1.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for /etc/sysctl.conf file\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
                        if not setPerms(self.path, [0, 0, 0o644], self.logger):
                            self.detailedresults += "Could not set permissions on " + \
                                                    self.path + "\n"
                            success = False
                    resetsecon(sysctl)

        # here we also check the output of the sysctl command for each key
        # to cover all bases
        for key in self.sysctls:
            if self.ch.executeCommand("/sbin/sysctl " + key):
                output = self.ch.getOutputString().strip()
                errmsg = output + self.ch.getErrorString()
                if re.search("unknown key", errmsg):
                    continue
                if not re.search(self.sysctls[key] + "$", output):
                    undovalue = output[-1]
                    self.ch.executeCommand("/sbin/sysctl -q -e -w " + key +
                                           "=" + self.sysctls[key])
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        success = False
                        self.detailedresults += "Failed to set " + key + " = " + self.sysctls[
                            key] + "\n"
                        errmsg = self.ch.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errmsg)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        command = "/sbin/sysctl -q -e -w " + key + "=" + undovalue
                        event = {
                            "eventtype": "commandstring",
                            "command": command
                        }
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Unable to get value for " + key + "\n"
                success = False
        # at the end do a print and ignore any key errors to ensure
        # the new values are read into the kernel
        self.ch.executeCommand("/sbin/sysctl -q -e -p")
        retcode2 = self.ch.getReturnCode()
        if retcode2 != 0:
            success = False
            self.detailedresults += "Failed to load new sysctl configuration from config file\n"
            errmsg2 = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg2)

        # We never found the correct contents in any of the modprobe.d files
        # so we're going to created the stonix-blacklist file
        # this file is used in other rules
        if not self.modprobeOK:
            created = False
            tmpfile = blacklistfile + ".tmp"
            modprobekveditor = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", blacklistfile, tmpfile,
                                              self.modprobes, "notpresent",
                                              "space")
            if not os.path.exists(blacklistfile):
                # create the file and record the event as file creation
                if createFile(blacklistfile, self.logger):
                    created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "creation",
                        "filepath": blacklistfile
                    }
                    self.statechglogger.recordchgevent(myid, event)
            if os.path.exists(blacklistfile):
                if not modprobekveditor.report():
                    if not modprobekveditor.fix():
                        success = False
                        self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"
                    else:
                        # if the file was created, then we already recorded an event
                        # for that, so this step would get skipped
                        if not created:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            modprobekveditor.setEventID(myid)
                        if not modprobekveditor.commit():
                            success = False
                            self.detailedresults += "Unable to correct contents in " + \
                                                blacklistfile + "\n"

        # fix ifcfg (interface) files
        if self.ph.manager == "yum":
            ifacefile = "/etc/sysconfig/network-scripts/"
            netwrkfile = "/etc/sysconfig/network"
        elif self.ph.manager == "zypper":
            ifacefile = "/etc/sysconfig/network/"
        if ifacefile:
            if os.path.exists(ifacefile):
                dirs = glob.glob(ifacefile + "*")
                if dirs:
                    for loc in dirs:
                        interface = {"IPV6INIT": "no", "NETWORKING_IPV6": "no"}
                        interface2 = {
                            "IPV6INIT": "no",
                            "NETWORKING_IPV6": "no"
                        }
                        found = False
                        tempstring = ""
                        if re.search('^' + ifacefile + 'ifcfg', loc):
                            filename = loc
                            tmpfile = filename + ".tmp"
                            contents = readFile(filename, self.logger)
                            if not checkPerms(filename, [0, 0, 420],
                                              self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                if not setPerms(filename, [0, 0, 420],
                                                self.logger,
                                                self.statechglogger, myid):
                                    debug = "Unable to set permissions on " + \
                                        filename + "\n"
                                    self.logger.log(LogPriority.DEBUG, debug)
                                    success = False
                            for key in interface:
                                found = False
                                for line in contents:
                                    if re.search("^#", line) or \
                                       re.match("^\s*$", line):
                                        continue
                                    if re.search("^" + key, line):
                                        if re.search("=", line):
                                            temp = line.split("=")
                                            if temp[1].strip(
                                            ) == interface[key]:
                                                if found:
                                                    continue
                                                found = True
                                            else:
                                                contents.remove(line)
                                if found:
                                    del interface2[key]
                            for line in contents:
                                tempstring += line
                            tempstring += universal
                            for key in interface2:
                                tempstring += key + "=" + interface2[key] + \
                                    "\n"
                            if not writeFile(tmpfile, tempstring, self.logger):
                                success = False
                                debug = "Unable to write to file " + loc + "\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {'eventtype': 'conf', 'filepath': filename}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(
                                filename, tmpfile, myid)
                            os.rename(tmpfile, filename)
                            os.chown(filename, 0, 0)
                            os.chmod(filename, 420)
                            resetsecon(filename)
            elif not os.path.exists(ifacefile) and ifacefile != "":
                # will not attempt to create the interface files
                debug = "interface directory which holds interface \
                files, doesn't exist, stonix will not attempt to make this \
                directory or the files contained therein"

                success = False
                self.logger.log(LogPriority.DEBUG, debug)

        # fix network file if it exists
        if netwrkfile:
            if not os.path.exists(netwrkfile):
                if not createFile(netwrkfile, self.logger):
                    debug = "Unable to create " + netwrkfile + "file\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
                else:
                    if not checkPerms(netwrkfile, [0, 0, 420], self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(netwrkfile, [0, 0, 420], self.logger,
                                        self.statechglogger, myid):
                            debug = "Unable to set permissions on " + \
                                    netwrkfile + "\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                    tmpfile = netwrkfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf",
                                                  netwrkfile, tmpfile,
                                                  self.interface, "present",
                                                  "closedeq")
                    if not self.editor2.report():
                        self.detailedresults += netwrkfile + " doesn't contain \
the correct contents\n"

            if self.editor2:
                if self.editor2.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        success = False
                        debug = "Unable to complete kveditor fix method" + \
                            "for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    elif not self.editor2.commit():
                        success = False
                        debug = "Unable to complete kveditor commit " + \
                            "method for " + netwrkfile + "\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                    os.chown(netwrkfile, 0, 0)
                    os.chmod(netwrkfile, 420)
                    resetsecon(netwrkfile)

        # fix sshd_config file for apt-get systems if ssh is installed
        if self.ph.manager == "apt-get":
            if not os.path.exists("/etc/ssh/sshd_config"):
                msg = "/etc/ssh/ssd_config doesn\'t exist.  This could mean ssh \
    is not installed or the file has been inadvertantly deleted.  Due to the \
    complexity of this file stonix will not attempt to create this file"

                self.logger.log(LogPriority.DEBUG, msg)
                success = False
            else:
                if not checkPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                  self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms("/etc/ssh/sshd_config", [0, 0, 420],
                                    self.logger, self.statechglogger, myid):
                        success = False
                        debug = "Unable to set permissions on " + \
                            "/etc/ssh/sshd_config\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                if self.editor3:
                    if self.editor3.fixables:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor3.setEventID(myid)
                        if not self.editor3.fix():
                            success = False
                            debug = "Unable to complete kveditor fix method" + \
                                "for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        elif not self.editor3.commit():
                            success = False
                            debug = "Unable to complete kveditor commit " + \
                                "method for /etc/ssh/sshd_config file\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        os.chown("/etc/ssh/sshd_config", 0, 0)
                        os.chmod("/etc/ssh/sshd_config", 420)
                        resetsecon("/etc/ssh/sshd_config")
        return success
示例#12
0
    def fixSSHFile(self, sshfile, directives):
        """
        apply configuration options to config files
        :param: sshfile - filepath string
        :param: directives - dictionary of desired directives
        :return: compliant
        :rtype: bool

        """
        success = True
        debug = ""
        directives = dict(directives)
        tpath = sshfile + ".tmp"
        created = False
        if not os.path.exists(sshfile):
            createFile(sshfile, self.logger)
            created = True
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "creation",
                     "filepath": sshfile}
            self.statechglogger.recordchgevent(myid, event)
        if os.path.exists(sshfile):
            if re.search("Ubuntu", self.environ.getostype()):
                if sshfile == "/etc/ssh/sshd_config":
                    del (directives["GSSAPIAuthentication"])
                    del (directives["KerberosAuthentication"])
                elif sshfile == "/etc/ssh/ssh_config":
                    del (directives["GSSAPIAuthentication"])
            elif self.environ.getostype() == "Mac OS X" and self.mac_piv_auth_CI.getcurrvalue():
                if sshfile == "/private/etc/ssh/sshd_config":
                    directives["ChallengeResponseAuthentication"] = "no"
                    directives["PasswordAuthentication"] = "no"
            editor = KVEditorStonix(self.statechglogger,
                                      self.logger, "conf", sshfile,
                                      tpath, directives, "present",
                                      "space")
            editor.report()
            if re.search("Ubuntu", self.environ.getostype()):
                if sshfile == "/etc/ssh/sshd_config":
                    directives = {"GSSAPIAuthentication": "",
                                  "KerberosAuthentication": ""}
                elif sshfile == "/etc/ssh/ssh_config":
                    directives = {"GSSAPIAuthentication": ""}
                editor.setIntent("notpresent")
                editor.setData(directives)
                editor.report()
            if not checkPerms(sshfile, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(sshfile, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
                else:
                    if not setPerms(sshfile, [0, 0, 0o644], self.logger):
                        success = False
            if editor.fixables or editor.removeables:
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    editor.setEventID(myid)
                if editor.fix():
                    if editor.commit():
                        os.chown(sshfile, 0, 0)
                        os.chmod(sshfile, 0o644)
                        resetsecon(sshfile)
                    else:
                        self.detailedresults += "Unable to correct contents " + \
                            "in " + sshfile + "\n"
                        debug = "kveditor1 commit did not run successfully"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                else:
                    self.detailedresults += "Unable to correct contents " + \
                                            "in " + sshfile + "\n"
                    debug = "kveditor1 fix did not run successfully"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
        return success
示例#13
0
class CheckRootPath(Rule):
    """
    The CheckRootPath rule checks the root user's PATH environment variable,
ensuring that it is set to the vendor default and that there are no user or
world-writable files or directories in any of the path directories.
    """

    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 = 44
        self.rulename = 'CheckRootPath'
        self.formatDetailedResults("initialize")
        self.compliant = False
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = ['NSA RHEL 2.3.4.1, 2.3.4.1.1, 2.3.4.1.2',
                         "CCE-RHEL7-CCE-TBD 2.4.1.1.7"]
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}

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

        if self.isapplicable():
            myos = self.environ.getostype().lower()
            self.myos = myos
            if re.search("os x", myos):
                defaultPath = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
            elif re.search("opensuse", myos):
                defaultPath = "/sbin:/usr/sbin:/usr/local/sbin:/root/bin:" + \
                    "/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games"
            elif re.search("fedora|centos|red hat", myos):
                defaultPath = "/usr/local/sbin:/usr/local/bin:/sbin:/bin:" + \
                    "/usr/sbin:/usr/bin:/root/bin"
            else:
                defaultPath = "/usr/local/sbin:/usr/local/bin:/sbin:/bin:" + \
                    "/usr/sbin:/usr/bin"
            self.defaultPath = defaultPath

    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.detailedresults 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 - True if compliant; False if not
        :rtype: bool

        """

        try:
            compliant = True
            self.detailedresults = ""
            self.vendorDefault = True
            wwList = []
            defaultPath = self.defaultPath
            path = os.environ['PATH']

            if not re.search(defaultPath, path):
                compliant = False
                self.vendorDefault = False
                self.detailedresults += "root's PATH variable is not set " + \
                    "to the vendor default\n"

            exPaths = path.split(":")
            self.logger.log(LogPriority.DEBUG,
                            "PATH entries: " + str(exPaths))
            for exPath in exPaths:
                if not os.path.exists(exPath):
                    continue
                pathEntries = os.listdir(exPath)
                for entry in pathEntries:
                    absPath = exPath + "/" + entry
                    if not os.path.exists(absPath):
                        continue
                    entryStat = os.stat(absPath)
                    userMode = oct(entryStat.st_mode)[-1]
                    if userMode == "7" or userMode == "6" or userMode == "2":
                        compliant = False
                        wwList.append(absPath)
                        self.detailedresults += "World-writeable entry " + \
                            "found at: " + absPath + "\n"

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

    def fix(self):
        """set root's default PATH environment variable to vendor default

        :return: self.rulesucces - True if fix succeeds; False if not
        :rtype: bool

        """

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

            if not self.vendorDefault:
                os.environ['PATH'] = self.defaultPath
                if re.search("darwin", self.myos):
                    root = "/var/root/"
                else:
                    root = "/root/"
                checkFiles = [root + ".profile", root + ".bashrc"]
                for checkFile in checkFiles:
                    if not os.path.exists(checkFile):
                        open(checkFile, "w")
                    tmppath = checkFile + ".tmp"
                    data = {"PATH": self.defaultPath}
                    self.editor = KVEditorStonix(self.statechglogger,
                                                 self.logger, "conf",
                                                 checkFile, tmppath, data,
                                                 "present", "closedeq")
                    if not self.editor.report():
                        if self.editor.fix():
                            if not self.editor.commit():
                                success = False
                                self.detailedresults += "Failed to commit " + \
                                    "changes to " + checkFile + "\n"
                        else:
                            success = False
                            self.detailedresults += "Error fixing file " + \
                                checkFile + "\n"

            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
示例#14
0
class SecureNFS(Rule):
    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 = 39
        self.rulename = "SecureNFS"
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.guidance = [
            "NSA(3.13.4)", "cce-4241-6", "cce-4465-1", "cce-4559-1",
            "cce-4015-4", "cce-3667-3", "cce-4310-9", "cce-4438-8",
            "cce-3579-0"
        ]

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

    def report(self):
        """
        Run report actions for SecureNFS

        :return: self.compliant
        :rtype: bool

        """

        self.detailedresults = ""
        self.compliant = True
        nfsexports = True

        try:

            if self.environ.getosfamily() == "linux":
                self.ph = Pkghelper(self.logger, self.environ)

            # secure NFS service
            self.sh = ServiceHelper(self.environ, self.logger)
            if self.environ.getostype() == "Mac OS X":
                nfsfile = "/etc/nfs.conf"
                data1 = {
                    "nfs.lockd.port": "",
                    "nfs.lockd.tcp": "1",
                    "nfs.lockd.udp": "1"
                }
                if not self.sh.auditService('/System/Library/LaunchDaemons/' +
                                            'com.apple.nfsd.plist',
                                            serviceTarget='com.apple.nfsd'):
                    self.compliant = True
                    self.formatDetailedResults("report", self.compliant,
                                               self.detailedresults)
                    self.logdispatch.log(LogPriority.INFO,
                                         self.detailedresults)
                    return self.compliant
            elif self.ph.manager in ("yum", "zypper", "dnf"):
                nfsfile = "/etc/sysconfig/nfs"
                data1 = {
                    "LOCKD_TCPPORT": "32803",
                    "LOCKD_UDPPORT": "32769",
                    "MOUNTD_PORT": "892",
                    "RQUOTAD_PORT": "875",
                    "STATD_PORT": "662",
                    "STATD_OUTGOING_PORT": "2020"
                }
                if self.ph.manager == "zypper":
                    nfspackage = "nfs-kernel-server"
                elif self.ph.manager == "yum" or self.ph.manager == "dnf":
                    nfspackage = "nfs-utils"
            elif self.ph.manager == "apt-get":
                nfsfile = "/etc/services"
                data1 = {
                    "rpc.lockd": ["32803/tcp", "32769/udp"],
                    "rpc.mountd": ["892/tcp", "892/udp"],
                    "rpc.quotad": ["875/tcp", "875/udp"],
                    "rpc.statd": ["662/tcp", "662/udp"],
                    "rpc.statd-bc": ["2020/tcp", "2020/udp"]
                }
                nfspackage = "nfs-kernel-server"
            if self.environ.getostype() != "Mac OS X":
                if self.ph.manager in ("apt-get", "zypper", "yum", "dnf"):
                    if not self.ph.check(nfspackage):
                        self.compliant = True
                        self.formatDetailedResults("report", self.compliant,
                                                   self.detailedresults)
                        self.logdispatch.log(LogPriority.INFO,
                                             self.detailedresults)
                        return self.compliant

            if os.path.exists(nfsfile):
                nfstemp = nfsfile + ".stonixtmp"
                eqtype = ""
                eqtypestr = ""
                if self.environ.getostype() == "Mac OS X":
                    eqtype = "openeq"
                    self.editor1 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", nfsfile,
                                                  nfstemp, data1, "present",
                                                  eqtype)
                elif self.ph.manager in ("yum", "zypper", "dnf"):
                    eqtype = "closedeq"
                    self.editor1 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", nfsfile,
                                                  nfstemp, data1, "present",
                                                  eqtype)
                elif self.ph.manager == "apt-get":
                    eqtype = "space"
                    self.editor1 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", nfsfile,
                                                  nfstemp, data1, "present",
                                                  eqtype)
                if eqtype == "openeq":
                    eqtypestr = " = "
                elif eqtype == "closedeq":
                    eqtypestr = "="
                elif eqtype == "space":
                    eqtypestr = " "

                if not self.editor1.report():
                    if self.editor1.fixables:
                        missingconfiglines = []
                        for item in self.editor1.fixables:
                            if isinstance(data1[item], list):
                                for li in data1[item]:
                                    missingconfiglines.append(
                                        str(item) + eqtypestr + str(li))
                            else:
                                missingconfiglines.append(
                                    str(item) + eqtypestr + str(data1[item]))
                        self.detailedresults += "\nThe following configuration lines are missing from " + str(
                            nfsfile) + ":\n" + "\n".join(missingconfiglines)
                    else:
                        self.detailedresults += "\nOne or more configuration lines are missing from " + str(
                            nfsfile)
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    self.compliant = False
                if not checkPerms(nfsfile, [0, 0, 420], self.logger):
                    self.detailedresults += "\nPermissions aren't correct on " \
                        + nfsfile
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    self.compliant = False
            else:
                self.detailedresults += "\n" + nfsfile + " does not exist"
                self.logger.log(LogPriority.DEBUG, self.detailedresults)
                self.compliant = False

            # secure NFS exports
            export = "/etc/exports"
            if os.path.exists(export):

                if not self.checkNFSexports():
                    nfsexports = False

                extemp = export + ".stonixtmp"
                data2 = {
                    "all_squash": "",
                    "no_root_squash": "",
                    "insecure_locks": ""
                }
                self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                              "conf", export, extemp, data2,
                                              "notpresent", "space")
                if not self.editor2.report():
                    incorrectconfiglines = []
                    if self.editor2.fixables:
                        for item in self.editor2.fixables:  # no fixables being generated for items not compliant with data2 and the "notpresent" directive
                            incorrectconfiglines.append(str(item))
                        self.detailedresults += "\nThe following configuration options are insecure, in file " + str(
                            export) + ":\n" + "\n".join(incorrectconfiglines)
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    self.compliant = False
                if not checkPerms(export, [0, 0, 420], self.logger):
                    self.detailedresults += "\n" + export + " file doesn't " + \
                        "have the correct permissions"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    self.compliant = False

            if not nfsexports:
                self.compliant = False

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

    def checkNFSexports(self):
        """
        check the NFS export lines in the exports configuration file

        :return: retval
        :rtype: bool
        """

        retval = True
        filename = "/etc/exports"
        directory = "/etc/exports.d"
        fileslist = []

        try:

            if os.path.exists(filename):

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

                if not self.checkNFScontents(contentlines, filename):
                    retval = False

            if os.path.exists(directory):
                fileslist = [
                    f for f in listdir(directory) if isfile(join(directory, f))
                ]
            if fileslist:
                for filename in fileslist:
                    f = open(filename, "r")
                    contentlines = f.readlines()
                    f.close()
                    if not self.checkNFScontents(contentlines, str(f)):
                        retval = False
            else:
                self.logger.log(LogPriority.DEBUG, "No NFS exports to check")

        except Exception:
            raise
        return retval

    def checkNFScontents(self, contentlines, filename=""):
        """
        check given list of contentlines for required nfs export formatting

        :param contentlines: 
        :param filename:  (Default value = "")
        :return: retval
        :rtype: bool
        """

        retval = True

        if not filename:
            filename = "(file name not specified)"
        if not isinstance(filename, str):
            filename = "(file name not specified)"

        if not isinstance(contentlines, list):
            self.logger.log(LogPriority.DEBUG,
                            "Parameter contentlines must be of type: list!")

        if not contentlines:
            self.logger.log(LogPriority.DEBUG,
                            "Parameter contentlines was empty!")

        try:

            if contentlines:
                for line in contentlines:
                    if re.search('^#', line):
                        continue
                    elif re.search('^\/', line):
                        # search for overly broad exports
                        broadexports = [
                            '^\/\w+.*\s*\d{2,3}\.\d{2,3}\.0\.0\/16\b',
                            '^\/\w+.*\s*\d{1,3}\.0\.0\.0\/8\b',
                            '^\/\w+.*\s*.*\*.*'
                        ]
                        for be in broadexports:
                            if re.search(be, line):
                                retval = False
                                self.detailedresults += "The nfs export line:\n" + str(
                                    line
                                ) + "\nin " + str(
                                    filename
                                ) + " contains an export that is overly broad."
                    else:
                        if re.search('^([^ !$`&*()+]|(\\[ !$`&*()+]))+\s*',
                                     line):
                            sline = line.split()
                            if len(sline) < 2:
                                retval = False
                                self.detailedresults += "\nThe export line:\n" + str(
                                    '\"' + line.strip() + '\"') + " in " + str(
                                        filename
                                    ) + " lacks a host specification."
            if not retval:
                self.detailedresults += "\n\nThere is no automatic fix action for host exports. Please ensure that each NFS export in " + str(
                    filename
                ) + " has a host specification.\nSee man exports for help.\n"

        except Exception:
            raise
        return retval

    def fix(self):
        """
        Run fix actions for SecureNFS


        :return: self.rulesuccess

        :rtype: bool
@author: dwalker
@change: Breen Malmberg - 4/26/2016 - changed location of defaults variables in method;
        added detailedresults message if fix run while CI disabled; added formatdetailedresults update if fix called when CI disabled;
        changed return value to always be self.rulesuccess; updated self.rulesuccess based on success variable as well
@change: Breen Malmberg - 7/11/2017 - added another service check on mac os x; no files will be created on mac if the service is not
        enabled

        """

        self.logdispatch.log(LogPriority.DEBUG, "Entering SecureNFS.fix()...")

        success = True
        changed1, changed2 = False, False
        installed = False
        self.detailedresults = ""

        try:

            if not self.ci.getcurrvalue():
                self.detailedresults += "\nThe CI for this rule was not enabled. Nothing has been done."
                success = True
                self.formatDetailedResults("fix", success,
                                           self.detailedresults)
                self.logdispatch.log(LogPriority.DEBUG,
                                     "Exiting SecureNFS.fix()...")
                return success

            # 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":
                nfsservice = "nfsd"
                nfsfile = "/etc/nfs.conf"
                data1 = {
                    "nfs.lockd.port": "",
                    "nfs.lockd.tcp": "1",
                    "nfs.lockd.udp": "1"
                }

            elif self.ph.manager in ("yum", "zypper", "dnf"):
                nfsfile = "/etc/sysconfig/nfs"
                data1 = {
                    "LOCKD_TCPPORT": "32803",
                    "LOCKD_UDPPORT": "32769",
                    "MOUNTD_PORT": "892",
                    "RQUOTAD_PORT": "875",
                    "STATD_PORT": "662",
                    "STATD_OUTGOING_PORT": "2020"
                }

                nfsservice = "nfs"
                if self.ph.manager == "zypper":
                    nfspackage = "nfs-kernel-server"
                elif self.ph.manager == "yum" or self.ph.manager == "dnf":
                    nfspackage = "nfs-utils"

            elif self.ph.manager == "apt-get":
                nfsservice = "nfs-kernel-server"
                nfspackage = "nfs-kernel-server"
                nfsfile = "/etc/services"
                data1 = {
                    "rpc.lockd": ["32803/tcp", "32769/udp"],
                    "rpc.mountd": ["892/tcp", "892/udp"],
                    "rpc.quotad": ["875/tcp", "875/udp"],
                    "rpc.statd": ["662/tcp", "662/udp"],
                    "rpc.statd-bc": ["2020/tcp", "2020/udp"]
                }

            if self.environ.getostype() != "Mac OS X":
                if self.ph.manager in ("apt-get", "zypper"):
                    if not self.ph.check(nfspackage):
                        success = True
                        self.formatDetailedResults("fix", success,
                                                   self.detailedresults)
                        self.logdispatch.log(LogPriority.INFO,
                                             self.detailedresults)
                        return success

            if not os.path.exists(nfsfile):
                if createFile(nfsfile, self.logger):
                    nfstemp = nfsfile + ".stonixtmp"
                    if self.environ.getostype() == "Mac OS X":
                        if not self.sh.auditService(
                                '/System/Library/LaunchDaemons/com.apple.nfsd.plist',
                                serviceTarget='com.apple.nfsd'):
                            success = True
                            self.formatDetailedResults("fix", success,
                                                       self.detailedresults)
                            self.logdispatch.log(LogPriority.INFO,
                                                 self.detailedresults)
                            return success
                        self.editor1 = KVEditorStonix(self.statechglogger,
                                                      self.logger, "conf",
                                                      nfsfile, nfstemp, data1,
                                                      "present", "openeq")
                    elif self.ph.manager in ("yum", "zypper", "dnf"):
                        self.editor1 = KVEditorStonix(self.statechglogger,
                                                      self.logger, "conf",
                                                      nfsfile, nfstemp, data1,
                                                      "present", "closedeq")
                    elif self.ph.manager == "apt-get":
                        self.editor1 = KVEditorStonix(self.statechglogger,
                                                      self.logger, "conf",
                                                      nfsfile, nfstemp, data1,
                                                      "present", "space")
                    if not self.editor1.report():
                        if not self.editor1.fix():
                            success = False
                            debug = "fix for editor1 failed"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.editor1.setEventID(myid)
                            if not self.editor1.commit():
                                success = False
                                debug = "commit for editor1 failed"
                                self.logger.log(LogPriority.DEBUG, debug)
                            else:
                                changed1 = True
                    if not checkPerms(nfsfile, [0, 0, 420], self.logger):
                        if not setPerms(nfsfile, [0, 0, 420], self.logger,
                                        self.statechglogger):
                            success = False
                            debug = "Unable to set permissions on " + nfsfile
                            self.logger.log(LogPriority.DEBUG, debug)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation", "filepath": nfsfile}
                    self.statechglogger.recordchgevent(myid, event)
                else:
                    success = False
                    debug = "Unable to create " + nfsfile + " file"
                    self.logger.log(LogPriority.DEBUG, debug)
            else:
                if self.editor1.fixables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor1.setEventID(myid)
                    if not self.editor1.fix():
                        success = False
                        debug = "editor1 fix failed"
                        self.logger.log(LogPriority.DEBUG, debug)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor1.setEventID(myid)
                        if not self.editor1.commit():
                            success = False
                            debug = "editor1 commit failed"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            changed1 = True
                if not checkPerms(nfsfile, [0, 0, 420], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(nfsfile, [0, 0, 420], self.logger,
                                    self.statechglogger, myid):
                        debug = "Unable to set permissions on " + nfsfile
                        self.logger.log(LogPriority.DEBUG, debug)

            export = "/etc/exports"
            if not os.path.exists(export):
                # mac os x will automatically enable the nfs
                # service and related ports if the file /etc/exports
                # is created
                if self.environ.getostype() == "Mac OS X":
                    if not self.sh.auditService(
                            '/System/Library/LaunchDaemons/com.apple.nfsd.plist',
                            serviceTarget='com.apple.nfsd'):
                        success = True
                        self.formatDetailedResults("fix", success,
                                                   self.detailedresults)
                        self.logdispatch.log(LogPriority.INFO,
                                             self.detailedresults)
                        return success
                if createFile(export, self.logger):
                    extemp = export + ".stonixtmp"
                    data2 = {
                        "all_squash": "",
                        "no_root_squash": "",
                        "insecure_locks": ""
                    }
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", export,
                                                  extemp, data2, "notpresent",
                                                  "space")
                    if not self.editor2.report():
                        if not self.editor2.fix():
                            success = False
                            debug = "fix for editor2 failed"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.editor2.setEventID(myid)
                            if not self.editor2.commit():
                                success = False
                                debug = "commit for editor2 failed"
                                self.logger.log(LogPriority.DEBUG, debug)
                            else:
                                changed2 = True
                    if not checkPerms(export, [0, 0, 420], self.logger):
                        if not setPerms(export, [0, 0, 420], self.logger,
                                        self.statechglogger):
                            success = False
                            debug = "Unable to set permissions on " + export
                            self.logger.log(LogPriority.DEBUG, debug)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation", "filepath": export}
                    self.statechglogger.recordchgevent(myid, event)
            else:
                if installed:
                    extemp = export + ".stonixtmp"
                    data2 = {
                        "all_squash": "",
                        "no_root_squash": "",
                        "insecure_locks": ""
                    }
                    self.editor2 = KVEditorStonix(self.statechglogger,
                                                  self.logger, "conf", export,
                                                  extemp, data2, "notpresent",
                                                  "space")
                    self.editor2.report()
                if self.editor2.removeables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor2.setEventID(myid)
                    if not self.editor2.fix():
                        success = False
                        debug = "editor2 fix failed"
                        self.logger.log(LogPriority.DEBUG, debug)
                    else:
                        if not self.editor2.commit():
                            success = False
                            debug = "editor2 commit failed"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            changed2 = True
                if not checkPerms(export, [0, 0, 420], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(export, [0, 0, 420], self.logger,
                                    self.statechglogger, myid):
                        success = False
                        debug = "Unable to set permissions on " + export
                        self.logger.log(LogPriority.DEBUG, debug)

            if changed1 or changed2:
                ## CHANGE (Breen Malmberg) 1/23/2017
                # The self.sh.reloadservice() call, for SHlaunchd, will start
                # the service even if it is not already running.
                # We don't want to start THIS service if it is not
                # already running/enabled!
                # We also don't want to change this functionality at the
                # SHlaunchd class-level, because there may be other
                # instances in which we want it to do a stop and start
                # (aka a full reload), so this decision should be made at
                # the rule-level.
                ##
                if self.sh.isRunning(nfsservice, serviceTarget=nfsservice):
                    self.sh.reloadService(nfsservice, serviceTarget=nfsservice)

            self.logdispatch.log(LogPriority.DEBUG,
                                 "Exiting SecureNFS.fix()...")

        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", success, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return success
示例#15
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
示例#16
0
class SecureDHCPServer(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 134
        self.rulename = "SecureDHCPServer"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        datatype = "bool"
        key = "SECUREDHCPSERVER"
        instructions = '''To disable this rule set the value of \
SECUREDHCPSERVER to False.'''
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.guidance = [
            "NSA 3.9.4", "CCE 4257-2", "CCE 4403-2", "CCE 4345-5",
            "CCE 3724-2", "CCE 4243-2", "CCE 4389-3", "CCE 3913-1",
            "CCE 4169-9", "CCE 4318-2", "CCE 4319-0", "CCE 3733-3"
        ]
        self.applicable = {"type": "white", "family": ["linux"]}
        self.iditerator = 0
        self.created = False

    def report(self):
        try:
            self.detailedresults = ""
            self.ph = Pkghelper(self.logger, self.environ)
            self.data1 = {
                "ddns-update-style": "none;",
                "deny": ["declines;", "bootp;"]
            }
            self.data2 = [
                "domain-name", "domain-name-servers", "nis-domain",
                "nis-servers", "ntp-servers", "routers", "time-offset"
            ]
            if self.ph.manager == "zypper":
                self.path = "/etc/dhcpd.conf"
            elif self.ph.manager == "yum" or self.ph.manager == "dnf":
                self.path = "/etc/dhcp/dhcpd.conf"
            elif self.ph.manager == "apt-get":
                self.path = "/etc/dhcp/dhcpd.conf"
            self.tmppath = self.path + ".tmp"
            compliant = True
            if os.path.exists(self.path):
                if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                    self.detailedresults += "The permissions on " + \
                        self.path + " are incorrect\n"
                    compliant = False
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             "conf", self.path, self.tmppath,
                                             self.data1, "present", "space")
                if not self.editor.report():
                    self.detailedresults += self.path + " doesn't contain " + \
                        "the correct contents\n"
                    compliant = False
                contents = readFile(self.path, self.logger)
                for line in contents:
                    if re.match('^#', line) or re.match(r'^\s*$', line):
                        continue
                    if re.search("^option", line):
                        linesplit = line.split()
                        if len(linesplit) >= 2:
                            for item in self.data2:
                                if re.search(item, linesplit[1]):
                                    compliant = False
                                    self.detailedresults += "Unwanted " + \
                                        "option found in " + self.path + \
                                        ": " + line
            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):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            # Clean out old undo events
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            self.detailedresults = ""
            if not os.path.exists(self.path):
                createFile(self.path, self.logger)
                self.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.tmppath,
                                             self.data1, "present", "space")
                self.editor.report()
            tempstring = ""
            tmpfile = self.path + ".tmp"
            contents = readFile(self.path, self.logger)
            changes = False
            for line in contents:
                found = False
                if re.match('^#', line) or re.match(r'^\s*$', line):
                    tempstring += line
                    continue
                if re.search("^option", line):
                    temp = line.split()
                    if len(temp) >= 2:
                        for item in self.data2:
                            if re.search(item, temp[1]):
                                found = True
                                changes = True
                                break
                        if found:
                            continue
                        else:
                            tempstring += line
                else:
                    tempstring += line
            if changes:
                debug = "Writing changes to " + tmpfile
                self.logger.log(LogPriority.DEBUG, debug)
                if not writeFile(tmpfile, tempstring, self.logger):
                    debug = "Unable to write changes to " + tmpfile
                    self.detailedresults += debug
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "conf", "filepath": self.path}
                    self.statechglogger.recordchgevent(myid, event)
                    self.statechglogger.recordfilechange(
                        self.path, tmpfile, myid)
                    os.rename(tmpfile, self.path)
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    setPerms(self.path, [0, 0, 0o644], self.logger,
                             self.statechglogger, myid)
                    resetsecon(self.path)
            if self.editor.fixables:
                if not self.created:
                    if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(self.path, [0, 0, 0o644], self.logger,
                                        self.statechglogger, myid):
                            success = False
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                if self.editor.fix():
                    if self.editor.commit():
                        debug = self.path + "'s contents have been " + \
                            "corrected\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        os.chown(self.path, 0, 0)
                        os.chmod(self.path, 0o644)
                        resetsecon(self.path)
                    else:
                        debug = "kveditor commit not successful\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                else:
                    debug = "kveditor fix not successful\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
示例#17
0
class PreventXListen(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 31
        self.rulename = "PreventXListen"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.guidance = ["NSA 3.6.1.3.2"]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

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

        self.properties = {}
        self.iditerator = 0

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

    def report(self):
        try:
            self.detailedresults = ""
            self.fixables1, self.fixables2 = [], []
            # self.fp1 contains a regex that needs to conform to each line in file
            self.fp1 = [
                ["^:(.)* -nolisten tcp", "/etc/X11/xdm/Xservers", True],
                [
                    "^:(.)* -nolisten tcp", "/usr/X11R6/lib/X11/xdm/Xservers",
                    True
                ], ["^:(.)* -nolisten tcp", "/etc/dt/config/Xservers", True],
                ["^:(.)* -nolisten tcp", "/usr/dt/config/Xservers", True]
            ]
            # self.fp2 contains regex that only needs to appear once in file
            self.fp2 = [[
                '^command = (.)* -nolisten tcp', '/etc/X11/gdm/gdm.conf', False
            ], ['^DisallowTCP = true', '/usr/share/gdm/defaults.conf', False],
                        ['^DisallowTCP = true', '/etc/gdm/custom.conf', False],
                        [
                            '^exec(.)* -nolisten tcp',
                            '/etc/X11/xinit/xserverrc', False
                        ],
                        [
                            '^ServerArgsLocal=(.)* -nolisten tcp',
                            '/etc/kde/kdm/kdmrc', False
                        ],
                        [
                            '^ServerArgsLocal=(.)* -nolisten tcp',
                            '/etc/kde4/kdm/kdmrc', False
                        ],
                        [
                            '^ServerArgsLocal=(.)* -nolisten tcp',
                            '/usr/share/config/kdm/kdmrc', False
                        ],
                        [
                            'DISPLAYMANAGER_XSERVER_TCP_PORT_6000_OPEN=NO',
                            '/etc/sysconfig/displaymanager', False
                        ]]
            compliant = True
            for item in self.fp1:
                if os.path.exists(item[1]):
                    if not self.checkConfig(item[0], item[1], item[2]):
                        self.detailedresults += item[1] + " doesn\'t have \
correct configuration\n"

                        self.fixables1.append(item)
                        compliant = False
                    if item[1] == "/etc/X11/xdm/Xservers" or item[1] == \
                        "/usr/X11R6/lib/X11/xdm/Xservers":
                        if not checkPerms(item[1], [0, 0, 292], self.logger):
                            self.detailedresults += item[1] + " doesn\'t have \
correct permissions\n"

                            compliant = False
                    else:
                        if not checkPerms(item[1], [0, 3, 292], self.logger):
                            self.detailedresults += item[1] + " doesn\'t have \
correct permissions\n"

                            compliant = False
            for item in self.fp2:
                if os.path.exists(item[1]):
                    if not self.checkConfig(item[0], item[1], item[2]):
                        self.detailedresults += item[1] + " doesn\'t have \
correct configuration\n"

                        self.fixables2.append(item)
                    if item[1] == "/etc/X11/xinit/xserverrc":
                        if not checkPerms(item[1], [0, 0, 493], self.logger):
                            self.detailedresults += item[1] + " doesn\'t have \
correct permissions\n"

                            compliant = False
                    elif not checkPerms(item[1], [0, 0, 420], self.logger):
                        self.detailedresults += item[1] + " doesn\'t have \
correct permissions\n"

                        compliant = False
            if self.environ.getosfamily() == "solaris":
                fp3 = "/etc/X11/gdm/gdm.conf"
                keys = {"security": {"DisallowTCP": "true"}}
                if os.path.exists(fp3):
                    tmpPath = "/etc/X11/gdm/gdm.conf.tmp"
                    kvtype = "tagconf"
                    intent = "present"
                    self.editor = KVEditorStonix(self.statechglogger,
                                                 self.logger, kvtype, fp3,
                                                 tmpPath, keys, intent,
                                                 "closedeq")
                    if not self.editor.report():
                        self.detailedresults += "Kveditor report for solaris \
is non compliant\n"

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

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

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            self.iditerator = 0

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

            success = True
            index = {
                "/etc/X11/gdm/gdm.conf":
                "command = /usr/X11R6/bin/X -nolisten tcp",
                "/usr/share/gdm/defaults.conf":
                "DisallowTCP = true",
                "/etc/gdm/custom.conf":
                "DisallowTCP = true",
                "/etc/X11/xinit/xserverrc":
                "exec /usr/X11R6/bin/X -nolisten tcp",
                "/etc/kde/kdm/kdmrc":
                "ServerArgsLocal = -nolisten tcp",
                "/etc/kde4/kdm/kdmrc":
                "ServerArgsLocal = -nolisten tcp",
                "/usr/share/config/kdm/kdmrc":
                "ServerArgsLocal = -nolisten tcp",
                "/etc/sysconfig/displaymanager":
                'DISPLAYMANAGER_XSERVER_TCP_PORT_6000_OPEN=NO',
                "/etc/dt/config/Xservers":
                ":0   Local local_uid@console root /usr/X11/bin/Xserver :0 -nobanner -nolisten tcp",
                "/usr/dt/config/Xservers":
                ":0   Local local_uid@console root /usr/X11/bin/Xserver :0 -nobanner -nolisten tcp"
            }
            for item in self.fp1:
                if os.path.exists(item[1]):
                    if item[1] == "/etc/X11/xdm/Xservers" or \
                       item[1] == "/usr/X11R6/lib/X11/xdm/Xservers":
                        if not checkPerms(item[1], [0, 0, 292], self.logger):
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            if not setPerms(item[1], [0, 0, 292], self.logger,
                                            self.statechglogger, myid):
                                success = False
                    else:
                        if not checkPerms(item[1], [0, 3, 292], self.logger):
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            if not setPerms(item[1], [0, 3, 292], self.logger,
                                            self.statechglogger, myid):
                                success = False
            for item in self.fp2:
                if os.path.exists(item[1]):
                    if item[1] == "/etc/X11/xinit/xserverrc":
                        if not checkPerms(item[1], [0, 0, 493], self.logger):
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            if not setPerms(item[1], [0, 0, 493], self.logger,
                                            self.statechglogger, myid):
                                success = False
                    elif not checkPerms(item[1], [0, 0, 420], self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(item[1], [0, 0, 420], self.logger,
                                        self.statechglogger, myid):
                            success = False
            for item in self.fixables1:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if self.writeConfig(item[1], myid, item[0], item[2]):
                    os.chown(item[1], 0, 0)
                    os.chmod(item[1], 292)
                    resetsecon(item[1])
                else:
                    success = False
            for item in self.fixables2:
                for item2 in index:
                    if item[1] == item2:
                        item[0] = index[item2]
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if self.writeConfig(item[1], myid, item[0], item[2]):
                    if item[1] == "/etc/X11/xinit/xserverrc":
                        os.chmod(item[1], 493)
                    else:
                        os.chmod(item[1], 420)
                    os.chown(item[1], 0, 0)
                    resetsecon(item[1])
                else:
                    success = False
            if self.environ.getosfamily() == "solaris":
                fp3 = "/etc/X11/gdm/gdm.conf"
                if self.editor.fixables():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                    if self.editor.fix():
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor.setEventID(myid)
                        if self.editor.commit():
                            self.detailedresults += "/etc/X11/gdm/gdm.conf \
file has been fixed\n"

                            os.chown(fp3, 0, 0)
                            os.chmod(fp3, 292)
                            resetsecon(fp3)
                        else:
                            debug = "kveditor commit did not run successfully, must return"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                    else:
                        debug = "kveditor fix did not run successfully, must return"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

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

    def checkConfig(self, regex, filepath, mult):
        contents = readFile(filepath, self.logger)
        if not contents:
            debug = filepath + " contents are blank\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return False
        if mult:
            for line in contents:
                if re.match("^#", line) or re.match(r"^\s*$", line):
                    continue
                if not re.search(regex, line.strip()):
                    return False
            return True
        else:
            for line in contents:
                if re.match("^#", line) or re.match("^\s*$", line):
                    continue
                if re.search(regex, line.strip()):
                    return True
            return False

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

    def writeConfig(self, filepath, myid, regex, mult):
        tempfile = filepath + ".tmp"
        tempstring = ""
        contents = readFile(filepath, self.logger)
        if not contents:
            debug = filepath + " contents are blank\n"
            self.logger.log(LogPriority.DEBUG, debug)
            return False
        if mult:
            for line in contents:
                if re.match("^#", line) or re.match("^\s*$", line):
                    tempstring += line
                elif not re.search(regex, line):
                    tempstring += line.strip() + " -nolisten tcp\n"
        else:
            for line in contents:
                tempstring += line
            tempstring += regex + "\n"
        if not writeFile(tempfile, tempstring, self.logger):
            return False
        event = {"eventtype": "conf", "filepath": filepath}
        self.statechglogger.recordchgevent(myid, event)
        self.statechglogger.recordfilechange(filepath, tempfile, myid)
        os.rename(tempfile, filepath)
        return True
示例#18
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
示例#19
0
class ExecShield(Rule):
    '''This class is responsible for auditing and correcting the setting of the
    ExecShield overflow prevention and the virtual address space randomizer.
    On most modern Linux distributions these are correct by default.
    
    @author: dkennel


    '''

    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 63
        self.rulename = 'ExecShield'
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.rootrequired = True
        self.rulesuccess = True
        self.comment = re.compile('^#|^;')
        self.sysctlconf = '/etc/sysctl.conf'
        self.tmpPath = '/etc/sysctl.conf.tmp'
        self.comment = re.compile('^#|^;')
        self.guidance = ['CCE-27007-4', 'CCE-26999-3']
        self.applicable = {'type': 'white',
                           'family': ['linux']}
        self.varandomcompliant = False
        self.shieldprocpath = '/proc/sys/kernel/exec-shield'
        if os.path.exists(self.shieldprocpath):
            self.execshieldapplies = True
            self.directives = {'kernel.exec-shield': '1',
                               'kernel.randomize_va_space': '2'}
        else:
            self.execshieldapplies = False
            self.directives = {'kernel.randomize_va_space': '2'}
        self.execshieldcompliant = False
        self.ExecCI = self.__initializeExecShield()
        self.sethelptext()

    def __initializeExecShield(self):
        '''
        Private method to initialize the configurationitem object for the
        EXECSHIELD bool.
        @return: configuration object instance
        @author: dkennel
        '''
        datatype = 'bool'
        key = 'EXECSHIELD'
        instructions = 'If set to yes or true the EXECSHIELD action will, ' + \
            'if needed correct the kernel settings for the ExecShield and ' + \
            'virtual address randomization functions. This should be safe ' + \
            'for all systems.'
        default = True
        myci = self.initCi(datatype, key, instructions, default)
        return myci

    def checkproc(self, procpath):
        '''Check for the value of a specific key in proc. Return that value.
        This method is designed for proc keys that only return a single value.

        :param procpath: string: fully qualified path to the element to be
        checked.
        :returns: string version of the value at that proc location.
        @author: dkennel

        '''
        myval = ''
        try:
            rhandle = open(procpath, 'r')
            myval = rhandle.read()
            rhandle.close()
            myval = myval.strip()
            return myval
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.detailedresults = 'ExecShield.checkproc: '
            self.detailedresults += traceback.format_exc()
            self.rulesuccess = False
            self.logdispatch.log(LogPriority.ERROR,
                                 ['ExecShield.checkproc',
                                  self.detailedresults])

    def report(self):
        '''Main report method. We rely on the active values in proc to make our
        compliant/not-compliant decision.
        
        @author dkennel


        :returns: self.compliant

        :rtype: bool
@change: Breen Malmberg - 1/10/2017 - minor doc string edit; return var init

        '''

        self.detailedresults = ''
        self.compliant = False
        va_path = '/proc/sys/kernel/randomize_va_space'

        try:

            if self.execshieldapplies:
                execval = int(self.checkproc(self.shieldprocpath))
                if execval == 1:
                    self.execshieldcompliant = True
                    self.detailedresults += 'Exec-Shield present and ' + \
                        'compliant\n'
                else:
                    self.detailedresults += 'Exec-Shield present but not ' + \
                        'compliant. Current value: ' + str(execval) + '\n'

            vaval = int(self.checkproc(va_path))

            if vaval == 2:
                self.varandomcompliant = True
                self.detailedresults += 'Randomize_va_space compliant\n'
            else:
                self.detailedresults += 'Randomize_va_space not compliant. ' + \
                    'Current value: ' + str(vaval) + '\n'

            if self.execshieldapplies:
                if self.execshieldcompliant and self.varandomcompliant:
                    self.compliant = True
            else:
                if self.varandomcompliant:
                    self.compliant = True

        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.detailedresults = 'ExecShield.report: '
            self.detailedresults += traceback.format_exc()
            self.rulesuccess = False
            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):
        '''Main fix method. We update the current vaules in proc via shell
        commands and set the correct settings in /etc/sysctl.conf since our
        assumption is that if it didn't pass it's because it's been overridden
        in sysctl.conf.
        
        @author: dkennel


        :returns: self.rulesuccess

        :rtype: bool
@change: Breen Malmberg - 1/10/2017 - minor doc string edit; self.rulesuccess
        now default init to True (only being set to False in the method);
        method now returns self.rulesuccess; fixed perms on file sysctl.conf
        (should be 0o600; was 420)

        '''

        self.detailedresults = ""
        self.rulesuccess = True

        if self.ExecCI.getcurrvalue():

            try:

                kvtype = "conf"
                intent = "present"
                self.editor = KVEditorStonix(self.statechglogger, self.logdispatch,
                                             kvtype, self.sysctlconf, self.tmpPath,
                                             self.directives, intent, "openeq")
                if self.execshieldapplies:
                    cmdshield = '/sbin/sysctl -w kernel.exec-shield=1'
                    subprocess.call(cmdshield, shell=True)
                cmdvarand = '/sbin/sysctl -w kernel.randomize_va_space=2'
                subprocess.call(cmdvarand, shell=True)
    
                if not self.editor.report():
                    if self.editor.fixables:
                        myid = '0063001'
                        self.editor.setEventID(myid)
                        if not self.editor.fix():
                            self.rulesuccess = False
                        elif not self.editor.commit():
                            self.rulesuccess = False
                        if self.rulesuccess:
                            os.chown(self.sysctlconf, 0, 0)
                            os.chmod(self.sysctlconf, 0o600)
                            resetsecon(self.sysctlconf)
    
            except (KeyboardInterrupt, SystemExit):
                # User initiated exit
                raise
            except Exception:
                self.detailedresults = 'ExecShield.fix: '
                self.detailedresults += traceback.format_exc()
                self.rulesuccess = False
                self.logdispatch.log(LogPriority.ERROR,
                                     self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess
示例#20
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
示例#21
0
class SecureMDNS(Rule):
    '''The Avahi daemon implements the DNS Service Discovery and Multicast DNS
    protocols, which provide service and host discovery on a network. It allows
    a system to automatically identify resources on the network, such as
    printers or web servers. This capability is also known as mDNSresponder
    and is a major part of Zeroconf networking. By default, it is enabled.
    This rule makes a number of configuration changes to the avahi service
    in order to secure it.
    @change: 04/16/2014 ekkehard ci and self.setkvdefaultscurrenthost updates


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 135
        self.rulename = 'SecureMDNS'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.compliant = False
        self.rulesuccess = True
        self.guidance = [
            'NSA(3.7.2)', 'CCE 4136-8', 'CCE 4409-9', 'CCE 4426-3',
            'CCE 4193-9', 'CCE 4444-6', 'CCE 4352-1', 'CCE 4433-9',
            'CCE 4451-1', 'CCE 4341-4', 'CCE 4358-8', 'CCE-RHEL7-CCE-TBD 2.5.2'
        ]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # set up command helper object
        self.ch = CommandHelper(self.logger)

        # init helper classes
        self.sh = ServiceHelper(self.environ, self.logger)
        self.serviceTarget = ""

        if self.environ.getostype() == "Mac OS X":
            self.ismac = True
            self.hasSIP = False
            self.plb = "/usr/libexec/PlistBuddy"
            osxversion = str(self.environ.getosver())
            versplit = osxversion.split(".")
            if len(versplit) > 2:
                minorVersion = int(versplit[1])
                releaseVersion = int(versplit[2])
            elif len(versplit) == 2:
                minorVersion = int(versplit[1])
                releaseVersion = 0
            else:
                self.logger.log(LogPriority.ERROR,
                                "Unexpected version string length")
                raise Exception
            if minorVersion == 10 and releaseVersion < 4:
                self.service = "/System/Library/LaunchDaemons/com.apple." + \
                    "discoveryd.plist"
                self.servicename = "com.apple.networking.discoveryd"
                self.parameter = "--no-multicast"
                self.pbr = self.plb + " -c Print " + self.service + \
                    " | grep 'no-multicast'"
                self.pbf = self.plb + ' -c "Add :ProgramArguments: string ' + \
                    self.parameter + '" ' + self.service
            elif minorVersion > 10:
                self.hasSIP = True
                self.service = "/System/Library/LaunchDaemons/" + \
                    "com.apple.mDNSResponder.plist"
                self.servicename = "com.apple.mDNSResponder.reloaded"
                self.parameter = "NoMulticastAdvertisements"
                self.preferences = "/Library/Preferences/" + \
                    "com.apple.mDNSResponder.plist"
                self.pbr = self.plb + " -c Print " + self.preferences + \
                    " | grep 'NoMulticastAdvertisements'"
                self.pbf = "defaults write " + self.preferences + " " + \
                    self.parameter + " -bool YES"
            else:
                self.service = "/System/Library/LaunchDaemons/" + \
                    "com.apple.mDNSResponder.plist"
                if minorVersion >= 10:
                    self.servicename = "com.apple.mDNSResponder.reloaded"
                else:
                    self.servicename = "com.apple.mDNSResponder"
                self.parameter = "-NoMulticastAdvertisements"
                self.pbr = self.plb + " -c Print " + self.service + \
                    " | grep 'NoMulticastAdvertisements'"
                self.pbf = self.plb + ' -c "Add :ProgramArguments: string ' + \
                    self.parameter + '" ' + self.service
        else:
            self.ismac = False
            # init CIs
            datatype = 'bool'
            mdnskey = 'SECUREMDNS'
            avahikey = 'DISABLEAVAHI'
            mdnsinstructions = 'To configure the Avahi server daemon ' + \
                'securely set the value of SECUREMDNS to True and the ' + \
                'value of DISABLEAVAHI to False.'
            avahiinstructions = 'To completely disable the Avahi server ' + \
                'daemon rather than configure it, set the value of ' + \
                'DISABLEAVAHI to True and the value of SECUREMDNS to False.'
            mdnsdefault = False
            avahidefault = True
            self.SecureMDNS = self.initCi(datatype, mdnskey, mdnsinstructions,
                                          mdnsdefault)
            self.DisableAvahi = self.initCi(datatype, avahikey,
                                            avahiinstructions, avahidefault)

            self.configparser = configparser.SafeConfigParser()

            self.confavahidict = {
                'use-ipv6': {
                    'section': 'server',
                    'val': 'no'
                },
                'check-response-ttl': {
                    'section': 'server',
                    'val': 'yes'
                },
                'disallow-other-stacks': {
                    'section': 'server',
                    'val': 'yes'
                },
                'disable-publishing': {
                    'section': 'publish',
                    'val': 'yes'
                },
                'disable-user-service-publishing': {
                    'section': 'publish',
                    'val': 'yes'
                },
                'publish-addresses': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-hinfo': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-workstation': {
                    'section': 'publish',
                    'val': 'no'
                },
                'publish-domain': {
                    'section': 'publish',
                    'val': 'no'
                }
            }
            self.confoptions = {
                'server': {
                    'use-ipv6': 'no',
                    'check-response-ttl': 'yes',
                    'disallow-other-stacks': 'yes'
                },
                'publish': {
                    'disable-publishing': 'yes',
                    'disable-user-service-publishing': 'yes',
                    'publish-addresses': 'no',
                    'publish-hinfo': 'no',
                    'publish-workstation': 'no',
                    'publish-domain': 'no'
                }
            }
        self.iditerator = 0

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


        :returns: bool
        @author: Breen Malmberg
        @change: dwalker - added conditional call to reportmac()
        @change: Breen Malmberg - 12/05/2017 - removed unnecessary argument
                "serviceTarget" in linux-only call to servicehelper; removed
                assignment of unused local variable serviceTarget to self.servicename
                since servicename is not assigned in the linux code logic path (which
                was resulting in variable referenced before assignment error)

        '''

        try:

            # defaults
            compliant = True
            self.detailedresults = ''
            self.rulesuccess = True

            # if system is a mac, run reportmac
            if self.ismac:
                compliant = self.reportmac()

            # if not mac os x, then run this portion
            else:
                self.editor = None
                # set up package helper object only if not mac os x
                self.pkghelper = Pkghelper(self.logger, self.environ)

                # if the disableavahi CI is set, we want to make sure it is
                # completely disabled
                if self.DisableAvahi.getcurrvalue():
                    self.package = "avahi-daemon"
                    # if avahi-daemon is still running, it is not disabled
                    if self.sh.auditService('avahi-daemon'):
                        compliant = False
                        self.detailedresults += 'DisableAvahi has been ' + \
                            'set to True, but avahi-daemon service is ' + \
                            'currently running.\n'
                    self.numdependencies = 0
                    if self.pkghelper.determineMgr() == 'yum' or \
                       self.pkghelper.determineMgr() == 'dnf':
                        self.package = "avahi"
                        # The following KVEditor for /etc/sysconfig/network is
                        # used to meet the zeroconf requirement in
                        # CCE-RHEL7-CCE-TBD 2.5.2
                        path = "/etc/sysconfig/network"
                        self.path = path
                        if os.path.exists(path):
                            tmppath = path + ".tmp"
                            data = {"NOZEROCONF": "yes"}
                            self.editor = KVEditorStonix(
                                self.statechglogger, self.logger, "conf", path,
                                tmppath, data, "present", "closedeq")
                            if not self.editor.report():
                                self.compliant = False
                                self.detailedresults += path + " does not " + \
                                    "have the correct settings.\n"
                        else:
                            self.compliant = False
                            self.detailedresults += path + " does not exist.\n"

                        self.numdependencies = \
                            self.parseNumDependencies(self.package)
                        if self.numdependencies <= 3:
                            if self.pkghelper.check(self.package):
                                compliant = False
                                self.detailedresults += 'DisableAvahi is ' + \
                                    'set to True, but Avahi is currently ' + \
                                    'installed.\n'
                        else:
                            self.detailedresults += 'Avahi has too many ' + \
                                'dependent packages. Will not attempt to ' + \
                                'remove it.\n'

                    elif self.pkghelper.determineMgr() == "zypper":
                        self.package = "avahi"
                    elif self.pkghelper.check(self.package):
                        compliant = False
                        self.detailedresults += 'DisableAvahi is ' + \
                            'set to True, but Avahi is currently ' + \
                            'installed.\n'

                # otherwise if the securemdns CI is set, we want to make sure
                # it is securely configured
                if self.SecureMDNS.getcurrvalue():

                    # if the config file is found, proceed
                    if os.path.exists('/etc/avahi/avahi-daemon.conf'):

                        kvtype = "tagconf"
                        intent = "present"
                        filepath = '/etc/avahi/avahi-daemon.conf'
                        tmpfilepath = '/etc/avahi/avahi-daemon.conf.stonixtmp'
                        conftype = "closedeq"
                        self.avahiconfeditor = KVEditorStonix(
                            self.statechglogger, self.logger, kvtype, filepath,
                            tmpfilepath, self.confoptions, intent, conftype)
                        self.avahiconfeditor.report()
                        if self.avahiconfeditor.fixables:
                            compliant = False
                            self.detailedresults += "\nThe following configuration options are missing or incorrect in " + str(
                                filepath) + ":\n" + "\n".join(
                                    self.avahiconfeditor.fixables)

                    # if config file not found, check if avahi is installed
                    else:

                        # if not installed, we can't configure anything
                        if not self.pkghelper.check('avahi'):
                            self.detailedresults += 'Avahi Daemon not ' + \
                                'installed. Cannot configure it.\n'
                            compliant = True
                            self.logger.log(LogPriority.DEBUG,
                                            self.detailedresults)

                        # if it is installed, then the config file is missing
                        else:
                            compliant = False
                            self.detailedresults += 'Avahi is installed ' + \
                                'but could not find config file in ' + \
                                'expected location.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            self.detailedresults)

            self.compliant = compliant

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

    def reportmac(self):
        '''check for configuration items needed for mac os x


        :returns: bool
        @author: Breen Malmberg
        @change: dwalker - implemented kveditor defaults

        '''
        try:
            self.detailedresults = ""
            # See if parameter is set
            self.ch.executeCommand(self.pbr)
            resultOutput = self.ch.getOutput()
            if len(resultOutput) >= 1:
                if (resultOutput[0] == ""):
                    commandsuccess = False
                    self.detailedresults += "Parameter: " + str(self.parameter) + \
                        " for service " + self.servicename + " is not set.\n"
                else:
                    commandsuccess = True
                    debug = "Parameter: " + str(self.parameter) + \
                        " for service " + self.servicename + \
                        " is set correctly."
                    self.logger.log(LogPriority.DEBUG, debug)
            else:
                commandsuccess = False
                self.detailedresults += "Parameter: " + str(self.parameter) + \
                    " for service " + self.servicename + " is not set.\n"
            # see if service is running
            if not re.match("^10.11", self.environ.getosver()):
                servicesuccess = self.sh.auditService(
                    self.service, serviceTarget=self.servicename)
            else:
                servicesuccess = self.sh.auditService(
                    self.service, serviceTarget=self.servicename)
            if servicesuccess:
                debug = "Service: " + str(self.service) + ", " + \
                    self.servicename + " audit successful."
                self.logger.log(LogPriority.DEBUG, debug)
            else:
                self.detailedresults += "Service: " + str(self.service) + \
                    ", " + self.servicename + " audit failed.\n"

            if servicesuccess and commandsuccess:
                return True
            else:
                return False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.compliant = False
            raise
        return self.compliant

    def fix(self):
        '''The fix method will apply the required settings to the system.
        self.rulesuccess will be updated if the rule does not succeed.
        
        @author: Breen Malmberg
        @change: dwalker - added statechglogger findrulechanges and deleteentry
        @changed: Breen Malmberg - 12/05/2017 - removed unnecessary servicetarget


        '''

        try:

            self.rulesuccess = True
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            self.detailedresults = ""

            # if this system is a mac, run fixmac()
            if self.ismac:
                self.rulesuccess = self.fixmac()

            # if not mac os x, run this portion
            else:
                # if DisableAvahi CI is enabled, disable the avahi service
                # and remove the package
                if self.DisableAvahi.getcurrvalue():
                    avahi = self.package
                    avahid = 'avahi-daemon'
                    if self.sh.auditService(avahid):
                        debug = "Disabling " + avahid + " service"
                        self.logger.log(LogPriority.DEBUG, debug)
                        self.sh.disableService(avahid)
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "servicehelper",
                            "servicename": avahid,
                            "startstate": "enabled",
                            "endstate": "disabled"
                        }
                        self.statechglogger.recordchgevent(myid, event)
                    if self.environ.getosfamily() == 'linux' and \
                       self.pkghelper.check(avahi):
                        if self.numdependencies <= 3:
                            self.pkghelper.remove(avahi)
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                "eventtype": "pkghelper",
                                "pkgname": avahi,
                                "startstate": "installed",
                                "endstate": "removed"
                            }
                            self.statechglogger.recordchgevent(myid, event)
                        else:
                            debug += 'Avahi package has too many dependent ' \
                                + 'packages. Will not attempt to remove.\n'
                            self.logger.log(LogPriority.DEBUG, debug)

                    if self.pkghelper.determineMgr() == 'yum' or \
                       self.pkghelper.determineMgr() == 'dnf':
                        path = self.path
                        if not os.path.exists(path):
                            if createFile(path, self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                event = {
                                    "eventtype": "creation",
                                    "filepath": path
                                }
                                self.statechglogger.recordchgevent(myid, event)
                            else:
                                self.rulesuccess = False
                                self.detailedresults += "Failed to create " + \
                                    "file: " + path + ".\n"
                        if self.editor is None:
                            tmppath = path + ".tmp"
                            data = {"NOZEROCONF": "yes"}
                            self.editor = KVEditorStonix(
                                self.statechglogger, self.logger, "conf", path,
                                tmppath, data, "present", "closedeq")
                        if not self.editor.report():
                            if self.editor.fix():
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                self.editor.setEventID(myid)
                                if not self.editor.commit():
                                    self.rulesuccess = False
                                    self.detailedresults += "Could not " + \
                                        "commit changes to " + path + ".\n"
                            else:
                                self.rulesuccess = False
                                self.detailedresults += "Could not fix " + \
                                    "file " + path + ".\n"

                # if SecureMDNS CI is enabled, configure avahi-daemon.conf
                if self.SecureMDNS.getcurrvalue():
                    # if config file is present, proceed
                    avahiconf = '/etc/avahi/avahi-daemon.conf'
                    if os.path.exists(avahiconf):
                        if self.avahiconfeditor.fixables:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            self.avahiconfeditor.setEventID(myid)
                            if not self.avahiconfeditor.fix():
                                self.rulesuccess = False
                                debug = "KVEditor fix for " + avahiconf + \
                                    "failed"
                                self.logger.log(LogPriority.DEBUG, debug)
                            elif not self.avahiconfeditor.commit():
                                self.rulesuccess = False
                                debug = "KVEditor commit for " + avahiconf + \
                                    "failed"
                                self.logger.log(LogPriority.DEBUG, debug)

                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        setPerms(avahiconf, [0, 0, 0o644], self.logger,
                                 self.statechglogger, myid)
                        resetsecon(avahiconf)

                    # if config file is not present and avahi not installed,
                    # then we can't configure it
                    else:
                        if not self.pkghelper.check(avahi):
                            debug = 'Avahi Daemon not installed. ' + \
                                'Cannot configure it.'
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.detailedresults += 'Avahi daemon ' + \
                                'installed, but could not locate the ' + \
                                'configuration file for it.\n'
                            self.rulesuccess = False

        except IOError:
            self.detailedresults += '\n' + traceback.format_exc()
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def fixmac(self):
        '''apply fixes needed for mac os x
        
        @author: Breen Malmberg
        @change: dwalker - implemented kveditor instead of direct editing


        '''
        try:
            self.detailedresults = ""
            success = True
            # See if parameter is set
            self.ch.executeCommand(self.pbr)
            resultOutput = self.ch.getOutput()
            if len(resultOutput) >= 1:
                if (resultOutput[0] == ""):
                    fixit = True
                else:
                    fixit = False
            else:
                fixit = True
            # Add parameter
            if fixit:
                # Due to weaknesses in using PlistBuddy and defaults to delete
                # from plists, as well as shortcomings in STONIX's state change
                # logging, we will record this change as a file deletion.
                # If the rule's undo is run on OS X, it will restore the
                # previous version of this file.
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if self.hasSIP:
                    self.statechglogger.recordfiledelete(
                        self.preferences, myid)
                else:
                    self.statechglogger.recordfiledelete(self.service, myid)
                self.ch.executeCommand(self.pbf)
                resultOutput = self.ch.getOutput()
                errorcode = self.ch.getReturnCode()
                if errorcode == 0:
                    debug = self.parameter + " was set successfully!"
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    self.detailedresults += self.parameter + \
                        " was not set successfully!\n"
                    self.statechglogger.deleteentry(myid)
                    success = False
            else:
                debug = self.parameter + " was already set!"
                self.logger.log(LogPriority.DEBUG, debug)
            # Reload Service
            if success:
                success = self.sh.reloadService(self.service,
                                                serviceTarget=self.servicename)
                if success:
                    debug = "Service: " + str(self.service) + ", " + \
                        self.servicename + " was reloaded successfully."
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    debug = "Service: " + str(self.service) + ", " + \
                        self.servicename + " reload failed!"
                    self.logger.log(LogPriority.DEBUG, debug)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            success = False
            raise
        return success

    def parseNumDependencies(self, pkgname):
        '''parse output of yum command to determine number of dependent packages
        to the given pkgname

        :param pkgname: 
        :returns: int
        @author: Breen Malmberg

        '''
        numdeps = 0
        flag = 0

        try:

            if self.pkghelper.determineMgr() == 'zypper':
                command = ['zypper', 'info', '--requires', pkgname]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if flag:
                        numdeps += 1
                    if re.search('Requires:', line):
                        flag = 1

            elif self.pkghelper.determineMgr() == 'yum':
                command = ['yum', '--assumeno', 'remove', pkgname]
                self.ch.wait = False
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Dependent packages\)', line):
                        sline = line.split('(+')
                        if len(sline) < 2:
                            return numdeps
                        cline = [
                            int(s) for s in sline[1].split() if s.isdigit()
                        ]
                        numdeps = int(cline[0])

            elif self.pkghelper.determineMgr() == 'dnf':
                command = ['dnf', '--assumeno', 'remove', pkgname]
                self.ch.wait = False
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Dependent packages\)', line):
                        sline = line.split('(+')
                        if len(sline) < 2:
                            return numdeps
                        cline = [
                            int(s) for s in sline[1].split() if s.isdigit()
                        ]
                        numdeps = int(cline[0])

            elif self.pkghelper.determineMgr() == 'apt-get':
                command = ['apt-cache', 'depends', pkgname]
                self.ch.executeCommand(command)
                output = self.ch.getOutput()
                for line in output:
                    if re.search('Depends:', line):
                        numdeps += 1
            else:
                self.detailedresults += 'Unable to detect package manager\n'
                return numdeps

        except (IOError, OSError):
            self.detailedresults += 'Specified package: ' + str(pkgname) + \
                ' not found.\n'
            return numdeps
        except Exception:
            raise
        return numdeps
示例#22
0
class SecureIPV4(Rule):

    def __init__(self, config, environ, logger, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 15
        self.cmdhelper = CommandHelper(self.logger)
        self.rulename = "SecureIPV4"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        if self.environ.getostype() == "Mac OS X":
            self.networkTuning2 = self.__InitializeNetworkTuning2()
        else:
            self.networkTuning1 = self.__InitializeNetworkTuning1()
            self.networkTuning2 = self.__InitializeNetworkTuning2()
        self.guidance = ["NSA 2.5.1.1", "NSA 2.5.1.2"]
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        self.iditerator = 0
        self.ch = CommandHelper(self.logger)

    def __InitializeNetworkTuning1(self):
        '''Private method to initialize the configurationitem object for the
        NetworkTuning1 bool.
        @return: configurationitem object instance'''

        datatype = 'bool'
        key = "NETWORKTUNING1"
        instructions = "Network Parameter Tuning. You should not need " + \
            "to override this under normal circumstances."
        default = True
        ci = self.initCi(datatype, key, instructions, default)
        return ci

    def __InitializeNetworkTuning2(self):
        '''Private method to initialize the configurationitem object for the
        NetworkTuning2 bool.
        @return: configurationitem object instance'''

        key = "NETWORKTUNING2"
        instructions = "Additional network parameters. Set this to False " + \
            "if you are running a router or a bridge. Also, in rare " + \
            "cases, you may need to set this to False for VMware (if you " + \
            "are using normal VMware routing, True should be fine)."
        default = True
        datatype = "bool"
        ci = self.initCi(datatype, key, instructions, default)
        return ci

    def report(self):
        '''Main parent report method that calls the sub report methods


        :returns: bool

        '''
        try:
            self.detailedresults = ""
            if self.environ.getosfamily() == "linux":
                self.path = "/etc/sysctl.conf"
                self.tmpPath = "/etc/sysctl.conf.tmp"
                self.original = readFile(self.path, self.logger)
                rep1success = self.reportLinux1()
                rep2success = self.reportLinux2()
            elif self.environ.getosfamily() == "freebsd":
                self.path = "/etc/sysctl.conf"
                self.tmpPath = "/etc/sysctl.conf.tmp"
                self.original = readFile(self.path, self.logger)
                rep1success = self.reportFreebsd1()
                rep2success = self.reportFreebsd2()
            elif self.environ.getostype() == "Mac OS X":
                self.path = "/private/etc/sysctl.conf"
                self.tmpPath = "/private/etc/sysctl.conf.tmp"
                rep1success = True
                rep2success = self.reportMac()
            if rep1success and rep2success:
                self.compliant = True
            else:
                self.compliant = False
        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
        return self.rulesuccess

    def reportLinux1(self):
        '''Linux specific report method that ensures the items in fileContents
        exist in /etc/sysctl.conf.  Sets self.compliant to True if all items
        exist in the file.  Returns True if successful in updating the file


        :returns: bool

        '''
        compliant = True
        if not os.path.exists(self.path):
            self.detailedresults += self.path + " does not exist\n"
            compliant = False
        else:
            lfc = {"net.ipv4.conf.all.secure_redirects": "0",
                   "net.ipv4.conf.all.accept_redirects": "0",
                   "net.ipv4.conf.all.rp_filter": "1",
                   "net.ipv4.conf.all.log_martians": "1",
                   "net.ipv4.conf.all.accept_source_route": "0",
                   "net.ipv4.conf.default.accept_redirects": "0",
                   "net.ipv4.conf.default.secure_redirects": "0",
                   "net.ipv4.conf.default.rp_filter": "1",
                   "net.ipv4.conf.default.accept_source_route": "0",
                   "net.ipv4.tcp_syncookies": "1",
                   "net.ipv4.icmp_echo_ignore_broadcasts": "1",
                   "net.ipv4.tcp_max_syn_backlog": "4096"}
            editor = KVEditorStonix(self.statechglogger, self.logger,
                                    "conf", self.path, self.tmpPath, lfc,
                                    "present", "openeq")
            if not editor.report():
                self.detailedresults += self.path + " is not configured " + \
                    "correctly for configuration item 1\n"
                compliant = False
            if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions are incorrect on " + \
                    self.path + "\n"
                compliant = False
        for key in lfc:
            self.ch.executeCommand("/sbin/sysctl " + key)
            retcode = self.ch.getReturnCode()

            if retcode != 0:
                self.detailedresults += "Failed to get value of core dumps configuration with sysctl command\n"
                errmsg = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errmsg)
                compliant = False
            else:
                output = self.ch.getOutputString()
                if output.strip() != key + " = " + lfc[key]:
                    compliant = False
                    self.detailedresults += "sysctl output has incorrect value: " + \
                        output + "\n"
        return compliant

    def reportLinux2(self):
        '''Linux specific report method2 that ensures the items in fileContents
        exist in /etc/sysctl.conf.  Sets self.compliant to True if all items
        exist in the file.  Returns True if successful in updating the file


        :returns: bool

        '''
        compliant = True
        if not os.path.exists(self.path):
            compliant = False
        else:
            lfc = {"net.ipv4.conf.default.send_redirects": "0",
                   "net.ipv4.conf.all.send_redirects": "0",
                   "net.ipv4.ip_forward": "0"}
            editor = KVEditorStonix(self.statechglogger, self.logger,
                                    "conf", self.path, self.tmpPath,
                                    lfc, "present", "openeq")
            if not editor.report():
                self.detailedresults += self.path + " is not configured " + \
                    "correctly for configuration item 2\n"
                compliant = False

        for key in lfc:
            self.ch.executeCommand("/sbin/sysctl " + key)
            retcode = self.ch.getReturnCode()

            if retcode != 0:
                self.detailedresults += "Failed to get value of core dumps configuration with sysctl command\n"
                errmsg = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errmsg)
                compliant = False
            else:
                output = self.ch.getOutputString()
                if output.strip() != key + " = " + lfc[key]:
                    compliant = False
                    self.detailedresults += "sysctl output has incorrect value: " + \
                        output + "\n"
        return compliant

    def reportMac(self):
        '''Mac specific report method1 that ensures the items in fileContents
        exist in /etc/sysctl.conf.  Sets self.compliant to True if all items
        exist in the file.


        :returns: compliant

        :rtype: bool
@author: dwalker
@change: Breen Malmberg - 1/10/2017 - minor doc string adjustments; fixed
        permissions on file /etc/sysctl.conf (needs to be 0o600; was 0o644);
        try/except

        '''

        compliant = True

        try:
            self.editor = None

            if not os.path.exists(self.path):
                self.detailedresults += self.path + " does not exist\n"
                compliant = False
            else:
                mfc = {"net.inet.ip.forwarding": "0",
                       "net.inet.ip.redirect": "0"}
                kvtype = "conf"
                intent = "present"
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             kvtype, self.path, self.tmpPath, mfc,
                                             intent, "closedeq")
                if not self.editor.report():
                    self.detailedresults += self.path + " is not " + \
                        "configured correctly\n"
                    compliant = False
                else:
                    self.detailedresults += self.path + " is " + \
                        "configured correctly\n"
                if not checkPerms(self.path, [0, 0, 0o600], self.logger):
                    self.detailedresults += "Permissions are incorrect on " + \
                        self.path + ": Expected 644, found " + \
                        str(getOctalPerms(self.path)) + "\n"
                    compliant = False

        except Exception:
            raise

        return compliant

    def reportFreebsd1(self):
        '''Freebsd specific report method1 that ensures the items in the file
        exist in /etc/sysctl.conf.  Sets self.compliant to True if all items
        exist in the file.  Returns True if successful in updating the file


        :returns: bool

        '''
        compliant = True
        if not os.path.exists(self.path):
            self.detailedresults += self.path + " does not exist\n"
            compliant = False
        else:
            ffc = {"net.inet.icmp.bmcastecho": "0",
                   "net.inet.ip.redirect": "0",
                   "net.inet.icmp.maskrepl": "0",
                   "net.inet.ip.sourceroute": "0",
                   "net.inet.ip.accept_sourceroute": "0",
                   "net.inet.tcp.syncookies": "1"}
            kvtype = "conf"
            intent = "present"
            self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                         kvtype, self.path, self.tmpPath, ffc,
                                         intent, "openeq")
            if not self.editor.report():
                compliant = False
            if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions are incorrect on " + \
                    self.path + ": Expected 644, found " + \
                    str(getOctalPerms(self.path)) + "\n"
                compliant = False
        return compliant

    def reportFreebsd2(self):
        '''Freebsd specific report method1 that ensures the items in
        fileContents exist in /etc/sysctl.conf. Sets self.compliant to True
        if all items exist in the file. Returns True if successful in updating
        the file


        :returns: bool

        '''
        compliant = True
        if not os.path.exists(self.path):
            self.detailedresults += self.path + " does not exist\n"
            compliant = False
        else:
            ffc = {"net.inet.ip.forwarding": "0",
                   "net.inet.ip.fastforwarding": "0"}
            if not self.networkTuning1.getcurrvalue():
                kvtype = "conf"
                intent = "present"
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             kvtype, self.path, self.tmpPath,
                                             ffc, intent, "closedeq")
            else:
                self.editor.setData(ffc)
            if not self.editor.report():
                compliant = False
            if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions are incorrect on " + \
                    self.path + ": Expected 644, found " + \
                    str(getOctalPerms(self.path)) + "\n"
                compliant = False
        return compliant

    def fix(self):
        '''Main parent fix method that calls the sub fix methods


        :returns: bool

        '''
        try:
            self.detailedresults = ""
            success = True
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            if self.environ.getosfamily() == "linux":
                if self.networkTuning1 and self.networkTuning2:
                    success = self.fixLinux()
                else:
                    self.detailedresults += "Required CI has not been initialized."
                    success = False
            elif self.environ.getosfamily() == "freebsd":
                if self.networkTuning1 and self.networkTuning2:
                    success = self.fixFreebsd()
                else:
                    self.detailedresults += "Required CI has not been initialized."
                    success = False
            elif self.environ.getosfamily() == "darwin":
                if self.networkTuning2:
                    success = self.fixMac()
                else:
                    self.detailedresults += "Required CI has not been initialized."
                    success = False
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
            success = False
        self.formatDetailedResults("fix", success, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)

        self.rulesuccess = success
        return success

    def fixLinux(self):
        success = True
        created = False
        debug = ""
        sysctl = "/etc/sysctl.conf"
        tmpfile = sysctl + ".tmp"
        if not os.path.exists(sysctl):
            if createFile(sysctl, self.logger):
                created = True
                setPerms(sysctl, [0, 0, 0o644], self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation",
                         "filepath": sysctl}
                self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Could not create file " + self.path + \
                    "\n"
                self.formatDetailedResults("fix", False,
                                           self.detailedresults)
        if not checkPerms(sysctl, [0, 0, 0o644], self.logger):
            if not created:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(sysctl, [0, 0, 0o644], self.logger,
                                self.statechglogger, myid):
                    success = False
        lfc = {}
        if self.networkTuning1 and self.networkTuning1.getcurrvalue():
            lfc.update({"net.ipv4.conf.all.secure_redirects": "0",
                        "net.ipv4.conf.all.accept_redirects": "0",
                        "net.ipv4.conf.all.rp_filter": "1",
                        "net.ipv4.conf.all.log_martians": "1",
                        "net.ipv4.conf.all.accept_source_route": "0",
                        "net.ipv4.conf.default.accept_redirects": "0",
                        "net.ipv4.conf.default.secure_redirects": "0",
                        "net.ipv4.conf.default.rp_filter": "1",
                        "net.ipv4.conf.default.accept_source_route": "0",
                        "net.ipv4.tcp_syncookies": "1",
                        "net.ipv4.icmp_echo_ignore_broadcasts": "1",
                        "net.ipv4.tcp_max_syn_backlog": "4096"})
        if self.networkTuning2 and self.networkTuning2.getcurrvalue():
            lfc.update({"net.ipv4.conf.default.send_redirects": "0",
                        "net.ipv4.conf.all.send_redirects": "0",
                        "net.ipv4.ip_forward": "0"})
        self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                         "conf", sysctl, tmpfile,
                                         lfc, "present", "openeq")
        if not self.editor.report():
            if self.editor.fixables:
                # If we did not create the file, set an event ID for the
                # KVEditor's undo event to record the file write
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                if not self.editor.fix():
                    success = False
                    debug = "KVEditor fix of " + self.path + \
                                            " was not successful\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                elif not self.editor.commit():
                    success = False
                    debug = "KVEditor commit to " + \
                        self.path + " was not successful\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                # permissions on file are incorrect
                if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                    if not setPerms(self.path, [0, 0, 0o644], self.logger):
                        self.detailedresults += "Could not set permissions on " + \
                                                self.path + "\n"
                        success = False
                resetsecon(self.path)

        # here we also check the output of the sysctl command for each key
        # to cover all bases
        for key in lfc:
            if self.ch.executeCommand("/sbin/sysctl " + key):
                output = self.ch.getOutputString().strip()
                if not re.search(lfc[key] + "$", output):
                    undovalue = output[-1]
                    self.ch.executeCommand("/sbin/sysctl -w " + key + "=" + lfc[key])
                    retcode = self.ch.getReturnCode()
                    if retcode != 0:
                        success = False
                        self.detailedresults += "Failed to set " + key + " = " + lfc[key] + "\n"
                        errmsg = self.ch.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errmsg)
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        command = "/sbin/sysctl -w " + key + "=" + undovalue
                        event = {"eventtype": "commandstring",
                                 "command": command}
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.detailedresults += "Unable to get value for " + key + "\n"
                success = False
        # at the end do a print and ignore any key errors to ensure
        # the new values are read into the kernel
        self.ch.executeCommand("/sbin/sysctl -q -e -p")
        retcode2 = self.ch.getReturnCode()
        if retcode2 != 0:
            success = False
            self.detailedresults += "Failed to load new sysctl configuration from config file\n"
            errmsg2 = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg2)
        return success

    def fixMac(self):
        '''run fix actions for mac systems


        :returns: success

        :rtype: bool
@author: dwalker
@change: Breen Malmberg - 1/10/2017 - added doc string; try/except;
        fixed perms for file sysctl.conf (should be 0o600; was 0o644)

        '''

        success = True

        try:

            if not os.path.exists(self.path):
                if createFile(self.path, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation", "filepath": self.path}
                    self.statechglogger.recordchgevent(myid, event)
                else:
                    return False
            if self.networkTuning2.getcurrvalue():
                if not self.editor:
                    mfc = {"net.inet.ip.forwarding": "0",
                           "net.inet.ip.redirect": "0"}
                    kvtype = "conf"
                    intent = "present"
                    self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                                 kvtype, self.path, self.tmpPath,
                                                 mfc, intent, "closedeq")
                if not self.editor.report():
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                    if self.editor.fix():
                        if not self.editor.commit():
                            success = False
                            self.detailedresults += "KVEditor commit to " + \
                                self.path + " was not successful\n"
                    else:
                        success = False
                        self.detailedresults += "KVEditor fix of " + self.path + \
                            " was not successful\n"
                    resetsecon(self.path)
                if not checkPerms(self.path, [0, 0, 0o600], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.path, [0, 0, 0o600], self.logger,
                                    self.statechglogger, myid):
                        self.detailedresults += "Could not set permissions on " + \
                            self.path + "\n"
                        success = False

        except Exception:
            raise

        return success

    def fixFreebsd(self):
        if not checkPerms(self.path, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.path, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                return False
        if self.networkTuning1.getcurrvalue() or \
                self.networkTuning2.getcurrvalue():
            if self.editor.fixables:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor.setEventID(myid)
                if not self.editor.fix():
                    return False
                elif not self.editor.commit():
                    return False
                os.chown(self.path, 0, 0)
                os.chmod(self.path, 0o644)
                resetsecon(self.path)
                cmd = ["/usr/sbin/service", "sysctl", "restart"]
                self.ch.executeCommand(cmd)
                if self.ch.getReturnCode() != 0:
                    self.detailedresults = "Unable to restart sysctl\n"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return False
                else:
                    return True
            else:
                return True
示例#23
0
class DisableGDMAutoLogin(Rule):
    """
    The GNOME Display Manager (GDM) can allow users to automatically login without user interaction or credentials. User
should always be required to authenticate themselves to the system that they are authorized to use.
    """

    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 = 192
        self.rulename = 'DisableGDMAutoLogin'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {'type': 'white',
                           'family': ['linux']}
        self.guidance = ['RHEL 7 STIG CCE-80104-3']
        datatype = 'bool'
        key = 'DISABLEGDMAUTOLOGIN'
        instructions = """To disable this rule set the value of DISABLEGDMAUTOLOGIN to False."""
        default = True
        self.PrimaryCI = self.initCi(datatype, key, instructions, default)

    def report(self):
        """

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

        self.detailedresults = ""
        self.compliant = True
        basedir = "/etc/gdm/"

        try:

            # if there is no gdm folder then there is likely no reason to configure it
            if not os.path.isdir(basedir):
                self.logger.log(LogPriority.DEBUG, "Rule does not apply to this system in its current state")
                return self.compliant

            kvtype = "tagconf"
            path = basedir + "custom.conf"
            tmppath = path + ".stonixtmp"
            data = {"daemon": {"AutomaticLoginEnable": "False",
                               "TimedLoginEnable": "False"}}
            intent = "present"
            delimiter = "closedeq"
            self.gdm_editor = KVEditorStonix(self.statechglogger, self.logger, kvtype, path, tmppath, data, intent, delimiter)

            if not self.gdm_editor.report():
                self.compliant = False
                try:
                    self.detailedresults += "\nThe following config options in " + str(path) + " are incorrect:\n" + "\n".join(self.gdm_editor.fixables)
                except:
                    self.detailedresults += "\nOne or more config options in " + str(path) + " are incorrect"

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

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

        self.detailedresults = ""
        self.rulesuccess = True
        basedir = "/etc/gdm/"

        try:

            # if there is no gdm folder then there is likely no reason to configure it
            if not os.path.isdir(basedir):
                self.logger.log(LogPriority.DEBUG, "Rule does not apply to this system in its current state")
                return self.rulesuccess

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

            if not self.rulesuccess:
                self.detailedresults += "\nFailed to disable GDM Auto Login"

        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
示例#24
0
class DisableThumbnailers(Rule):
    """disable the thumbnail creation feature in nautilus/gnome"""

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

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

        Rule.__init__(self, config, environ, logdispatcher, statechglogger)
        self.logger = logdispatcher
        self.rulenumber = 111
        self.mandatory = True
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd']}
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 2.2.2.6"]
        self.rulename = "DisableThumbnailers"
        self.rulesuccess = True
        self.sethelptext()

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

    def localize(self):
        """
        set paths based on what versions of which utilities exist on the system

        :return: void
        """

        self.gsettings = "/usr/bin/gsettings"
        self.gconf = "/usr/bin/gconftool-2"
        self.dconf = "/usr/bin/dconf"
        self.getcmd = ""
        self.setcmd = ""
        self.updatecmd = ""
        packages = ["gnome", "gdm", "gdm3", "gnome3"]
        self.lockfile = "/etc/dconf/db/local.d/locks/stonix-thumbnailers"
        self.lockthumbnails = {"/org/gnome/desktop/thumbnailers/disable-all":""}

        if os.path.exists(self.gsettings):
            self.getcmd = self.gsettings + " get org.gnome.desktop.thumbnailers disable-all"
            self.setcmd = self.gsettings + " set org.gnome.desktop.thummailerss disable-all true"
        elif os.path.exists(self.gconf):
            self.getcmd = self.gconf + " --get /schemas/org/gnome/desktop/thumbnailers/disable_all"
            self.setcmd = self.gconf + " --type bool --set /schemass/org/gnome/desktop/thumbnailers/disable_all true"
        if os.path.exists(self.dconf):
            self.updatecmd = self.dconf + " update"

        self.ch = CommandHelper(self.logger)
        self.ph = Pkghelper(self.logger, self.environ)
        self.gnome_installed = False
        if self.environ.getosname() != "Mac OS":
            if [self.ph.check(p) for p in packages]:
                self.gnome_installed = True

    def report(self):
        """check the gdm/gnome setting for thumbnailers to determine
        if it is off or on. report compliant if it is off,
        non-compliant if it is on.


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

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

        try:
            self.localize()
            if self.gnome_installed:
                # This portion of the code is relevant to user context
                if self.environ.geteuid() != 0:
                    self.ch.executeCommand(self.getcmd)
                    if not self.ch.findInOutput("true"):
                        self.compliant = False
                        self.detailedresults += "\nGnome thumbnailers are enabled"
                # This portion of the code is relevant to root context
                else:
                    if not self.checkLockFile():
                        self.compliant = False
            else:
                self.logger.log(LogPriority.DEBUG, "Gnome is not installed. Nothing to check.")

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            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 checkLockFile(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

        kvtype = "conf"
        path = self.lockfile
        tmppath = self.lockfile + ".stonixtmp"
        intent = "present"
        conftype = "space"

        self.lock_editor = KVEditorStonix(self.statechglogger, self.logdispatch, kvtype, path, tmppath, self.lockthumbnails, intent, conftype)
        if not self.lock_editor.report():
            compliant = False
            if self.lock_editor.fixables:
                self.detailedresults += "\nThe following required configuration lines were not found in the gnome lock file:\n" + "\n".join(self.lock_editor.fixables)
            else:
                self.detailedresults += "\nOne or more required configuration lines were not found in the gnome lock file"

        return compliant

    def fix(self):
        """
        set the value of schema thumbnailers disable-all to true
        and create the thumbnailers lock file if it doesn't exist

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

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

        try:

            if self.ci.getcurrvalue():
                # This portion of the code is run in user context
                if self.environ.geteuid() != 0:
                    if not self.setValue():
                        self.rulesuccess = False
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "commandstring",
                                 "command": re.sub("true", "false", self.setcmd)}
                        self.statechglogger.recordchgevent(myid, event)
                # This portion of the code has to be run in root context
                else:
                    if not self.setLockFile():
                        self.rulesuccess = False

                if os.path.exists(self.dconf):
                    self.ch.executeCommand(self.updatecmd)
                    if self.ch.getReturnCode() == 0:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "commandstring",
                                 "command": self.updatecmd}
                        self.statechglogger.recordchgevent(myid, event)
            else:
                self.logger.log(LogPriority.DEBUG, "CI not enabled. Fix was not performed.")

        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.logger.log(LogPriority.INFO, self.detailedresults)

        return self.rulesuccess

    def setValue(self):
        """
        set the thumbnailers disable-all configuration value to true

        :return: success
        :rtype: bool
        """

        success = True

        self.ch.executeCommand(self.setcmd)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            errstr = self.ch.getErrorString()
            self.detailedresults += "\n" + errstr

        return success

    def setLockFile(self):
        """
        create the lock file for disable thumbnailers (if running in root context)

        :return: success - True if file is created; False if not
        :rtype: bool
        """

        success = True

        if not os.path.isdir("/etc/dconf/db/local.d/locks"):
            try:
                os.makedirs("/etc/dconf/db/local.d/locks", 0o755)
            except Exception:
                pass
        try:

            if not self.lock_editor.fix():
                self.detailedresults += "\nFailed to lock thumbnailers setting"
                success = False
            elif not self.lock_editor.commit():
                self.detailedresults += "\nFailed to lock thumbnailers setting"
                success = False

        except Exception:
            success = False

        if success:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "conf",
                     "filepath": self.lockfile}
            self.statechglogger.recordchgevent(myid, event)

        return success
示例#25
0
class RestrictAdminSSH(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 269
        self.rulename = "RestrictAdminSSH"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        datatype = 'bool'
        key = 'RESTRICTADMINSSH'
        instructions = '''To disable this rule set the value of
RESTRICTADMINSSH to False.'''
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.guidance = []
        self.ssh = {"DenyGroups": "admin"}
        self.iditerator = 0
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

    def usesSip(self):
        '''Determines whether this is Mac OS X >= v10.11
        @author: Eric Ball


        :returns: True if this is Mac OS X >= v10.11

        '''
        if self.environ.getosfamily() == "darwin":
            versplit = self.environ.getosver().split(".")
            verlist = []
            for num in versplit:
                verlist.append(int(num))
            if verlist[0] >= 10 and verlist[1] >= 11:
                return True
        return False

    def report(self):
        try:
            results = ""
            compliant = True
            path1 = "/private/etc/sshd_config"
            path2 = "/private/etc/ssh/sshd_config"
            if self.usesSip():
                if os.path.exists(path2):
                    self.path = path2
                elif os.path.exists(path1):
                    self.path = path1
                else:
                    compliant = False
                    results += "Could not find path to sshd_config file\n"
            else:
                if os.path.exists(path1):
                    self.path = path1
                elif os.path.exists(path2):
                    self.path = path2
                else:
                    compliant = False
                    results += "Could not find path to sshd_config file\n"
            self.tmppath = self.path + ".tmp"
            if os.path.exists(self.path):
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             "conf", self.path, self.tmppath,
                                             self.ssh, "present", "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"
            self.detailedresults = results
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return

            results = ""
            success = True

            # 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 os.path.exists(self.path):
                if not checkPerms(self.path, [0, 0, 0o644], self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.path, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
                        results += "Could not set permissions on " + \
                            self.path + "\n"
                if self.editor.fixables or self.editor.removeables:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                    if not self.editor.fix():
                        debug = "kveditor fix did not run successfully\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                    elif not self.editor.commit():
                        debug = "kveditor commit did not run  successfully\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                    os.chown(self.path, 0, 0)
                    os.chmod(self.path, 0o644)
                    resetsecon(self.path)
            else:
                success = False
                results += "Could not find path to sshd_config\n"
            self.detailedresults = results
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            success = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", success, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
示例#26
0
class DisablePrelinking(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 89
        self.rulename = "DisablePrelinking"
        self.sethelptext()
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.applicable = {'type': 'white', 'family': ['linux']}

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

        self.guidance = ["CCE-RHEL7-CCE-TBA 2.1.3.1.2"]
        self.iditerator = 0

        self.ch = CommandHelper(self.logger)
        if re.search("debian|ubuntu", self.environ.getostype().lower()):
            self.isDebian = True
        else:
            self.isDebian = False

    def report(self):
        try:
            if self.isDebian:
                path = "/etc/default/prelink"
            else:
                path = "/etc/sysconfig/prelink"
            self.path = path
            prelink = "/usr/sbin/prelink"
            self.compliant = True
            self.detailedresults = ""

            if os.path.exists(path):
                tmppath = path + ".tmp"
                data = {"PRELINKING": "no"}
                self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                             "conf", path, tmppath, data,
                                             "present", "closedeq")
                if not self.editor.report():
                    self.compliant = False
                    self.detailedresults += path + " does not have the " + \
                        "correct settings.\n"
            else:
                self.compliant = False
                self.detailedresults += path + " does not exist.\n"

            if os.path.exists(prelink):
                self.ch.executeCommand([prelink, "-p"])
                output = self.ch.getOutputString()
                splitout = output.split()
                try:
                    if len(splitout) > 0:
                        numPrelinks = int(splitout[0])  # Potential ValueError
                        if numPrelinks > 0:
                            self.compliant = False
                            self.detailedresults += "There are currently " + \
                                str(numPrelinks) + " prelinked binaries.\n"
                except ValueError:
                    debug = "Unexpected result from " + prelink + ". This " + \
                        "does not affect compliance."
                    self.logger.log(LogPriority.DEBUG, debug)
                    self.detailedresults += debug + "\n"

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

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            path = self.path
            tmppath = path + ".tmp"
            prelinkCache = "/etc/prelink.cache"
            self.detailedresults = ""
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            if not os.path.exists(path):
                if createFile(path, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation", "filepath": path}
                    self.statechglogger.recordchgevent(myid, event)
                else:
                    success = False
                    self.detailedresults += "Failed to create file: " + \
                        path + "\n"

                if writeFile(tmppath, "PRELINKING=no", self.logger):
                    os.rename(tmppath, path)
                    resetsecon(path)
                else:
                    success = False
                    self.detailedresults += "Failed to write settings " + \
                        "to file: " + path + "\n"
            elif not self.editor.report():
                if self.editor.fix():
                    if self.editor.commit():
                        self.detailedresults += "Changes successfully " + \
                            "committed to " + path + "\n"
                    else:
                        success = False
                        self.detailedresults += "Changes could not be " + \
                            "committed to " + path + "\n"
                else:
                    success = False
                    self.detailedresults += "Could not fix file " + path + "\n"

            # Although the guidance and documentation recommends using "prelink
            # -ua" command, testing has shown this command to be completely
            # unreliable. Instead, the prelink cache will be removed entirely.
            if os.path.exists(prelinkCache):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.statechglogger.recordfiledelete(prelinkCache, myid)
                os.remove(prelinkCache)

            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
示例#27
0
class ConfigureSudo(Rule):
    """

    """
    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 = 56
        self.rulename = "ConfigureSudo"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.guidance = ["NSA 2.3.1.3"]
        self.applicable = {
            'type': 'white',
            'family': 'linux',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        datatype2 = 'bool'
        key2 = 'CONFIGURESUDO'
        instructions2 = """To disable this rule set the value of CONFIGURESUDO to False."""
        default2 = True
        self.primary_ci = self.initCi(datatype2, key2, instructions2, default2)

        self.localization()

    def localization(self):
        """
        set up class variables, specific to OS type
        """

        self.logger.log(LogPriority.DEBUG, "Running localization() method...")

        ostype = self.environ.getostype()
        osname = self.environ.getosname()

        if ostype == "darwin":
            self.sudoers_file = "/private/etc/sudoers"
        else:
            self.sudoers_file = "/etc/sudoers"

        if ostype == "darwin":
            self.sudoers_opts = {
                "root": "ALL = (ALL) ALL",
                "%admin": "ALL = (ALL) ALL"
            }
        elif osname == "Debian":
            self.sudoers_opts = {
                "root": "ALL=(ALL:ALL) ALL",
                "%sudo": "ALL=(ALL:ALL) ALL"
            }
        elif osname == "Ubuntu":
            self.sudoers_opts = {
                "root": "ALL=(ALL:ALL) ALL",
                "%admin": "ALL=(ALL) ALL",
                "%sudo": "ALL=(ALL:ALL) ALL"
            }
        else:
            self.sudoers_opts = {
                "root": "ALL=(ALL) ALL",
                "%wheel": "ALL=(ALL) ALL"
            }

    def report(self):
        """
        ConfigureScreenLocking.report() method to report whether system is
        configured with a sudoers group.
        @author: dwalker

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

        try:

            self.detailedresults = ""
            self.compliant = True

            if not self.sudoers_file:
                self.detailedresults += "\nCan't find sudoers file!"
                self.compliant = False
            elif not os.path.isfile(self.sudoers_file):
                self.detailedresults += "\nCan't find sudoers file!"
                self.compliant = False
            else:
                self.sudoers_backup = self.sudoers_file + ".stonixbak"
                tmppath = self.sudoers_file + ".stonixtmp"
                self.sudoers_editor = KVEditorStonix(self.statechglogger,
                                                     self.logger, "conf",
                                                     self.sudoers_file,
                                                     tmppath,
                                                     self.sudoers_opts,
                                                     "present", "space")
                if not self.sudoers_editor.report():
                    if self.sudoers_editor.fixables:
                        self.detailedresults += "\nThe following configuration options are missing or incorrect in the sudoers file:\n" + "\n".join(
                            self.sudoers_editor.fixables)
                        self.compliant = False
                    else:
                        self.detailedresults += "\nOne or more configuration options are missing or incorrect in the sudoers file"
                        self.compliant = False

        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):
        """
        Fix method that writes specified or default sudo group to sudoers file
        if not present from the report method

        :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

        try:

            if not self.primary_ci.getcurrvalue():
                self.formatDetailedResults("fix", self.rulesuccess,
                                           self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                return self.rulesuccess

            if not os.path.isfile(self.sudoers_backup):
                shutil.copy2(self.sudoers_file, self.sudoers_backup)

            if not self.sudoers_editor.fix():
                self.detailedresults += "\nFailed to update sudoers configuration"
                self.rulesuccess = False
            elif not self.sudoers_editor.commit():
                self.detailedresults += "\nFailed to update sudoers configuration"
                self.rulesuccess = False
            else:
                self.detailedresults += "\nSuccessfully updated sudoers configuration"

        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 undo(self):
        """
        Revert all fix actions taken by this rule
        """

        try:

            if os.path.isfile(self.sudoers_backup):
                os.rename(self.sudoers_backup, self.sudoers_file)
                resetsecon(self.sudoers_file)
                os.remove(self.sudoers_backup)
                self.detailedresults += "\nOriginal files/settings restored."
            else:
                self.detailedresults += "\nNo files/settings to restore."

        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.detailedresults += traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
示例#28
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)
示例#29
0
class DisableInteractiveStartup(Rule):
    def __init__(self, config, environ, logger, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 119
        self.rulename = 'DisableInteractiveStartup'
        self.mandatory = True
        self.formatDetailedResults("initialize")
        self.guidance = ['CCE 4245-7']
        self.applicable = {'type': 'white', 'family': ['linux']}

        # configuration item instantiation
        datatype = 'bool'
        key = 'DISABLEINTERACTIVESTARTUP'
        instructions = "To prevent the disabling of interactive startup, " + \
            "set the value of DISABLEINTERACTIVESTARTUP to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.iditerator = 0
        self.ch = CommandHelper(self.logger)
        self.restart = ""
        self.created = False
        self.sethelptext()

    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.detailedresults and self.currstate properties are
        updated to reflect the system status. self.rulesuccess will be updated
        if the rule does not succeed.
        Perform a check to see if PROMPT has been set to 'no' or not


        :returns: bool
        @author bemalmbe
        @change: dwalker

        '''
        try:
            self.detailedresults = ""
            compliant = True
            self.perms = [0, 0, 420]
            self.helper = Pkghelper(self.logger, self.environ)
            if self.helper.manager == "portage":
                self.filepath = "/etc/conf.d/rc"
                keyval = {"RC_INTERACTIVE": "no"}
            elif self.helper.manager == "zypper":
                self.filepath = "/etc/sysconfig/boot"
                keyval = {"PROMPT_FOR_CONFIRM": "no"}
            elif self.helper.manager == "apt-get":
                self.filepath = "/etc/default/grub"
                keyval = {"GRUB_DISABLE_RECOVERY": '"true"'}
                self.restart = "/usr/sbin/update-grub"
            elif self.helper.manager == "yum" or self.helper.manager == "dnf":
                self.filepath = "/etc/sysconfig/init"
                keyval = {"PROMPT": "no"}
            tmpPath = self.filepath + ".tmp"
            if not os.path.exists(self.filepath):
                if createFile(self.filepath, self.logger):
                    self.created = True
            if not checkPerms(self.filepath, self.perms, self.logger):
                compliant = False
                self.detailedresults += "Permissions are not correct on " + \
                    self.filepath + "\n"
            self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                         "conf", self.filepath, tmpPath,
                                         keyval, "present", "closedeq")
            if os.path.exists(self.filepath):
                if not self.editor.report():
                    self.detailedresults += "Configuration for " + \
                        self.filepath + " is incorrect via kveditor report\n"
                    compliant = False
            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):
        '''The fix method will apply the required settings to the system.
        self.rulesuccess will be updated if the rule does not succeed.
        Search for the /etc/sysconfig/init configuration file and set the
        PROMPT setting to PROMPT=no
        
        @author bemalmbe
        @change: dwalker 4/8/2014 implementing KVEditorStonix


        '''
        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 os.path.exists(self.filepath):
                if not checkPerms(self.filepath, self.perms, self.logger):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.filepath, self.perms, self.logger,
                                    self.statechglogger, myid):
                        self.rulesuccess = False
            if self.editor.fixables:
                if not self.created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                if self.editor.fix():
                    self.detailedresults += "kveditor fix ran successfully\n"
                    if self.editor.commit():
                        self.detailedresults += "kveditor commit ran " + \
                            "successfully\n"
                    else:
                        self.detailedresults += "kveditor commit did not " + \
                            "run successfully\n"
                        self.rulesuccess = False
                else:
                    self.detailedresults += "kveditor fix did not run " + \
                        "successfully\n"
                    self.rulesuccess = False
                os.chown(self.filepath, self.perms[0], self.perms[1])
                os.chmod(self.filepath, self.perms[2])
                resetsecon(self.filepath)
            if self.restart:
                self.ch.executeCommand(self.restart)
                if self.ch.getReturnCode() != 0:
                    self.detailedresults += "Unable to restart Grub with " + \
                        "new changes\n"
                    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
示例#30
0
    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