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


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

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


        '''

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

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

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

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

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

    def fix(self):
        '''SSHTimeout.fix(): set the correct values in /etc/ssh/sshd_config
        so that ssh sessions time out appropriately.
        @author: D.Walker


        '''

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

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

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

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

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

                self.rulesuccess = success
            if debug:
                self.logger.log(LogPriority.DEBUG, debug)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Example #2
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
class DisableWeakAuthentication(Rule):
    '''This rule will remove rsh(server and client) if installed, remove
    pam_rhosts entry from any pam file,


    '''
    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 30
        self.rulename = "DisableWeakAuthentication"
        self.mandatory = True
        self.formatDetailedResults("initialize")
        self.guidance = ["NSA 3.2.3.1"]
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd']
        }

        # Configuration item instantiation
        datatype = 'bool'
        key = 'DISABLEWEAKAUTHENTICATION'
        instructions = "To prevent the disabling of services using weak " + \
                       "authentication set DISABLEWEAKAUTHENTICATION to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

        self.rsh = ["rshell", "rsh-client", "rsh-server", "rsh", "SUNWrcmdc"]
        self.pams = [
            "/etc/pam.conf", "/etc/pam_ldap.conf", "/etc/pam.conf-winbind"
        ]
        self.incorrects = []
        self.iditerator = 0
        self.sethelptext()

    def report(self):
        '''DisableWeakAuthentication.report() Public method to report on the
        presence of certain r-command packages and contents in pam files.
        @author: dwalker


        :returns: bool - False if the method died during execution

        '''
        self.detailedresults = ""
        try:
            self.helper = Pkghelper(self.logger, self.environ)
            compliant = True
            for item in self.rsh:
                if self.helper.check(item):
                    compliant = False
                    self.detailedresults += item + " is still installed\n"
                    break
            for item in self.pams:
                found = False
                if os.path.exists(item):
                    contents = readFile(item, self.logger)
                    if contents:
                        for line in contents:
                            if re.match('^#', line) or \
                               re.match(r'^\s*$', line):
                                continue
                            elif re.search("pam_rhosts", line):
                                found = True
                                compliant = False
                                self.detailedresults += "pam_rhosts line " + \
                                    "found in " + item + "\n"
                                break
                        if found:
                            self.incorrects.append(item)
                        if not checkPerms(item, [0, 0, 420], self.logger):
                            compliant = False
                            self.detailedresults += "Permissions for " + \
                                item + " are incorrect\n"
            if os.path.exists("/etc/pam.d/"):
                fileItems = glob.glob("/etc/pam.d/*")
                for item in fileItems:
                    found = False
                    if os.path.islink(item) or os.path.isdir(item):
                        continue
                    contents = readFile(item, self.logger)
                    if not contents:
                        continue
                    for line in contents:
                        if re.match('^#', line) or re.match(r'^\s*$', line):
                            continue
                        elif re.search("pam_rhosts", line):
                            found = True
                            compliant = False
                            self.detailedresults += "pam_rhosts line " + \
                                "found in " + item + "\n"
                            break
                    if found:
                        self.incorrects.append(item)
                for item in fileItems:
                    if os.path.islink(item) or os.path.isdir(item):
                        continue
                    if not checkPerms(item, [0, 0, 420], self.logger):
                        compliant = False
                        self.detailedresults += "Permissions for " + \
                            item + " are incorrect\n"
                        break
            if self.incorrects:
                debug = "The following files need to be corrected: " + \
                    str(self.incorrects) + "\n\n"
                self.logger.log(LogPriority.DEBUG, debug)
            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):
        '''DisableWeakAuthentication.fix() Public method to fix any issues
        that were found in the report method.
        @author: dwalker


        :returns: bool - False if the method died during execution

        '''
        try:
            self.detailedresults = ""
            if not self.ci.getcurrvalue():
                return
            success = True
            for item in self.rsh:
                if self.helper.check(item):
                    if not self.helper.remove(item):
                        success = False
            if self.incorrects:
                for item in self.incorrects:
                    tempstring = ""
                    contents = readFile(item, self.logger)
                    if not contents:
                        continue
                    for line in contents:
                        if re.match('^#', line) or re.match(r'^\s*$', line):
                            tempstring += line
                        elif re.search("pam_rhosts", line):
                            continue
                        else:
                            tempstring += line
                    if not checkPerms(item, [0, 0, 420], self.logger):
                        if not setPerms(item, [0, 0, 420], self.logger):
                            success = False
                    tmpfile = item + ".tmp"
                    if writeFile(tmpfile, tempstring, self.logger):
                        os.rename(tmpfile, item)
                        os.chown(item, 0, 0)
                        os.chmod(item, 420)
                        resetsecon(item)
                    else:
                        success = False
            for item in self.pams:
                if os.path.exists(item):
                    if not checkPerms(item, [0, 0, 420], self.logger):
                        if not setPerms(item, [0, 0, 420], self.logger):
                            success = False
            if os.path.exists("/etc/pam.d/"):
                fileItems = glob.glob("/etc/pam.d/*")
                for item in fileItems:
                    if not checkPerms(item, [0, 0, 420], self.logger):
                        if not setPerms(item, [0, 0, 420], self.logger):
                            success = False
            return 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

    def undo(self):
        '''There is no undo method for this rule since we don't ever want rsh
        installed or for the r services to be enabled.  Overrides the undo
        inside the rule.py class


        '''
        try:
            info = "no undo available"
            self.logger.log(LogPriority.INFO, info)
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            return False
Example #4
0
class SecureSquidProxy(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 143
        self.rulename = "SecureSquidProxy"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        datatype1 = "bool"
        key1 = "SECURESQUIDPROXY"
        instructions1 = "To disable this rule set the value of " + \
            "SECURESQUIDPROXY to False.  MinimizeServices rule disables " + \
            "squid by default however this rule will still configure it " + \
            "if installed"
        default1 = True
        self.ci = self.initCi(datatype1, key1, instructions1, default1)

        self.guidance = ["NSA 3.19", "CCE 4454-5", "CCE 4353-9", "CCE 4503-9",
                         "CCE 3585-7", "CCE 4419-8", "CCE 3692-1",
                         "CCE 4459-4", "CCE 4476-8", "CCE 4181-4",
                         "CCE 4577-3", "CCE 4344-8", "CCE 4494-1",
                         "CCE 4511-2", "CCE 4529-4", "CCE 3610-3",
                         "CCE 4466-9", "CCE 4607-8", "CCE 4255-6",
                         "CCE 4127-7", "CCE 4519-5", "CCE 4413-1",
                         "CCE 4373-7"]

        self.applicable = {"type": "white",
                           "family": ["linux"]}
        self.iditerator = 0

    def report(self):
        ''' '''
        try:
            self.detailedresults = ""
            compliant = True
            debug = ""
            self.ph = Pkghelper(self.logger, self.environ)
            self.installed = False
            if self.ph.manager == "apt-get":
                if self.ph.check("squid3"):
                    self.installed = True
                    self.squidfile = "/etc/squid3/squid.conf"
                elif self.ph.check("squid"):
                    self.installed = True
                    self.squidfile = "/etc/squid/squid.conf"
            if self.ph.check("squid"):
                self.installed = True
                self.squidfile = "/etc/squid/squid.conf"
            if self.installed:
                self.data1 = {"ftp_passive": "on",
                              "ftp_sanitycheck": "on",
                              "check_hostnames": "on",
                              "request_header_max_size": "20 KB",
                              "reply_header_max_size": "20 KB",
                              "cache_effective_user": "******",
                              "cache_effective_group": "squid",
                              "ignore_unknown_nameservers": "on",
                              "allow_underscore": "off",
                              "httpd_suppress_version_string": "on",
                              "forwarded_for": "off",
                              "log_mime_hdrs": "on"}
                self.data2 = {"http_access": "deny to_localhost"}
                #make sure these aren't in the file
                self.denied = ["acl Safe_ports port 70",
                               "acl Safe_ports port 210",
                               "acl Safe_ports port 280",
                               "acl Safe_ports port 488",
                               "acl Safe_ports port 591",
                               "acl Safe_ports port 777"]
                if os.path.exists(self.squidfile):
                    if not checkPerms(self.squidfile, [0, 0, 420], self.logger):
                        self.detailedresults += "Permissions are not correct " + \
                            "on " + self.squidfile + "\n"
                    contents = readFile(self.squidfile, self.logger)
                    if contents:
                        found = False
                        for line in contents:
                            if re.search("^http_access", line.strip()):
                                temp = line.strip()
                                temp = re.sub("\s+", " ", temp)
                                temp = re.sub("http_access\s+", "", temp)
                                if re.search("^deny to_localhost", temp):
                                    found = True
                                    break
                        if not found:
                            compliant = False
                        for key in self.data1:
                            found = False
                            for line in contents:
                                if re.match('^#', line) or re.match(r'^\s*$', line):
                                    continue
                                elif re.search("^" + key + " ", line):
                                    temp = line.strip()
                                    temp = re.sub("\s+", " ", temp)
                                    temp = temp.split(" ")
                                    if len(temp) >= 3:
                                        joinlist = [temp[1], temp[2]]
                                        joinstring = " ".join(joinlist)
                                        if self.data1[key] == joinstring:
                                            found = True
                                        else:
                                            found = False
                                            break
                                    else:
                                        if self.data1[key] == temp[1]:
                                            found = True
                                        else:
                                            found = False
                                            break
                            if not found:
                                debug += key + " either not found or has wrong value\n"
                                self.detailedresults += key + " either not found " + \
                                    "or has wrong value\n"
                                compliant = False
                        if debug:
                            self.logger.log(LogPriority.DEBUG, debug)
                        debug = ""
                        for entry in self.denied:
                            for line in contents:
                                if re.search(entry + "\s+", line.strip()):
                                    debug += "line: " + line + \
                                        "should not exist in this file\n"
                                    self.detailedresults += "line: " + line + \
                                        "should not exist in this file\n"
                                    compliant = False
                        if debug:
                            self.logger.log(LogPriority.DEBUG, debug)
                    else:
                        compliant = False
                        self.detailedresults += "Contents of squid " + \
                            "configuration file are blank\n"
                else:
                    compliant = False
                    self.detailedresults += "squid configuration file " + \
                        "doesn't 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():
                return
            self.detailedresults = ""
            success = True
            created = False
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            if self.installed:
                if not os.path.exists(self.squidfile):
                    if not createFile(self.squidfile, self.logger):
                        success = False
                    else:
                        created = True
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "creation",
                                 "filepath": self.squidfile}
                        self.statechglogger.recordchgevent(myid, event)
                if not checkPerms(self.squidfile, [0, 0, 420], self.logger):
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(self.squidfile, [0, 0, 420], self.logger,
                                        self.statechglogger, myid):
                            success = False
                    else:
                        if not setPerms(self.squidfile, [0, 0, 420], self.logger):
                            success = False
                tempstring = ""
                contents = readFile(self.squidfile, self.logger)
                newcontents = []
                if contents:
                    '''Remove any undesired acl lines'''
                    for line in contents:
                        if re.match('^#', line) or re.match(r'^\s*$', line):
                            newcontents.append(line)
                        elif re.search("^acl Safe_ports port ", line.strip()):
                            m = re.search("acl Safe_ports port ([0-9]+).*", line)
                            if m.group(1):
                                item = "acl Safe_ports port " + m.group(1)
                                if item in self.denied:
                                    continue
                                else:
                                    newcontents.append(line)
                        else:
                            newcontents.append(line)
                    '''removeables list holds key vals we find in the file
                    that we can remove from self.data'''
                    removeables = []
                    '''deleteables list holds key vals we can delete from 
                    newcontents list if it's incorrect.'''
                    deleteables = {}
                    for key in self.data1:
                        found = False
                        for line in reversed(newcontents):
                            if re.match('^#', line) or re.match(r'^\s*$', line):
                                continue
                            elif re.search("^" + key + " ", line) or re.search("^" + key, line):
                                temp = line.strip()
                                temp = re.sub("\s+", " ", temp)
                                temp = temp.split(" ")
                                if len(temp) >= 3:
                                    joinlist = [temp[1], temp[2]]
                                    joinstring = " ".join(joinlist)
                                    if self.data1[key] == joinstring:
                                        '''We already found this line and value
                                        No need for duplicates'''
                                        if found:
                                            newcontents.remove(line)
                                            continue
                                        removeables.append(key)
                                        found = True
                                    else:
                                        try:
                                            deleteables[line] = ""
                                        except Exception:
                                            continue
                                        continue
                                elif len(temp) == 2:
                                    if self.data1[key] == temp[1]:
                                        '''We already found this line and value
                                        No need for duplicates'''
                                        if found:
                                            newcontents.remove(line)
                                            continue
                                        removeables.append(key)
                                        found = True
                                    else:
                                        try:
                                            deleteables[line] = ""
                                        except Exception:
                                            continue
                                        continue
                                elif len(temp) == 1:
                                    try:
                                        deleteables[line] = ""
                                    except Exception:
                                        continue
                                    continue
                    if deleteables:
                        for item in deleteables:
                            newcontents.remove(item)
                    '''anything in removeables we found in the file so
                    we will remove from the self.data1 dictionary'''
                    if removeables:
                        for item in removeables:
                            del(self.data1[item])
                    '''now check if there is anything left over in self.data1
                    if there is we need to add that to newcontents list'''
                    if self.data1:
                        for item in self.data1:
                            line = item + " " + self.data1[item] + "\n"
                            newcontents.append(line)
                    for line in newcontents:
                        found = False
                        if re.search("^http_access", line.strip()):
                            temp = line.strip()
                            temp = re.sub("\s+", " ", temp)
                            temp = re.sub("http_access\s+", "", temp)
                            if re.search("^deny to_localhost", temp):
                                found = True
                                break
                    if not found:
                        newcontents.append("http_access deny to_localhost\n")
                    for item in newcontents:
                        tempstring += item
                    tmpfile = self.squidfile + ".tmp"
                    if not writeFile(tmpfile, tempstring, self.logger):
                        success = False
                    else:
                        if not created:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "conf",
                                     "filepath": self.squidfile}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(self.squidfile,
                                                                 tmpfile, myid)
                            os.rename(tmpfile, self.squidfile)
                            os.chown(self.squidfile, 0, 0)
                            os.chmod(self.squidfile, 420)
                            resetsecon(self.squidfile)
                else:
                    tempstring = ""
                    for item in self.data1:
                        tempstring += item + " " + self.data1[item] + "\n"
                    for item in self.data2:
                        tempstring += item + " " + self.data2[item] + "\n"
                    tmpfile = self.squidfile + ".tmp"
                    if not writeFile(tmpfile, tempstring, self.logger):
                        success = False
                    else:
                        if not created:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "conf",
                                     "filepath": self.squidfile}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(self.squidfile,
                                                                 tmpfile, myid)
                        os.rename(tmpfile, self.squidfile)
                        os.chown(self.squidfile, 0, 0)
                        os.chmod(self.squidfile, 420)
                        resetsecon(self.squidfile)
                self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults = self.detailedresults + "\n" + str(err) + \
                " - " + str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess 
Example #5
0
class InstallVLock(Rule):
    '''This class installs the vlock package to enable screen locking
    vlock is the package name on opensuse 15+, debian, ubuntu
    kbd is the package name on opensuse 42.3-, rhel, fedora, centos (contains vlock package)
    
    references:
    https://pkgs.org/download/vlock
    https://access.redhat.com/discussions/3543671


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

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

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

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

        majorver = self.environ.getosmajorver()

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

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


        :returns: self.compliant

        :rtype: bool
@author: Derek T Walker

        '''

        try:

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

            self.set_pkg()

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

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

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


        :returns: self.rulesuccess

        :rtype: bool
@author: Derek T Walker

        '''

        try:

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

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

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

            undocmd = self.ph.getRemove()

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

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

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

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

            self.euid = self.environ.geteuid()

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

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

        self.detailedresults = ""
        self.compliant = True

        try:

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

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

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

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

            self.compliant = compliant

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

        return self.compliant

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

        :return: installed
        :rtype: bool
        """

        self.screen_pkg = ""
        installed = True

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

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

        return installed

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

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

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

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

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

        """

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

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

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

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

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

        return compliant

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

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

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

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

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

        """

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

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

        return self.rulesuccess

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

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

        """

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

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

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

            elif not self.correctFile(kdefile, username):

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

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

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

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

            cmd = ""

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

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

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

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

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

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

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

        :param filehandle: string
        :returns: bool

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

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

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

        """
        success = True

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

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

        return success

    def undo(self):
        self.detailedresults += "This rule cannot be reverted\n"
        self.rulesuccess = False
        self.formatDetailedResults("undo", self.rulesuccess,
                                   self.detailedresults)
Example #8
0
class InstallBanners(RuleKVEditor):
    '''Install and configure warning banners, to be displayed at startup.'''
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """

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

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

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

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

        try:

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

        except Exception:
            raise

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


        '''

        self.gnome2 = True
        self.gnome2bannertext = GDMWARNINGBANNER

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

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

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

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

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

        try:

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

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

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

            self.ch.executeCommand(cmd2)

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

        except Exception:
            raise
        return retval

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


        '''

        self.gnome3 = True
        self.gnome3bannertext = GDM3WARNINGBANNER

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


        '''

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

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

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

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


        '''

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

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


        '''

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

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


        '''

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

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

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

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


        '''

        self.mac = True
        self.bannertext = WARNINGBANNER

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

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

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


        '''

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

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


        '''

        try:

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

                self.setlinuxcommon()

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

        except Exception:
            raise

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

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

        '''

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

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

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

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

        '''

        retval = True
        tmpPath = filepath + ".stonixtmp"

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

        try:

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

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

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

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

        except Exception:
            retval = False
            raise
        return retval

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

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

        '''

        retval = True
        appends = []

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

        try:

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

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

        except Exception:
            self.rulesuccess = False
            raise
        return retval

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

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

        '''

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

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

        self.setFileContents(filepath, contents)

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

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

        '''

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

        try:

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

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

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

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

@author: Breen Malmberg

        '''

        retval = True

        try:

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

        except Exception:
            raise
        return retval

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


        :returns: self.compliant

        :rtype: boolean

@author Breen Malmberg

        '''

        self.compliant = True
        self.detailedresults = ""

        try:

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

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

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


        :returns: g3ver

        :rtype: float
@author: Breen Malmberg

        '''

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

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

        compliant = True

        try:

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

            if not self.reportcommon():
                compliant = False

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean

@author: Breen Malmberg

        '''

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

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

        except Exception:
            raise
        return compliant

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

        compliant = True

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

        return compliant

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

        compliant = True

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

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

        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        compliant = True

        try:

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

        except Exception:
            raise
        return compliant

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

        try:

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

        except Exception:
            raise
        return retval

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


        :returns: retval

        :rtype: boolean
@author: Breen Malmberg

        '''

        retval = True

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

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

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


        :returns: success

        :rtype: boolean

@author Breen Malmberg

        '''

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

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

        try:

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

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean

@author: Breen Malmberg

        '''

        success = True

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

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

        try:

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

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:

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

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

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

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

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

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

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

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

        except Exception:
            raise
        return success

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True
        contentlines = []

        try:

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

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


        :returns: success

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

        '''

        success = True

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

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

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

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


        :returns: success

        :rtype: boolean
@author: Breen Malmberg

        '''

        success = True

        try:
            if not RuleKVEditor.fix(self, True):
                success = False
            if not self.setFileContents(self.ftpwelcomefile, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.ftpwelcomefile)
            if not self.setFileContents(self.policybanner, self.bannertext):
                success = False
                self.detailedresults += '\nUnable to set warning banner text in ' + str(
                    self.policybanner)
        except Exception:
            raise
        return success
Example #9
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
Example #10
0
class ConfigureAIDE(Rule):
    """Install and configure Advanced Intrusion Detection Environment (AIDE).
This rule is optional and will install and configure AIDE when it is run."""
    def __init__(self, config, environ, logger, statechglogger):
        """
        private method to initialize the module

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 110
        self.rulename = 'ConfigureAIDE'
        self.formatDetailedResults("initialize")
        self.sethelptext()
        self.guidance = ['NSA(2.1.3)', 'cce-4209-3']

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

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

    def report(self):
        """Check if AIDE is installed and properly configured.
        If the config is correct then the self.compliant, self.detailed results
        and self.currstate properties are updated to reflect the system status.
        self.rulesuccess will be updated if the rule does not succeed.

        :return: self.compliant
        :rtype: bool

        """

        try:

            self.compliant = True
            self.detailedresults = ""

            self.init_objs()

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

            if not self.report_aide_installed():
                self.compliant = False
            else:
                if not self.report_aide_conf():
                    self.compliant = False
                if not self.report_aide_cronjob():
                    self.compliant = False

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

        return self.compliant

    def init_objs(self):
        """

        """

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

    def set_aide_paths(self):
        """

        """

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

    def set_aide_cron_job(self):
        """

        """

        aidetime = self.aidetime.getcurrvalue()

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

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

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

    def report_aide_installed(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

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

        return compliant

    def report_aide_conf(self):
        """

        :return: compliant
        :rtype: bool
        """

        compliant = True

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

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

        return compliant

    def report_aide_cronjob(self):
        """

        :return: compliant
        :rtype: bool

        """

        compliant = True

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

        return compliant

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

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

        """

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

        try:

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

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

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

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

        return self.rulesuccess

    def fix_aide_conf(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

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

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

        return success

    def fix_aide_cronjob(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

        try:

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

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

        return success

    def fix_aide_installed(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

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

        return success

    def fix_aide_init(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

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

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

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

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

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

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

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

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

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

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

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

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

        self.localize()

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


        :returns: void
        @author: Breen Malmberg

        '''

        self.linux = False
        self.darwin = False

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

        self.initObjs()
        self.setVars()

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


        :returns: void
        @author: Breen Malmberg

        '''

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

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


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

        '''

        try:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        except Exception:
            raise

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


        :returns: sane

        :rtype: bool
@author: Breen Malmberg

        '''

        sane = True

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

        try:

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

        except Exception:
            raise
        return sane

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


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

        '''

        try:

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

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

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

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

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

        except Exception:
            raise

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


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

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

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

        try:

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

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

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

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

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

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

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

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

        except Exception:
            raise
        return retval

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

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

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

        try:

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

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

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

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

        except Exception:
            raise
        return retval

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


        :returns: retval

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

        '''

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

        retval = True

        try:

            if self.SecureCUPS.getcurrvalue():

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

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

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

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

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

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

        except Exception:
            raise
        return retval

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


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

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

        try:

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

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

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

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

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

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

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


        :returns: retval

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

        '''

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

        try:

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

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

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

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

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

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

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

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

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

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

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

            if not self.reloadCUPS():
                retval = False

        except Exception:
            raise
        return retval

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

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

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

        except Exception:
            raise
        return retval

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


        :returns: retval

        :rtype: bool
@author: Breen Malmberg

        '''

        retval = True

        try:

            if self.linux:

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

            elif self.darwin:

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

        except Exception:
            raise
        return retval
Example #12
0
class DisableCloudServices(RuleKVEditor):
    '''This method runs all the report methods for RuleKVEditors defined in the
    dictionary
    
    @author: ekkehard j. koch
    @change: 07/23/2014 added ubuntu methods and applicability; fixed typos in
            doc strings; added class doc string; implemented pkghelper and
            iterate methods - bemalmbe


    '''

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

    def __init__(self, config, environ, logdispatcher, statechglogger):
        RuleKVEditor.__init__(self, config, environ, logdispatcher,
                              statechglogger)

        self.rulenumber = 159
        self.rulename = 'DisableCloudServices'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.logger = self.logdispatch
        self.guidance = []
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10'],
                                  'Ubuntu': ['12.04', '+']}}
        self.ch = CommandHelper(self.logdispatch)

        # init CIs
        datatype = 'bool'
        key = 'DISABLECLOUDSERVICES'
        instructions = "To prevent cloud services from being disabled, " + \
            "set the value of DisableCloudServices to False."
        default = True
        self.DisableCloudServices = self.initCi(datatype, key, instructions,
                                                default)
        if self.environ.getosfamily() == 'darwin':
            self.addKVEditor("iCloudSaveNewDocumentsToDisk",
                             "defaults",
                             "NSGlobalDomain",
                             "",
                             {"NSDocumentSaveNewDocumentsToCloud":
                              ["0", "-bool no"]},
                             "present",
                             "",
                             "Save new documents to disk not to iCloud.",
                             None,
                             False,
                             {"NSDocumentSaveNewDocumentsToCloud":
                              ["1", "-bool yes"]})
        else:
            self.debianpkglist = ['unity-webapps-common',
                                  'unity-lens-shopping']
        self.sethelptext()
    def report(self):
        '''choose which report method to run based on OS archetype'''
        self.detailedresults = ""
        if self.environ.getosfamily() == 'darwin':
            RuleKVEditor.report(self, False)
        elif re.search('Ubuntu', self.environ.getostype()):
            retval = self.reportUbuntu()
            return retval
###############################################################################

    def reportUbuntu(self):
        '''if debian, check for unity-lens-shopping and unity-webapps-common
        cloud service packages


        '''

        # defaults
        self.compliant = True
        self.detailedresults = ""

        try:

            self.ph = Pkghelper(self.logdispatch, self.environ)

            for package in self.debianpkglist:
                if self.ph.check(package):
                    self.compliant = False

        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):
        '''choose which fix method to run, based on OS archetype'''
        self.detailedresults = ""
        if self.DisableCloudServices.getcurrvalue():
            if self.environ.getosfamily() == 'darwin':
                RuleKVEditor.fix(self, False)
            elif re.search('Ubuntu', self.environ.getostype()):
                self.fixUbuntu()
###############################################################################

    def fixUbuntu(self):
        '''if debian, disable unity-lens-shopping and unity-webapps-common
        cloud service packages, if they are installed


        '''

        # defaults
        self.iditerator = 0

        try:

            for package in self.debianpkglist:
                if self.ph.check(package):
                    self.ph.remove(package)

                    cmd = self.ph.getInstall() + package

                    event = {'eventtype': 'commandstring',
                             'command': cmd}
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)

                    self.statechglogger.recordchgevent(myid, event)

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            self.detailedresults += "\n" + str(err) + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Example #13
0
class ForceIdleLogout(Rule):
    '''The ForceIdleLogout class attempts to configure the system to log out users
    after long periods of inactivity. This control reinforces the protection
    offered by the screen lock by terminating long idle sessions. Note that
    the guidance for this control, AC-2(5) from 800-53, seems to be written
    to the capabilities of Microsoft's active directory product which has the
    ability to establish work schedules in the directory service and limit
    logons to those time windows. *NIX systems typically do not have that type
    of tooling so we rely on a long idle time.


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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 23
        self.rulename = 'ForceIdleLogout'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.helptext = '''The ForceIdleLogout rule will configure \
the system to log out GUI sessions that have been idle for a long time. This \
helps prevent take over and illicit use of idle sessions and frees system \
resources. Because some environments may rely on the capability of interactive \
sessions to execute long running jobs this control is optional and will need \
to be enabled below for environments that require it. The idle time before \
logout may also be customized. N.B. Please note that most Linux window \
managers will not save work in progress when the logout occurs.
'''
        self.rootrequired = True
        self.applicable = {
            'type': 'white',
            'family': ['linux'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'fisma': 'high'
        }
        self.cmdhelper = CommandHelper(self.logger)
        self.guidance = ['NIST 800-53 AC-2(5)']
        myos = self.environ.getostype().lower()
        self.gconf = ""
        if re.search("red hat", myos) or re.search("centos", myos):
            self.gconf = "GConf2"
        else:
            self.gconf = "gconf2"

        datatype = 'bool'
        key = 'FORCEIDLELOGOUT'
        instructions = '''To disable this rule set the value of \
FORCEIDLELOGOUT to False.'''
        default = False
        self.filci = self.initCi(datatype, key, instructions, default)

        datatype2 = 'int'
        key2 = 'FORCEIDLELOGOUTTIMEOUT'
        instructions2 = '''To customize the timeout period set the \
FORCEIDLELOGOUTTIMEOUT to the desired duration in minutes.'''
        default2 = 240
        self.timeoutci = self.initCi(datatype2, key2, instructions2, default2)
        self.ph = Pkghelper(self.logger, self.environ)
        self.gnomesettingpath = "/etc/dconf/db/local.d/00-autologout"
        self.gnomelockpath = "/etc/dconf/db/local.d/locks/autologout"
        self.undotimeout = ""
        self.undoforcelogout = ""
        self.kdesddm = ""
        self.gnomeInstalled, self.kdeInstalled = False, False

    def report(self):
        '''Report on whether the Idle Logout settings are correct.


        :returns: bool
        @author: D.Kennel

        '''

        try:
            compliant = True
            self.detailedresults = ""
            try:
                self.seconds = self.timeoutci.getcurrvalue() * 60
            except (TypeError):
                self.detailedresults += "FORCEIDLELOGOUTTIMEOUT value is not " + \
                                        "valid!\n"
                return False
            if self.environ.osfamily == 'linux':
                self.kdesddm = self.ph.check("sddm")
                if self.ph.check("gdm") or self.ph.check("gdm3"):
                    self.gnomeInstalled = True
                    if not self.chkgnome():
                        self.detailedresults += "Gnome GUI environment " + \
                                                "does not appear to be correctly configured " + \
                                                "for automatic logout of idle sessions. This " + \
                                                "guidance is optional in STONIX, check local " + \
                                                "policy to see if it is required.\n"
                        compliant = False
                    else:
                        self.detailedresults += "Gnome GUI environment " + \
                                                "appears to be correctly configured for " + \
                                                "automatic logout of idle sessions.\n"
                else:
                    self.gnomeInstalled = False
                    self.detailedresults += "Gnome not installed.  No need to configure for gnome\n"

                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.chkkde():
                        self.detailedresults += "KDE GUI environment " + \
                                                "does not appear to be correctly configured " + \
                                                "for automatic logout of idle sessions. This " + \
                                                "guidance is optional in STONIX, check local " + \
                                                "policy to see if it is required.\n"
                        compliant = False
                    else:
                        self.detailedresults += "KDE GUI environment " + \
                                                "appears to be correctly configured for " + \
                                                "automatic logout of idle sessions.\n"
                else:
                    self.kdeInstalled = False
                    self.detailedresults += "KDE not installed.  No need to configure for kde\n"
            elif self.environ.getosfamily() == 'darwin':
                if not self.chkosx():
                    self.detailedresults += "Idle logout value is not set to " + \
                        str(self.timeoutci.getcurrvalue()) + "\n"
            self.compliant = compliant

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

    def chkgnome(self):
        '''Check that the GNOME 3 auto logout settings are set correctly


        :returns: boot - true if settings are set to logout
        @author: D. Kennel

        '''

        if not self.environ.geteuid() == 0:
            # Short circuit for user mode run
            # manipulating the GNOME settings requires privilege
            return True
        compliant = True
        self.timeoutwrong = False
        self.logoutwrong = False
        havedconffile = False
        havelockfile = False
        havetimeout = False
        havetimeoutlock = False
        havelogout = False
        havelogoutlock = False
        if os.path.exists("/usr/bin/gsettings"):
            if os.path.exists('/etc/dconf/db/local.d'):
                if os.path.exists(self.gnomesettingpath):
                    havedconffile = True
                    self.logdispatch.log(LogPriority.DEBUG, [
                        'ForceIdleLogout.__chkgnome3',
                        'Found Gnome settings file'
                    ])
                    contents = readFile(self.gnomesettingpath, self.logger)
                    for line in contents:
                        if re.search(
                                'sleep-inactive-ac-timeout=' +
                                str(self.seconds), line):
                            havetimeout = True
                            self.logdispatch.log(LogPriority.DEBUG, [
                                'ForceIdleLogout.__chkgnome3',
                                'Found Gnome timeout'
                            ])
                        if re.search("sleep-inactive-ac-type='logout'", line):
                            havelogout = True
                            self.logdispatch.log(LogPriority.DEBUG, [
                                'ForceIdleLogout.__chkgnome3',
                                'Found Gnome logout'
                            ])
                if os.path.exists(self.gnomelockpath):
                    havelockfile = True
                    self.logdispatch.log(LogPriority.DEBUG, [
                        'ForceIdleLogout.__chkgnome3', 'Found Gnome lock file'
                    ])
                    contents = readFile(self.gnomelockpath, self.logger)
                    for line in contents:
                        if re.search(
                                '/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-timeout',
                                line):
                            havetimeoutlock = True
                            self.logdispatch.log(LogPriority.DEBUG, [
                                'ForceIdleLogout.__chkgnome3',
                                'Found Gnome timeout lock'
                            ])
                        if re.search(
                                "/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-type",
                                line):
                            havelogoutlock = True
                            self.logdispatch.log(LogPriority.DEBUG, [
                                'ForceIdleLogout.__chkgnome3',
                                'Found Gnome logout lock'
                            ])
                if not havedconffile:
                    self.detailedresults += "GNOME 3 autologout settings " + \
                        "file not found at: " + \
                        "/etc/dconf/db/local.d/00-autologout\n"
                    compliant = False

                if not havetimeout:
                    self.detailedresults += "GNOME 3 autologout timeout " + \
                        "not found or does not match expected value. Set " + \
                        "sleep-inactive-ac-timeout=" + str(self.seconds) + \
                        " in /etc/dconf/db/local.d/00-autologout\n"
                    compliant = False

                if not havelogout:
                    self.detailedresults += "GNOME 3 autologout logout " + \
                        "not found. Set sleep-inactive-ac-type='logout' in " + \
                        "/etc/dconf/db/local.d/00-autologout\n"
                    compliant = False

                if not havelockfile:
                    self.detailedresults += "GNOME 3 autologout lock " + \
                        "file not found at: " + \
                        "/etc/dconf/db/local.d/locks/autologout\n"
                    compliant = False

                if not havetimeoutlock:
                    self.detailedresults += "GNOME 3 autologout timeout " + \
                        "lock not found. Set /org/gnome/settings-daemon/" + \
                        "plugins/power/sleep-inactive-ac-timeout in " + \
                        "/etc/dconf/db/local.d/locks/autologout\n"
                    compliant = False

                if not havelogoutlock:
                    self.detailedresults += "GNOME 3 autologout lock not " + \
                        "found. Set /org/gnome/settings-daemon/plugins/" + \
                        "power/sleep-inactive-ac-type in " + \
                        "/etc/dconf/db/local.d/locks/autologout\n"
                    compliant = False

        if self.ph.check(self.gconf):
            self.logdispatch.log(
                LogPriority.DEBUG,
                ['ForceIdleLogout.__chkgnome3', 'Checking GNOME with gconf'])
            prefix = '/usr/bin/gconftool-2 --direct --config-source ' + \
                'xml:readwrite:/etc/gconf/gconf.xml.mandatory --get '
            idletimecmd = prefix + '/desktop/gnome/session/max_idle_time'
            idleactcmd = prefix + '/desktop/gnome/session/max_idle_action'
            self.cmdhelper.executeCommand(idletimecmd)
            output = self.cmdhelper.getOutput()
            self.logdispatch.log(LogPriority.DEBUG, [
                'ForceIdleLogout.__chkgnome3',
                'Value of idle time ' + str(output)
            ])
            if output:
                try:
                    if int(output[0].strip()) != self.seconds:
                        compliant = False
                        self.timeoutwrong = True
                        if re.search("No value set", output[0]):
                            self.detailedresults += "GNOME autologout time not found\n"
                            self.undotimeout = "unset"
                        elif isinstance(output[0], int):
                            self.detailedresults += "GNOME autologout time not correct.\n"
                            self.undotimeout = output[0]
                        else:
                            self.detailedresults += "GNOME autologout time not correct\n"
                            self.undotimeout = "''"
                except (ValueError):
                    # value not found message
                    pass
            self.cmdhelper.executeCommand(idleactcmd)
            output2 = self.cmdhelper.getOutput()
            self.logdispatch.log(LogPriority.DEBUG, [
                'ForceIdleLogout.__chkgnome3',
                'Value of idle action ' + str(output2)
            ])
            if output2:
                if not re.search('forced-logout', output2[0]):
                    compliant = False
                    self.logoutwrong = True
                    if re.search("No value set", output2[0]):
                        self.detailedresults += "GNOME 3 autologout settings " + \
                                                "not found.\n"
                        self.undoforcelogout = "unset"
                    else:
                        self.detailedresults += "GNOME 3 autologout settings " + \
                                                "not correct.\n"
                        self.undoforcelogout = str(output2[0])
        return compliant

    def chkkde(self):
        '''Check that settings are correct for KDE 4 auto logout. Note that this
        setting lives in each user's home folder and is the reason this rule
        has root & non-root components. With root perms we can work on homes
        that are on local storage but user perms are required for NFS mounted
        home directories.


        :returns: bool - true if settings are set to logout for inspected users
        @author: D. Kennel

        '''
        debug = "Inside chkkde method"
        self.logger.log(LogPriority.DEBUG, debug)

        self.kdefix = {}
        if self.kdesddm:
            self.kdecheck = ".config/kdeglobals"
            self.rcpath = ".config/kscreenlockerrc"
            self.kdeprops = {"ScreenSaver": {"Timeout": str(self.seconds)}}
        else:
            self.kdecheck = ".kde"
            self.rcpath = ".kde/share/config/kscreensaverrc"
            self.kdeprops = {
                "ScreenSaver": {
                    "AutoLogout": "true",
                    "AutoLogoutTimeout": str(self.seconds)
                }
            }

        if self.environ.geteuid() == 0:
            contents = readFile("/etc/passwd", self.logger)
            for line in contents:
                username = ""
                homepath = ""
                temp = line.split(':')
                try:
                    username = temp[0]
                    homepath = temp[5]
                except (IndexError):
                    self.logdispatch.log(LogPriority.DEBUG, [
                        'ForceIdleLogout.__chkkde4',
                        'IndexError processing ' + str(username)
                    ])
                    continue
                kdeparent = os.path.join(homepath, self.kdecheck)
                kdefile = os.path.join(homepath, self.rcpath)
                if not os.path.exists(kdeparent):
                    # User does not use KDE
                    continue
                elif not os.path.exists(kdefile):
                    #User uses kde but doesn't have the necessary config file
                    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 " + \
                                        "auto logout:\n"
                for user in self.kdefix:
                    self.detailedresults += user + "\n"
                return False
            else:
                return True
        else:
            self.logdispatch.log(LogPriority.DEBUG, [
                'ForceIdleLogout.__chkkde4',
                'Non root user context starting check'
            ])
            kdeparent = os.path.join(self.environ.geteuidhome(), self.kdecheck)
            kdefile = os.path.join(kdeparent, self.rcpath)
            if not os.path.exists(kdeparent):
                # User does not use KDE
                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 does not 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 chkosx(self):
        ''' '''

        globalprefs = "/Library/Preferences/.GlobalPreferences.plist"
        globalprefstemp = globalprefs + ".stonixtmp"
        timeout = self.timeoutci.getcurrvalue() * 60
        data = {
            "com.apple.autologout.AutoLogOutDelay":
            [str(timeout), "-int " + str(timeout)]
        }
        self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                     "defaults", globalprefs, globalprefstemp,
                                     data, "present")
        return self.editor.report()

    def fix(self):
        '''Configure the system to enforce logout of idle GUI sessions
        
        @author: D. Kennel


        '''
        try:
            if not self.filci.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
            self.detailedresults = ""
            success = True
            if self.environ.geteuid() == 0:
                self.iditerator = 0
                eventlist = self.statechglogger.findrulechanges(
                    self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)
            if self.environ.getosfamily() == "linux":
                if self.gnomeInstalled:
                    if not self.fixgnome():
                        success = False
                if self.kdeInstalled:
                    if not self.fixkde():
                        success = False
            elif self.environ.getosfamily() == "darwin":
                success = self.fixosx()
            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 fixgnome(self):
        '''Configure GNOME 3 for automatic logout.
        @author: d.kennel


        '''

        if self.environ.geteuid() != 0:
            debug = "Configuring gnome requires root privileges"
            self.logger.log(LogPriority.DEBUG, debug)
            self.detailedresults += "Configuring gnome requires root " + \
                "privileges\n"
            return False
        success = True
        createddir = False
        debug = ""
        if os.path.exists("/usr/bin/gsettings"):
            if not os.path.exists('/etc/dconf/db/local.d'):
                if not os.makedirs('/etc/dconf/db/local.d/'):
                    success = False
                    debug = "Unable to create the /etc/dconf/db/local.d/ directory"
                    self.detailedresults += "Unable to create the /etc/dconf/db/local.d/ directory"
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    createddir = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "creation",
                        "filepath": "/etc/dconf/db/local.d"
                    }
                    self.statechglogger.recordchgevent(myid, event)
            if os.path.exists('/etc/dconf/db/local.d'):
                created1, created2 = False, False
                self.logdispatch.log(LogPriority.DEBUG, [
                    'ForceIdleLogout.__fixgnome3', 'Working GNOME with dconf'
                ])

                try:
                    seconds = self.timeoutci.getcurrvalue() * 60
                except (TypeError):
                    self.detailedresults += "FORCEIDLELOGOUTTIMEOUT value is " + \
                        "not valid!\n"
                    self.rulesuccess = False
                    return False
                if not os.path.exists(self.gnomesettingpath):
                    if not createFile(self.gnomesettingpath, self.logger):
                        success = False
                        self.detailedresults += "Unable to create " + self.gnomesettingpath + " file\n"
                    else:
                        created1 = True
                        if not createddir:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                "eventtype": "creation",
                                "filepath": self.gnomesettingpath
                            }
                            self.statechglogger.recordchgevent(myid, event)
                if os.path.exists(self.gnomesettingpath):
                    gdirectives = {
                        "org.gnome.settings-daemon.plugins.power": {
                            "sleep-inactive-ac-type": "'logout'",
                            'sleep-inactive-ac-timeout': str(seconds)
                        }
                    }
                    geditor = KVEditorStonix(self.statechglogger, self.logger,
                                             "tagconf", self.gnomesettingpath,
                                             self.gnomesettingpath + '.tmp',
                                             gdirectives, "present",
                                             "closedeq")
                    geditor.report()
                    if geditor.fixables:
                        if geditor.fix():
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            geditor.setEventID(myid)
                            if geditor.commit():
                                debug = self.gnomesettingpath + "'s contents have been " + \
                                    "corrected\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                os.chown(self.gnomesettingpath, 0, 0)
                                os.chmod(self.gnomesettingpath, 0o644)
                                resetsecon(self.gnomesettingpath)

                            else:
                                debug = "kveditor commit not successful\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                self.detailedresults += self.gnomesettingpath + \
                                    " properties could not be set\n"
                                succeess = False
                        else:
                            debug = "kveditor fix not successful\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            self.detailedresults += self.gnomesettingpath + \
                                                    " properties could not be set\n"
                            succeess = False
                    if not checkPerms(self.gnomesettingpath, [0, 0, 0o644],
                                      self.logger):
                        if not created1:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            if not setPerms(self.gnomesettingpath,
                                            [0, 0, 0o644], self.logger,
                                            self.statechglogger, myid):
                                self.detailedresults += "Unable to set permissions on " + \
                                    self.gnomesettingpath + " file\n"
                                success = False
                        else:
                            if not setPerms(self.gnomesettingpath,
                                            [0, 0, 0o644], self.logger):
                                self.detailedresults += "Unable to set permissions on " + \
                                                        self.gnomesettingpath + " file\n"
                                success = False
                havetimeoutlock = False
                havelogoutlock = False
                lockdata = []
                if not os.path.exists(self.gnomelockpath):
                    if not createFile(self.gnomelockpath, self.logger):
                        success = False
                        self.detailedresults += "Unable to create " + self.gnomelockpath + " file\n"
                    else:

                        created2 = True
                        if not createddir:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                "eventtype": "creation",
                                "filepath": self.gnomelockpath
                            }
                            self.statechglogger.recordchgevent(myid, event)
                if os.path.exists(self.gnomelockpath):
                    contents = readFile(self.gnomelockpath, self.logger)
                    for line in contents:
                        if re.search(
                                '/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-timeout',
                                line):
                            havetimeoutlock = True
                        if re.search(
                                "/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-type",
                                line):
                            havelogoutlock = True
                    if not havetimeoutlock:
                        lockdata.append(
                            '/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-timeout\n'
                        )
                    if not havelogoutlock:
                        lockdata.append(
                            "/org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-type\n"
                        )
                    tempfile = self.gnomelockpath + ".tmp"
                    if writeFile(tempfile, lockdata, self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "conf",
                            "filepath": self.gnomelockpath
                        }
                        self.statechglogger.recordchgevent(myid, event)
                        self.statechglogger.recordfilechange(
                            self.gnomelockpath, tempfile, myid)
                        os.rename(tempfile, self.gnomelockpath)
                        os.chown(self.gnomelockpath, 0, 0)
                        os.chmod(self.gnomelockpath, 0o644)
                        resetsecon(self.gnomelockpath)
                    else:
                        self.detailedresults += "Unable to write contents to " + \
                            self.gnomelockpath
                        success = False
                    if not checkPerms(self.gnomelockpath, [0, 0, 0o644],
                                      self.logger):
                        if not created2:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            if not setPerms(self.gnomelockpath, [0, 0, 0o644],
                                            self.logger, self.statechglogger,
                                            myid):
                                self.detailedresults += "Unable to set permissions on " + \
                                    self.gnomelockpath + " file\n"
                                success = False
                        else:
                            if not setPerms(self.gnomelockpath, [0, 0, 0o644],
                                            self.logger):
                                self.detailedresults += "Unable to set permissions on " + \
                                                        self.gnomelockpath + " file\n"
                                success = False

        if self.ph.check(self.gconf):
            self.logdispatch.log(
                LogPriority.DEBUG,
                ['ForceIdleLogout.__fixgnome3', 'Working GNOME with gconf'])
            undocmd = ""
            undoprefix = '/usr/bin/gconftool-2 --direct --config-source ' + \
                'xml:readwrite:/etc/gconf/gconf.xml.mandatory '
            setprefix = '/usr/bin/gconftool-2 --direct --config-source ' + \
                'xml:readwrite:/etc/gconf/gconf.xml.mandatory --set '
            settime = setprefix + \
                '--type integer /desktop/gnome/session/max_idle_time ' + \
                str(self.timeoutci.getcurrvalue())
            setlogout = setprefix + \
                '--type string /desktop/gnome/session/' + \
                'max_idle_action forced-logout'
            if self.timeoutwrong:
                self.cmdhelper.executeCommand(settime)
                retcode = self.cmdhelper.getReturnCode()
                if retcode != 0:
                    errstr = self.cmdhelper.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
                    self.detailedresults += 'Failed to configure idle time limit option.\n'
                    success = False
                else:
                    if self.undotimeout == "unset":
                        undocmd = undoprefix + '--unset /desktop/gnome/session/max_idle_time'
                    else:
                        undocmd = setprefix + '--type integer /desktop/gnome/session/max_idle_time ' + \
                            self.undotimeout
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "command", "command": undocmd}
                    self.statechglogger.recordchgevent(myid, event)
                    self.detailedresults += 'Maximum idle time limit configured successfully.\n'
            if self.logoutwrong:
                self.cmdhelper.executeCommand(setlogout)
                retcode = self.cmdhelper.getReturnCode()
                if retcode != 0:
                    errstr = self.cmdhelper.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
                    self.detailedresults += 'Failed to configure idle forced-logout option.\n'
                    success = False
                else:
                    if self.undoforcelogout == "unset":
                        undocmd = undoprefix + '--unset /desktop/gnome/session/max_idle_action'
                    else:
                        undocmd = setprefix + '--type integer /desktop/gnome/session/max_idle_action ' + \
                            self.undoforcelogout
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "command", "command": undocmd}
                    self.statechglogger.recordchgevent(myid, event)
                    self.detailedresults += 'Idle forced-logout option configured successfully.\n'
        return success

    def fixkde(self):
        '''Configure KDE 4 for automatic logout.
        @author: d.kennel


        '''
        success = True
        if self.environ.geteuid() == 0:
            self.logdispatch.log(
                LogPriority.DEBUG,
                ['ForceIdleLogout.__fixkde4', 'Root context starting loop'])
            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:
            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])
                    pdefgid = int(user[3])
                except (IndexError):
                    continue
                if puidnum == uidnum:
                    homepath = user[5]
                    found = True
            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, homepath):
                self.detailedresults += "Stonix couldn't correct the contents " + \
                    " of " + kdefile + "\n"
                success = False
        return success

    def fixosx(self):
        if not self.editor.report():
            if self.editor.fix():
                if self.editor.commit():
                    self.rulesuccess = True
                else:
                    self.detailedresults += "KVEditor could not commit " + \
                        "correct configuration\n"
                    self.rulesuccess = False
            else:
                self.detailedresults += "KVEditor could not fix configuration\n"
                self.rulesuccess = False

    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 filehandle: string
        :param kfile: 
        :param user: 
        :returns: bool

        '''
        created = False
        success = True
        self.editor = ""
        debug = ""
        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
            created = True
            if self.environ.geteuid() == 0:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": kfile}
                self.statechglogger.recordchgevent(myid, event)

        if not self.searchFile(kfile):
            if self.editor.fixables:
                if self.environ.geteuid() == 0 and not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    self.editor.setEventID(myid)
                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]
        os.chmod(kfile, 0o600)
        os.chown(kfile, uid, gid)
        resetsecon(kfile)
        return success
class ConfigureProcessAccounting(Rule):
    '''Class docs'''
    def __init__(self, config, environ, logger, statechglogger):
        """

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 97
        self.rulename = "ConfigureProcessAccounting"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.guidance = ["CCE-RHEL7-CCE-TBD 3.2.15"]
        self.applicable = {"type": "white", "family": ["linux"]}

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

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

    def report(self):
        '''


        :returns: self.compliant

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

        '''

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

        try:

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

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

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

    def fix(self):
        '''


        :returns: self.rulesuccess

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

        '''

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

        try:

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

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

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

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

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Example #15
0
class DisableRemoveableStorage(Rule):
    '''Disable removeable storage. This rule is optional, and disables USB, thunderbolt and firewire
    storage devices from accessing, or being accessed, by the system.


    '''
    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 = 29
        self.rulename = 'DisableRemoveableStorage'
        self.mandatory = False
        self.formatDetailedResults("initialize")
        self.guidance = ['NSA 2.2.2.2, CIS, NSA(2.2.2.2), cce-4006-3,4173-1']
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'fisma': 'high'
        }

        # configuration item instantiation
        datatype = "bool"
        key = "DISABLEREMOVEABLESTORAGE"
        instructions = "To disable removeable storage devices on this system, set the value of DISABLEREMOVEABLESTORAGE to True"
        default = False
        self.storageci = self.initCi(datatype, key, instructions, default)

        #global variables
        self.grubfiles = [
            "/boot/grub2/grub.cfg", "/boot/grub/grub.cfg",
            "/boot/grub/grub.conf"
        ]
        self.pcmcialist = ['pcmcia-cs', 'kernel-pcmcia-cs', 'pcmciautils']
        self.pkgremovedlist = []
        self.iditerator = 0
        self.created = False
        self.daemonpath = os.path.abspath(
            os.path.join(os.path.dirname(
                sys.argv[0]))) + "/stonix_resources/disablestorage.py"
        self.sethelptext()
        self.grubperms = ""
        self.ph = Pkghelper(self.logger, self.environ)
        self.ch = CommandHelper(self.logger)

    def report(self):
        '''report the current rule-compliance status of this system. update
        self.rulesuccess if method does not succeed. self.compliant if
        rule succeeds and reports true.


        :returns: self.compliant

        :rtype: bool

@author: Breen Malmberg

@change: dwalker - implementing kveditor and completely revamped rule
    logic. added event deletion at the beginning of the fix
@change: dwalker 8/13/2014 changed name of rule to
    DisableRemoveableStorage and rule now supports disabling other
    ports such thunderbolt and firewire

        '''

        try:
            # defaults
            self.detailedresults = ""
            if self.environ.getostype() == "Mac OS X":
                compliant = self.reportMac()
            else:
                compliant = self.reportLinux()
            self.compliant = compliant
        except OSError:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, 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("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def reportLinux(self):
        '''sub method for linux portion of compliance reporting
        @author: dwalker


        :returns: compliant

        :rtype: boolean

        '''
        compliant = True
        lsmodcmd = ""

        # determine location of lsmod binary
        if os.path.exists("/sbin/lsmod"):
            lsmodcmd = "/sbin/lsmod"
        elif os.path.exists("/usr/bin/lsmod"):
            lsmodcmd = "/usr/bin/lsmod"

        usbmods = ["usb_storage", "usb-storage"]
        # run lsmod command and look for any of the items from
        # usbmods list in the output.  If item exists in output
        # then that usb module is not disabled.  This is for
        # reporting only.  There is no fix using lsmod command.
        if lsmodcmd:
            for usb in usbmods:
                cmd = [lsmodcmd, "|", "grep", usb]
                self.ch.executeCommand(cmd)
                if self.ch.getReturnCode() == "0":
                    compliant = False
                    self.detailedresults += "lsmod command shows usb not disabled\n"
                    break

        # check compliance of grub file(s) if files exist
        if re.search("Red Hat", self.environ.getostype()) and \
                re.search("^6", self.environ.getosver()):
            self.grubperms = [0, 0, 0o600]
        elif self.ph.manager is "apt-get":
            self.grubperms = [0, 0, 0o400]
        else:
            self.grubperms = [0, 0, 0o644]
        for grub in self.grubfiles:
            if os.path.exists(grub):
                if self.grubperms:
                    if not checkPerms(grub, self.grubperms, self.logger):
                        compliant = False
                        self.detailedresults += "Permissions " + \
                                                "incorrect on " + grub + " file\n"
                contents = readFile(grub, self.logger)
                if contents:
                    for line in contents:
                        if re.search("^kernel", line.strip()) \
                                or re.search("^linux", line.strip()) \
                                or re.search("^linux16", line.strip()) \
                                or re.search("^set default_kernelopts", line.strip()):
                            if not re.search("\s+nousb\s*", line):
                                debug = grub + " file doesn't " + \
                                        "contain nousb kernel option\n"
                                self.detailedresults += grub + " file doesn't " + \
                                                        "contain nousb kernel option\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                compliant = False
                            if not re.search(
                                    "\s+usbcore\.authorized_default=0\s*",
                                    line):
                                debug = grub + " file doesn't " + \
                                        "contain usbcore.authorized_default=0 " + \
                                        "kernel option\n"
                                self.detailedresults += grub + " file doesn't " + \
                                                        "contain usbcore.authorized_default=0 " + \
                                                        "kernel option\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                                compliant = False
        # check for existence of certain usb packages, non-compliant
        # if any exist
        for item in self.pcmcialist:
            if self.ph.check(item):
                self.detailedresults += item + " is installed " + \
                                        "and shouldn't be\n"
                compliant = False

        # check modprobe files inside modprobe.d directory for
        # contents inside self.blacklist variable
        removeables = []
        found1 = True
        # self.blacklist dictionary contains the directives
        # and the value we're looking for (key) and contains
        # a default value of False for each one.  Upon finding
        # each directive and value pair e.g. blacklist usb_storage
        # the dictionary is updated with a True value.  This keeps
        # track of the directives we didnt find or that had
        # incorrect values
        self.blacklist = {
            "blacklist usb_storage": False,
            "install usbcore /bin/true": False,
            "install usb-storage /bin/true": False,
            "blacklist uas": False,
            "blacklist firewire-ohci": False,
            "blacklist firewire-sbp2": False
        }
        #check if /etc/modprobe.d directory exists
        if os.path.exists("/etc/modprobe.d"):
            #extract all files inside modprobe.d
            dirs = glob.glob("/etc/modprobe.d/*")
            # since file name doesn't matter
            # i.e. all files are read and treated the same in
            # modprobe.d, if directives are found in any of
            # the files inside this directory, where they don't
            # have to be in the same file, the system is compliant
            for directory in dirs:
                if os.path.isdir(directory):
                    continue
                contents = readFile(directory, self.logger)
                for item in self.blacklist:
                    for line in contents:
                        if re.search("^" + item, line.strip()):
                            self.blacklist[item] = True
            # if we don't find all directives in any of the files in
            # modprobe.d, we will now check /etc/modprobe.conf as
            # they are all equivalent. We will still keep track of
            # whether we already found one directive in one of the
            # files in modprobe.d
            for item in self.blacklist:
                if not self.blacklist[item]:
                    found1 = False
        else:
            found1 = False

        # either not all directives inside self.blacklist were found
        # or /etc/modprobe.d didn't exist.  Now we check /etc/modprobe.conf
        # for any remaining unfound directives.
        if not found1:
            if os.path.exists("/etc/modprobe.conf"):
                contents = readFile("/etc/modprobe.conf")
                if contents:
                    for item in self.blacklist:
                        for line in contents:
                            if re.search("^" + item, line.strip()):
                                self.blacklist[item] = True
            for item in self.blacklist:
                if not self.blacklist[item]:
                    debug = "modprobe.conf nor blacklist " + \
                            "files contain " + item + "\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    compliant = False
        # any directives that were found we remove from self.blacklist
        # We must add to a variable called removeables first then
        # iterate through removeables and remove each item self.blacklist
        for item in self.blacklist:
            if self.blacklist[item]:
                removeables.append(item)
        for item in removeables:
            del (self.blacklist[item])

        # check the contents of the udev file for a certain desired line
        self.udevfile = "/etc/udev/rules.d/10-local.rules"
        found2 = False
        if os.path.exists(self.udevfile):
            if not checkPerms(self.udevfile, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions not correct " + \
                                        "on " + self.udevfile + "\n"
                compliant = False
            contents = readFile(self.udevfile, self.logger)
            for line in contents:
                if re.search(
                        "ACTION\=\=\"add\"\, SUBSYSTEMS\=\=\"usb\"\, RUN\+\=\"/bin/sh \-c \'for host in /sys/bus/usb/devices/usb\*\; do echo 0 \> \$host/authorized\_default\; done\'\"",
                        line.strip()):
                    found2 = True
            if not found2:
                self.detailedresults += "Udev rule not found to disable usb at boot\n"
                debug = "Udev rule not found to disable usb at boot\n"
                self.logger.log(LogPriority.DEBUG, debug)
                compliant = False
        else:
            self.detailedresults += "Udev file doesn't exist to disable usb at boot\n"
            debug = "Udev file doesn't exist to disable usb at boot\n"
            self.logger.log(LogPriority.DEBUG, debug)
            compliant = False
        return compliant

    def reportMac(self):
        '''
        :returns: compliant
        :rtype: bool
        '''

        self.detailedresults = ""
        compliant = True
        self.setvars()
        if not self.usbprofile:
            self.detailedresults += "Could not locate the appropriate usb disablement profile for your system.\n"
            compliant = False
            self.formatDetailedResults("report", compliant,
                                       self.detailedresults)
            self.logdispatch.log(LogPriority.INFO, self.detailedresults)
            return compliant
        self.usbdict = {
            "com.apple.systemuiserver": {
                "harddisk-external": {
                    "val": ["deny", "eject"],
                    "type": "",
                    "accept": "",
                    "result": False
                }
            }
        }
        self.usbeditor = KVEditorStonix(self.statechglogger, self.logger,
                                        "profiles", self.usbprofile, "",
                                        self.usbdict, "", "")
        if not self.usbeditor.report():
            if self.usbeditor.badvalues:
                self.detailedresults += self.usbeditor.badvalues + "\n"
            self.detailedresults += "USB Disablement profile either not installed or values are incorrect\n"
            compliant = False
        return compliant

    def setvars(self):
        self.usbprofile = ""
        baseconfigpath = "/Applications/stonix4mac.app/Contents/Resources/stonix.app/Contents/MacOS/stonix_resources/files/"
        self.usbprofile = baseconfigpath + "stonix4macDisableUSB.mobileconfig"

        # the following path and dictionaries are for testing on local vm's
        # without installing stonix package each time.  DO NOT DELETE
        # basetestpath = "/Users/username/stonix/src/stonix_resources/files/"
        # self.usbprofile = basetestpath + "stonix4macDisableUSB.mobileconfig"
        if not os.path.exists(self.usbprofile):
            self.logger.log(
                LogPriority.DEBUG,
                "Could not locate appropriate usb disablement profile\n")
            self.usbprofile = ""

    def fix(self):
        '''attempt to perform necessary operations to bring the system into
        compliance with this rule.
        
        @author Breen Malmberg
        @change: dwalker - implemented event deletion at the beginning of fix,
            also implemented a check for the ci value to see if fix should
            even be run.


        '''

        try:
            success = True
            self.detailedresults = ""
            # clear out event history so only the latest fix is recorded
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            if not self.storageci.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.environ.getostype() == "Mac OS X":
                success = self.fixMac()
            else:
                success = self.fixLinux()
            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

    def fixMac(self):
        '''This method will attempt to disable certain storage ports by moving
        certain kernel extensions.  If the check box is checked we will
        move the kernel (if present) associated with that storage port/device
        into a folder designated for those disabled extensions.  If the
        check box is unchecked, we will assume the user doesn't want this
        disabled and if the kernel is no longer where it should be, we will
        check the disabled extensions folder to see if it was previously
        disabled.  If it's in that folder, we will move it back.
        @author: Breen Malmberg


        :returns: bool
        @change: dwalker 8/19/2014

        '''
        success = True
        if not self.usbprofile:
            return False
        if not self.usbeditor.report():
            if self.usbeditor.fix():
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.usbeditor.setEventID(myid)
                if not self.usbeditor.commit():
                    success = False
                    self.detailedresults += "Unable to install " + self.usbprofile + " profile\n"
                    self.logdispatch.log(LogPriority.DEBUG,
                                         "Kveditor commit failed")
            else:
                success = False
                self.detailedresults += "Unable to install " + self.passprofile + "profile\n"
                self.logdispatch.log(LogPriority.DEBUG, "Kveditor fix failed")
        else:
            success = False
            self.detailedresults += "Password CI was not enabled.\n"
        return success

    def fixLinux(self):
        '''sub method for linux portion of compliance fixing
        @author: dwalker


        :returns: success

        :rtype: boolean

        '''
        success = True
        created1, created2 = False, False
        changed = False
        tempstring = ""
        grubfilefound = False

        for grub in self.grubfiles:
            if os.path.exists(grub):
                grubfilefound = True
                if self.grubperms:
                    if not checkPerms(grub, self.grubperms, self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(grub, self.grubperms, self.logger,
                                        self.statechglogger, myid):
                            success = False
                contents = readFile(grub, self.logger)
                kernellinefound = False
                if contents:
                    for line in contents:
                        if re.search("^kernel", line.strip()) or re.search("^linux", line.strip()) \
                                or re.search("^linux16", line.strip()):
                            kernellinefound = True
                            if not re.search("\s+nousb\s*", line):
                                changed = True
                                tempstring += line.strip() + " nousb"
                            if not re.search(
                                    "\s+usbcore\.authorized_default=0\s+",
                                    line):
                                changed = True
                                tempstring += line.strip(
                                ) + " usbcore.authorized_default=0"
                            tempstring += "\n"
                        elif re.search("^set default_kernelopts",
                                       line.strip()):
                            # Fedora 31 has changed it's kernel option line format
                            kernellinefound = True
                            kernelline = line.strip()
                            if not re.search("\s+nousb\s*", kernelline):
                                changed = True
                                kernelline = re.sub("\"$", " nousb\"",
                                                    kernelline)
                            if not re.search(
                                    "\s+usbcore\.authorized_default=0\s+",
                                    kernelline):
                                changed = True
                                kernelline = re.sub(
                                    "\"$", " usbcore.authorized_default=0\"",
                                    kernelline)
                            tempstring += kernelline + "\n"
                        else:
                            tempstring += line
                if not kernellinefound:
                    changed = False
                    self.detailedresults += "The grub file doesn't contain kernel line\n" + \
                                            "Unable to fully implement fixes in this rule\n"
                    success = False
                if changed:
                    tmpfile = grub + ".tmp"
                    if writeFile(tmpfile, tempstring, self.logger):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "conf", "filepath": grub}
                        self.statechglogger.recordchgevent(myid, event)
                        self.statechglogger.recordfilechange(
                            grub, tmpfile, myid)
                        os.rename(tmpfile, grub)
                        if not setPerms(grub, self.grubperms, self.logger):
                            success = False
                            self.detailedresults += "Unable to set permissions on " + \
                                                    grub + " file\n"
                    else:
                        success = False
        if not grubfilefound:
            self.detailedresults += "No grub configuration file found\n" + \
                                    "Unable to fully fix system for this rule\n"
            success = False
        blacklistf = "/etc/modprobe.d/stonix-blacklist.conf"
        tempstring = ""
        # Check if self.blacklist still contains values, if it
        # does, then we didn't find all the blacklist values
        # in report
        if self.blacklist:
            # didn't find one or more directives in the files
            # inside modprobe.d so we now check an alternate file
            # we create stonixblacklist file if it doesn't
            # exist and put remaining unfound blacklist
            # items there
            if not os.path.exists(blacklistf):
                created1 = True
                createFile(blacklistf, self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": blacklistf}
                self.statechglogger.recordchgevent(myid, event)
            # file was already present and we need contents already
            # inside file to remain in newly written file
            if not created1:
                contents = readFile(blacklistf, self.logger)
                for item in contents:
                    tempstring += item
            for item in self.blacklist:
                tempstring += item + "\n"
            tmpfile = blacklistf + ".tmp"
            if writeFile(tmpfile, tempstring, self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "conf", "filepath": blacklistf}
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange(blacklistf, tmpfile, myid)
                os.rename(tmpfile, blacklistf)
                os.chown(blacklistf, 0, 0)
                os.chmod(blacklistf, 420)
                resetsecon(blacklistf)
            if not checkPerms(blacklistf, [0, 0, 420], self.logger):
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                if not setPerms(blacklistf, [0, 0, 420], self.logger,
                                self.statechglogger, myid):
                    success = False
            if self.ph.manager == "apt-get":
                cmd = ["/usr/sbin/update-initramfs", "-u"]
                if not self.ch.executeCommand(cmd):
                    success = False
                    self.detailedresults += "Unable to run update-initramfs command\n"
        for item in self.pcmcialist:
            if self.ph.check(item):
                self.ph.remove(item)
                self.pkgremovedlist.append(item)
        if not os.path.exists(self.udevfile):
            if not createFile(self.udevfile, self.logger):
                self.detailedresults += "Unable to create " + \
                                        self.udevfile + " file\n"
                success = False
            else:
                created2 = True
        if os.path.exists(self.udevfile):
            if not checkPerms(self.udevfile, [0, 0, 0o644], self.logger):
                if created2:
                    if not setPerms(self.udevfile, [0, 0, 0o644], self.logger):
                        success = False
                        self.detailedresults += "Unable to set " + \
                                                "permissions on " + self.udevfile + "\n"
                else:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(self.udevfile, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
                        self.detailedresults += "Unable to set " + \
                                                "permissions on " + self.udevfile + "\n"
            found = False
            contents = readFile(self.udevfile, self.logger)
            tempstring = ""
            for line in contents:
                if re.search(
                        "ACTION==\"add\"\, SUBSYSTEMS==\"usb\"\, RUN+=\"/bin/sh -c \'for host in /sys/bus/usb/devices/usb\*\; do echo 0 > \$host/authorized_default; done\'\"",
                        line.strip()):
                    found = True
                tempstring += line
            if not found:
                tempstring += "ACTION==\"add\", SUBSYSTEMS==\"usb\", RUN+=\"/bin/sh -c \'for host in /sys/bus/usb/devices/usb*; do echo 0 > $host/authorized_default; done\'\""
                tmpfile = self.udevfile + ".tmp"
                if writeFile(tmpfile, tempstring, self.logger):
                    if not created2:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "conf",
                            "filepath": self.udevfile
                        }
                        self.statechglogger.recordchgevent(myid, event)
                        self.statechglogger.recordfilechange(
                            self.udevfile, tmpfile, myid)
                    os.rename(tmpfile, self.udevfile)
                    os.chown(self.udevfile, 0, 0)
                    os.chmod(self.udevfile, 0o644)
                    resetsecon(self.udevfile)
                else:
                    success = False
                    self.detailedresults += "Unable to write changes " + \
                                            "to " + self.udevfile + "\n"
        return success
Example #16
0
class SetTFTPDSecureMode(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.rootrequired = True
        self.rulenumber = 98
        self.rulename = 'SetTFTPDSecureMode'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        datatype = 'bool'
        key = 'SETTFTPDSECUREMODE'
        instructions = "To disable this rule set the value of " + \
            "SETTFTPDSECUREMODE to False"
        default = True
        self.tftpdci = self.initCi(datatype, key, instructions, default)

        self.guidance = ["NSA 3.1.5.4"]
        self.iditerator = 0
        self.editor = ""
        self.applicable = {
            'type': 'white',
            'family': ['linux', 'solaris', 'freebsd']
        }

    def report(self):
        """

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

        try:
            self.detailedresults = ""
            compliant = True

            self.ph = Pkghelper(self.logger, self.environ)
            if self.ph.manager == "apt-get":
                pkg = "tftpd-hpa"
                if self.ph.check(pkg):
                    self.tftpFile = "/etc/default/tftpd-hpa"
                    if os.path.exists(self.tftpFile):
                        compliant = self.reportDebianSys()
            else:
                pkg = "tftp-server"
                if self.ph.check(pkg):
                    self.tftpFile = "/etc/xinetd.d/tftp"
                    if os.path.exists(self.tftpFile):
                        compliant = self.reportOtherSys()
            self.compliant = compliant
        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 reportDebianSys(self):
        """

        :return: compliant
        :rtype: bool
        """

        contents = readFile(self.tftpFile, self.logger)
        found1 = False
        found2 = False
        compliant = True
        compliant1 = True
        compliant2 = True
        for line in contents:
            if re.search("TFTP_OPTIONS", line):
                found1 = True
                if re.search("=", line):
                    tmp = line.strip()
                    tmp = tmp.split("=")
                    try:
                        #remove beginning and ending spaces of the part
                        #after = if necessary
                        opts = tmp[1].strip()
                        #opts = "\" sdfads \""
                        #replace actual quotes with nothing
                        opts = re.sub("\"", "", opts)
                        #opts = " sdfads "
                        #once again replace any whitespace with just one space
                        opts = re.sub("/s+", " ", opts.strip())
                        #split by single whitespace
                        opts = opts.split(" ")
                        if "--secure" not in opts:
                            compliant1 = False
                    except IndexError:
                        self.detailedresults += "No value after = \n" + \
                            "Bad file format.\n"
                        compliant1 = False
            elif re.search("TFTP_DIRECTORY", line):
                found2 = True
                if re.search("=", line):
                    tmp = line.strip()
                    tmp = tmp.split("=")
                    try:
                        opts = tmp[1].strip()
                        opts = re.sub("\"", "", opts)
                        if not re.search("^/var/lib/tftpboot$", opts):
                            compliant2 = False
                    except IndexError:
                        self.detailedresults += "No value after = \n" + \
                            "Bad file format.\n"
                        compliant2 = False
        if not compliant1:
            self.detailedresults += self.tftpFile + " doesn't contain " + \
                "the --secure option for the TFTP_OPTIONS key\n"
            compliant = False
        if not compliant2:
            self.detailedresults += self.tftpFile + " doesn't contain " + \
                "the desired directory for the TFTP_DIRECTORY key\n"
            compliant = False
        if not found1:
            self.detailedresults += self.tftpFile + " doesn't contain " + \
                "the TFTP_OPTIONS key at all\n"
            compliant = False
        if not found2:
            self.detailedresults += self.tftpFile + " doesn't contain " + \
                "the TFTP_DIRECTORY key at all\n"
            compliant = False
        return compliant

    def reportOtherSys(self):
        """

        :return: compliant
        :rtype: bool
        """

        tftpoptions, contents2 = [], []
        contents = readFile(self.tftpFile, self.logger)
        found = False
        compliant = True
        i = 0
        if not checkPerms(self.tftpFile, [0, 0, 420], self.logger):
            self.detailedresults += "Permissions on tftp file are incorrect\n"
            compliant = False
        try:
            for line in contents:
                if re.search("service tftp", line.strip()):
                    contents2 = contents[i + 1:]
                else:
                    i += 1
        except IndexError:
            pass
        if contents2:
            if contents2[0].strip() == "{":
                del (contents2[0])
                if contents2:
                    i = 0
                    while i <= len(contents2) and contents2[i].strip(
                    ) != "}" and contents2[i].strip() != "{":
                        tftpoptions.append(contents2[i])
                        i += 1
                    if tftpoptions:
                        for line in tftpoptions:
                            if re.search("server_args", line):
                                found = True
                                if re.search("=", line):
                                    line = line.split("=")
                                    val = re.sub("\s+", " ", line[1].strip())
                                    if not re.search("-s",
                                                     val) and not re.search(
                                                         "--search", val):
                                        compliant = False
                                        self.detailedresults += "server_args line " + \
                                            "doesn't contain the -s option\n"
                                    elif not re.search(
                                            "\s?-s /var/lib/tftpboot", val
                                    ) and not re.search(
                                            "\s?--search /var/lib/tftpboot",
                                            val):
                                        compliant = False
                                        self.detailedresults += "server_args line " + \
                                            "doesn't contain the correct contents\n"
                                else:
                                    self.detailedresults += "server_args line " + \
                                        "found but contains no = sign, bad format.\n"
                                    compliant = False
                    else:
                        compliant = False
                        self.detailedresults += "There are no tftp " + \
                            "options inside tftp file\n"
        else:
            compliant = False
            self.detailedresults += "tftp file doesn't contain the " + \
                "line service tftp\nBad Format\n"
        if not found:
            self.detailedresults += "server_args line not found.\n"
            compliant = False
        elif found and not compliant:
            compliant = False
            self.detailedresults += "server_args line found but " + \
                "either doesn't contain -s argument or has bad " + \
                "format.\n"
        return compliant

    def fix(self):
        """

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

        try:
            success = True
            self.detailedresults = ""
            # clear out event history so only the latest fix is recorded
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            if not self.tftpdci.getcurrvalue():
                return

            elif self.ph.manager == "apt-get":
                if os.path.exists(self.tftpFile):
                    success = self.fixDebianSys()
            else:
                if os.path.exists(self.tftpFile):
                    success = self.fixOtherSys()
            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

    def fixOtherSys(self):
        """

        :return: success
        :rtype: bool
        """

        success = True
        if not checkPerms(self.tftpFile, [0, 0, 420], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.tftpFile, [0, 0, 420], self.logger,
                            self.statechglogger, myid):
                self.detailedresults += "Unable to set " + \
                    "permissions on " +  self.tftpFile + "\n"
                success = False
        try:
            contents2 = []
            contents = readFile(self.tftpFile, self.logger)
            found = False
            tempstring = ""
            i = 0
            tftplinefound = False
            for line in contents:
                if re.search("service tftp", line.strip()):
                    tempstring += line
                    tftplinefound = True
                    contents2 = contents[i + 1:]
                    break
                else:
                    tempstring += line
                    i += 1
            if not tftplinefound:
                self.detailedresults += "tftp file doesn't contain " + \
                    "\"service tftp\" line.  Stonix will not attempt to " + \
                    "fix this.  This will require a manual fix\n"
                return False
            if contents2:
                if contents2[0].strip() == "{":
                    tempstring += contents2[0]
                    del (contents2[0])
                    if contents2:
                        for line in contents2:
                            if re.search("server_args", line):
                                found = True
                                if re.search("=", line):
                                    tmp = line.split("=")
                                    val = re.sub("/s+", " ", tmp[1].strip())
                                    if re.search("-s", val) or re.search(
                                            "--secure", val):
                                        if not re.search(
                                                "-s /var/lib/tftpboot", val
                                        ) and not re.search(
                                                "--secure /var/lib/tftpboot",
                                                val):
                                            val = re.sub(
                                                "-s\s{0,1}/{0,1}.*\s{0,1}",
                                                "-s /var/lib/tftpboot", tmp[1])
                                            tempstring += "\tserver_args \t\t= " + val + "\n"
                                        else:
                                            tempstring += line
                                    else:
                                        tempstring += line + " -s /var/lib/tftpboot\n"
                                else:
                                    tempstring += "\tserver_args\t\t= -s /var/lib/tftpboot\n"
                            elif re.search("}", line.strip()):
                                if not found:
                                    tempstring += "\tserver_args\t\t= -s /var/lib/tftpboot\n"
                                    tempstring += "}"
                                    break
                                else:
                                    tempstring = ""
                                    break
                            else:
                                tempstring += line
        except IndexError:
            self.detailedresults += "The tftp file is in bad format\n " + \
                "Will not attempt to correct this file.  Exiting\n"
            return False
        if not tempstring:
            return True
        tmpfile = self.tftpFile + ".tmp"
        if writeFile(tmpfile, tempstring, self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "conf", "filepath": self.tftpFile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.tftpFile, tmpfile, myid)
            os.rename(tmpfile, self.tftpFile)
            os.chown(self.tftpFile, 0, 0)
            os.chmod(self.tftpFile, 420)
            resetsecon(self.tftpFile)
        else:
            self.detailedresults += "Unable to write new contents " + \
                "to " + self.tftpFile + " file.\n"
            success = False
        return success

    def fixDebianSys(self):
        """

        :return: success
        :rtype: bool
        """

        success = True
        if not checkPerms(self.tftpFile, [0, 0, 420], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.tftpFile, [0, 0, 420], self.logger,
                            self.statechglogger, myid):
                self.detailedresults += "Unable to set " + \
                    "permissions on " +  self.tftpFile + "\n"
                success = False
        contents = readFile(self.tftpFile, self.logger)
        found1 = False
        found2 = False
        tempstring = ""
        for line in contents:
            if re.search("TFTP_OPTIONS", line):
                if found1:
                    continue
                if re.search("=", line):
                    tmp = line.strip()
                    tmp = tmp.split("=")
                    try:
                        #remove beginning and ending spaces of the part
                        #after = if necessary
                        opts = tmp[1].strip()
                        #opts = "\" sdfads \""
                        #replace actual quotes with nothing
                        opts = re.sub("\"", "", opts)
                        #opts = " sdfads "
                        #once again replace any whitespace with just one space
                        opts = re.sub("/s+", " ", opts.strip())
                        #split by single whitespace
                        opts = opts.split(" ")
                        if "--secure" not in opts:
                            continue
                        else:
                            tempstring += line
                            found1 = True
                    except IndexError:
                        continue
                else:
                    continue
            elif re.search("TFTP_DIRECTORY", line):
                if found2:
                    continue

                if re.search("=", line):
                    tmp = line.strip()
                    tmp = tmp.split("=")
                    try:
                        opts = tmp[1].strip()
                        opts = re.sub("\"", "", opts)
                        if not re.search("^/var/lib/tftpboot$", opts):
                            continue
                        else:
                            tempstring += line
                            found2 = True
                    except IndexError:
                        continue
            else:
                tempstring += line
        if not found1:
            tempstring += "TFTP_OPTIONS=\"--secure\"\n"
        if not found2:
            tempstring += "TFTP_DIRECTORY=\"/var/lib/tftpboot\"\n"
        tmpfile = self.tftpFile + ".tmp"
        if writeFile(tmpfile, tempstring, self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "conf", "filepath": self.tftpFile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.tftpFile, tmpfile, myid)
            os.rename(tmpfile, self.tftpFile)
            os.chown(self.tftpFile, 0, 0)
            os.chmod(self.tftpFile, 420)
            resetsecon(self.tftpFile)
        else:
            self.detailedresults += "Unable to write new contents " + \
                "to " + self.tftpFile + " file.\n"
            success = False
        return success
Example #17
0
class ConfigureSystemAuthentication(Rule):
    """
    Configure system authentication and password settings in accordance
with rhel 7 stig requirements
    """

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

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 57
        self.rulename = "ConfigureSystemAuthentication"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.applicable = {'type': 'white',
                           'family': 'linux'}
        datatype = "bool"
        key = "CONFIGSYSAUTH"
        instructions = "To disable this rule, set the value of " + \
            "CONFIGSYSAUTH to False."
        default = True
        self.ci1 = self.initCi(datatype, key, instructions, default)

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

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

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

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

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

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

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

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

        @author: Derek Walker

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

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

        try:

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

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

        return self.compliant

    def fix(self):
        """
        ConfigureSystemAuthentication.fix() method to fix the system to be
        compliant with authentication and password settings

        @author: Derek Walker

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

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

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

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

            if not self.fixLinux():
                self.rulesuccess = False

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

        return self.rulesuccess

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

        @author: Derek Walker

        :return: compliant
        :rtype: bool
        """

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

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

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

        if not self.check_showfailed_logins():
            compliant = False

        return compliant

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

        @author: Derek Walker

        :return: success
        :rtype: bool
        """

        success = True

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

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

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

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

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

        if not self.set_showfailed_logins():
            success = False

        return success

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

        :return:
        """

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

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

        return configured

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

        :return: success
        :rtype: bool
        """

        success = True

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

        return success

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

        """

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

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

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

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

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

        :return: installed
        :rtype: bool
        """

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

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

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

        return installed

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

        :return: passwords_configured
        :rtype: bool
        """

        passwords_configured = True

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

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

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

        return passwords_configured

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

        @author: Derek Walker

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

        compliant = True
        regex1 = ""

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

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

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

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

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

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

        return compliant

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

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

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

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

        @author: Derek Walker

        :return: compliant
        :rtype: bool
        """

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

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

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

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

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

        :return: compliant
        :rtype: bool
        """

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

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

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

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

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

        @author: Derek Walker

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

    def setpwquality(self):
        """

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

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


        :return: bool

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

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

        :return: success
        :rtype: bool
        """

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

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

        return success
Example #18
0
class SecureSSH(Rule):
    """
    The SecureSSH class makes a number of configuration changes to SSH in \
    order to ensure secure use of the functionality.

    """

    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 8
        self.rulename = 'SecureSSH'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.environ = environ
        self.sethelptext()
        self.applicable = {'type': 'white',
                           'family': ['linux', 'solaris', 'freebsd'],
                           'os': {'Mac OS X': ['10.15', 'r', '10.15.10']}}
        datatype = 'bool'
        key = 'SECURESSH'
        instructions = "To disable this rule set the value " + \
                       "of SECURESSH to False"
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.guidance = ['CIS, NSA(3.5.2.1)', 'CCE 4325-7', 'CCE 4726-6',
                         'CCE 4475-0', 'CCE 4370-3', 'CCE 4387-7',
                         'CCE 3660-8', 'CCE 4431-3', 'CCE 14716-5',
                         'CCE 14491-5']
        self.ed1, self.ed2 = "", ""

        self.osname = self.environ.getosname()
        if self.osname == "Mac OS":
            mpa_datatype = "bool"
            mpa_key = "ENABLEMACPIVAUTHSSH"
            mpa_instructions = "To enable piv authentication over ssh, on Mac OS, set the value of ENABLEMACPIVAUTHSSH to True"
            mpa_default = False
            self.mac_piv_auth_CI = self.initCi(mpa_datatype, mpa_key, mpa_instructions, mpa_default)

    def report(self):
        """
        check if ssh is installed and if the correct configuration options
        are set in the configuration files

        :return: self.compliant
        :rtype: bool

        """
        try:
            self.detailedresults = ""
            self.installed = False
            packages = ["ssh", "openssh", "openssh-server", "openssh-client"]
            self.ph = Pkghelper(self.logger, self.environ)
            self.compliant = True
            self.sh = ServiceHelper(self.environ, self.logger)
            self.macloaded = False
            compliant = True
            self.client = {"Host": "*",
                           "Protocol": "2",
                           "GSSAPIAuthentication": "yes",
                           "GSSAPIDelegateCredentials": "yes"}
            self.server = {"Protocol": "2",
                           "SyslogFacility": "AUTHPRIV",
                           "PermitRootLogin": "******",
                           "MaxAuthTries": "5",
                           "RhostsRSAAuthentication": "no",
                           "HostbasedAuthentication": "no",
                           "IgnoreRhosts": "yes",
                           "PermitEmptyPasswords": "no",
                           "PasswordAuthentication": "yes",
                           "ChallengeResponseAuthentication": "no",
                           "KerberosAuthentication": "yes",
                           "GSSAPIAuthentication": "yes",
                           "GSSAPICleanupCredentials": "yes",
                           "UsePAM": "yes",
                           "Ciphers": "aes128-ctr,aes192-ctr,aes256-ctr",
                           "PermitUserEnvironment": "no",
                           "PrintLastLog": "yes",
                           "MACs": "hmac-sha2-256,hmac-sha2-512",
                           "UsePrivilegeSeparation": "sandbox",
                           "StrictModes": "yes",
                           "Compression": "no"}

            if self.environ.getostype() == "Mac OS X":
                self.serverfile = '/private/etc/ssh/sshd_config'
                self.clientfile = '/private/etc/ssh/ssh_config'
            else:
                self.serverfile = "/etc/ssh/sshd_config"  # server file
                self.clientfile = "/etc/ssh/ssh_config"  # client file

            # Portion for non mac systems i.e. linux
            if self.environ.getostype() != "Mac OS X":
                for package in packages:
                    if self.ph.check(package):
                        self.installed = True
                        break
                # If ssh is not installed there is no need to configure, return True
                if not self.installed:
                    self.formatDetailedResults("report", self.compliant, self.detailedresults)
                    self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                    return self.compliant
            # Portion for Mac systems
            else:
                # ssh is installed/enabled
                if self.sh.auditService("/System/Library/LaunchDaemons/ssh.plist", serviceTarget="com.openssh.sshd"):
                    self.macloaded = True

                # if ssh not installed/enabled, no need to configure, return True
                if not self.macloaded:
                    self.detailedresults += "SSH not installed/enabled. Nothing to configure.\n"
                    self.formatDetailedResults("report", self.compliant, self.detailedresults)
                    self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                    return self.compliant
                # if not self.reportMac():
                #     compliant = False

            # Portion for both mac and linux reporting
            if not self.reportSSHFile(self.serverfile, self.server):
                compliant = False
            if not self.reportSSHFile(self.clientfile, self.client):
                compliant = False
            self.compliant = compliant

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

    def fix(self):
        """
        Main fix method divided up into mac and linux sub methods for
        correcting ssh server and client files as well as configuring
        Mac OS smart card authentication.

        :return: self.rulesuccess
        :rtype: bool

        """
        try:
            self.detailedresults = ""
            self.iditerator = 0
            success = True
            debug = ""
            if not self.ci.getcurrvalue():
                self.detailedresults += "The rule CI was not enabled, so nothing was done.\n"
                self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
                self.logdispatch.log(LogPriority.INFO, self.detailedresults)
                self.rulesuccess = success
                return self.rulesuccess

            # Clear events from any previous fix run
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            # Portion for both linux and Mac
            if not self.fixSSHFile(self.serverfile, self.server):
                success = False
            if not self.fixSSHFile(self.clientfile, self.client):
                success = False
            # Mac only portion
            if self.osname == "Mac OS":
                # runs the fix for the smartcard authentication for mac
                if not self.fixMac():
                    success = False
            self.rulesuccess = success

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

    def reportSSHFile(self, sshfile, directives):
        """
        Report configuration options of config files
        :param: sshfile - filepath string
        :param: directives - dictionary of desired directives
        :return: compliant
        :rtype: bool

        """
        compliant = True
        debug = ""
        directives = dict(directives)
        tpath = sshfile + ".tmp"

        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["PasswordAuthentication"] = "no"
                    self.server = directives
            editor = KVEditorStonix(self.statechglogger,
                                      self.logger, "conf",
                                      sshfile, tpath,
                                      directives, "present",
                                      "space")
            if not editor.report():
                self.detailedresults += "Did not find the correct " + \
                                        "contents in sshd_config\n"
                compliant = False

            # for ubuntu systems we want to make sure the following two
            # directives don't exist in the server file
            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)
                if not editor.report():
                    self.detailedresults += "didn't find the correct" + \
                            " contents in sshd_config\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                    compliant = False
            if not checkPerms(sshfile, [0, 0, 0o644],
                              self.logger):
                self.detailedresults += "Incorrect permissions for " + \
                                        "file " + self.serverfile + "\n"
                compliant = False
        else:
            self.detailedresults += sshfile + " does not exist\n"
            compliant = False
        return compliant

    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

    def fixMac(self):
        """

        :return: success
        :rtype: bool
        """

        success = True

        # reload ssh to read the new configurations
        self.logger.log(LogPriority.DEBUG, "Restarting sshd service to read/load the new configuration changes")
        if self.osname == "Mac OS":
            if not self.sh.reloadService("/System/Library/LaunchDaemons/ssh.plist",
                                         serviceTarget="com.openssh.sshd"):
                success = False
                self.detailedresults += "Failed to load the new ssh configuration changes\n"
        else:
            if not self.sh.reloadService("sshd"):
                success = False
                self.detailedresults += "Failed to load the new ssh configuration changes\n"

        return success
Example #19
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
Example #20
0
class ConfigureKerberos(Rule):
    '''@author: Ekkehard J. Koch'''

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

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

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

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


        :returns: self.compliant

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

        '''

        self.compliant = True
        self.detailedresults = ""

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

        try:

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

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

    def fix(self):
        '''run fix actions


        :returns: self.rulesuccess

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

        '''

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

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

        try:

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

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

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += str(traceback.format_exc())
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
Example #21
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
Example #22
0
class AuditFirefoxUsage(Rule):
    """
    This module will audit firefox browser usage to determine
    if any site not listed in the APPROVEDDOMAINS CI has been visited
    while running in root mode

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

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

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

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

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

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

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

        """

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

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

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

            for ffDir in ffDirs:
                placesPath = ffDir + "/places.sqlite"
                if not os.path.exists(placesPath):
                    continue
                ch = CommandHelper(self.logger)
                command = ["/usr/bin/sqlite3", placesPath, ".tables"]
                ch.executeCommand(command)
                if not ch.getReturnCode() == 0:
                    # The version of sqlite3 on RHEL 6 cannot read these dbs.
                    # Instead, we will examine the file manually, looking for
                    # entries beginning with %\x08.
                    sqlBin = open(placesPath, "rb").read()
                    urlList = re.findall("%\x08https?://.*?/", sqlBin)
                    for url in urlList:
                        urls.append(url[2:])
                else:
                    conn = sqlite3.connect(placesPath)
                    c = conn.cursor()
                    c.execute("SELECT host FROM moz_hosts")
                    results = c.fetchall()
                    debug = "Results of 'SELECT host FROM moz_hosts': " + \
                        str(results)
                    self.logger.log(LogPriority.DEBUG, debug)
                    # Results are inside of a tuple
                    for item in results:
                        urls.append(item[0])
            badUrls = []
            urls = list(set(urls))
            if urls:
                self.logger.log(LogPriority.DEBUG, "URLs found: " + str(urls))
                for url in urls:
                    approved = self.approvedDomainsCi.getcurrvalue()
                    foundApproved = False
                    for site in approved:
                        if re.search(site, url):
                            foundApproved = True
                    if not foundApproved:
                        compliant = False
                        badUrls.append(url)
                if badUrls:
                    self.detailedresults = "URLs found in root's " + \
                        "Firefox history that are not on the approved " + \
                        "list: " + ", ".join(badUrls) + "\n"
            self.logger.log(LogPriority.DEBUG,
                            "Bad URLs found: " + str(badUrls))
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def getFirefoxDirs(self):
        """Discover the randomly-generated Firefox profile directory(ies) for the
        root user.

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

        """
        # It's possible to have several FF profiles for each user account. This
        # method will therefore return a list.
        ffDirs = []
        homeDir = "/root"
        ffParent = homeDir + "/.mozilla/firefox"
        if os.path.exists(ffParent):
            profileDirs = glob(ffParent + "/*.default")
            debug = "Found the following Firefox profile directories: " + \
                str(profileDirs)
            self.logger.log(LogPriority.DEBUG, debug)
            for pDir in profileDirs:
                # Since we gave glob the full path, the returned list will
                # have the full path for each entry
                ffDirs.append(pDir)
        return ffDirs
Example #23
0
class ConfigureMACPolicy(Rule):
    """The ConfigureMACPolicy class configures either selinux or apparmor
    depending on the os platform.
    @change: Derek Walker - created two config items, one for enable/disable, and
        another for whether the user wants to use permissive or enforcing

    """

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

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

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

    def report(self):
        """

        :return: 
        """

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

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

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

        try:

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

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

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

        return self.compliant

    def set_selinux_conf_path(self):
        """

        :return:
        """

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

    def reportSelinux(self):
        """

        :return:
        """

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

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

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

        self.set_selinux_conf_path()

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

        return compliant

    def reportApparmor(self):
        """

        :return:
        """

        compliant = True

        aa_enabled = "Yes"

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

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

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

        return compliant

    def fix(self):
        """

        :return: 
        """

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

        try:

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

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

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

        return self.rulesuccess

    def fixSelinux(self):
        """

        :return:
        """

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

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

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

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

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

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

        return success

    def fixApparmor(self):
        """

        :return:
        """

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

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

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

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

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

            os.rename(tmp_grub_file, grub_file)

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

        return success
Example #24
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
class DisableUbuntuDataCollection(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.environ = environ
        self.rulenumber = 311
        self.rulename = "DisableUbuntuDataCollection"
        self.mandatory = True
        self.rootrequired = True
        self.formatDetailedResults("initialize")
        self.guidance = []
        self.applicable = {'type': 'white', 'os': {'Ubuntu': ['16.04', '+']}}
        self.sethelptext()

        datatype = "bool"
        key = "DISABLEUBUNTUDATACOLLECTION"
        instructions = """To prevent the diabling of data collection from this system, set the value of DISABLEUBUNTUDATACOLLECTION to False."""
        default = True
        self.enabledCI = self.initCi(datatype, key, instructions, default)

    def report(self):
        '''Check for the existence of any of a number of data-collection
        and reporting utilities on the system
        report compliance status as not compliant if any exist
        report compliance status as compliant if none exist


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

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

        self.pkgslist = ["popularity-contest", "apport", "ubuntu-report"]
        self.removepkgs = []

        try:

            for pkg in self.pkgslist:
                if self.ph.check(pkg):
                    self.compliant = False
                    self.detailedresults += "\nData collection utility: " + str(
                        pkg) + " is still installed"
                    self.removepkgs.append(pkg)

        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):
        '''Remove any data-collection and reporting utilities from the system
        report success status as True if all are removed
        report success status as False if any remain


        :returns: self.rulesuccess

        :rtype: bool
@author: Breen Malmberg

        '''

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

        try:

            if self.enabledCI.getcurrvalue():

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

                for pkg in self.removepkgs:

                    if not self.ph.remove(pkg):
                        self.detailedresults += "\nUnable to remove package: " + str(
                            pkg)
                        self.rulesuccess = False
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        comm = self.ph.getRemove() + pkg
                        event = {"eventtype": "commandstring", "command": comm}
                        self.statechglogger.recordchgevent(myid, event)
                        self.logger.log(LogPriority.DEBUG,
                                        "Removing package: " + str(pkg))

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

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

        return self.rulesuccess
Example #26
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
Example #27
0
class DisableGUILogon(Rule):

    def __init__(self, config, environ, logger, statechglogger):
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 105
        self.rulename = "DisableGUILogon"
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.applicable = {'type': 'white',
                           'family': ['linux']}

        # Configuration item instantiation
        datatype = "bool"
        key = "DISABLEX"
        instructions = "To enable this item, set the value of DISABLEX " + \
            "to True. When enabled, this rule will disable the automatic " + \
            "GUI login, and the system will instead boot to the console " + \
            "(runlevel 3). This will not remove any GUI components, and the " + \
            "GUI can still be started using the \"startx\" command."
        default = False
        self.ci1 = self.initCi(datatype, key, instructions, default)

        datatype = "bool"
        key = "LOCKDOWNX"
        instructions = "To enable this item, set the value of LOCKDOWNX " + \
            "to True. When enabled, this item will help secure X Windows by " + \
            "disabling the X Font Server (xfs) service and disabling X " + \
            "Window System Listening. This item should be enabled if X " + \
            "Windows is disabled but will be occasionally started via " + \
            "startx, unless there is a mission-critical need for xfs or " + \
            "a remote display."
        default = False
        self.ci2 = self.initCi(datatype, key, instructions, default)

        datatype = "bool"
        key = "REMOVEX"
        instructions = "To enable this item, set the value of REMOVEX " + \
            "to True. When enabled, this item will COMPLETELY remove X " + \
            "Windows from the system, and on most platforms will disable " + \
            "any currently running display manager. It is therefore " + \
            "recommended that this rule be run from a console session " + \
            "rather than from the GUI.\nREMOVEX cannot be undone."
        default = False
        self.ci3 = self.initCi(datatype, key, instructions, default)

        self.guidance = ["NSA 3.6.1.1", "NSA 3.6.1.2", "NSA 3.6.1.3",
                         "CCE 4462-8", "CCE 4422-2", "CCE 4448-7",
                         "CCE 4074-1"]
        self.iditerator = 0
        self.ph = Pkghelper(self.logger, self.environ)
        self.ch = CommandHelper(self.logger)
        self.sh = ServiceHelper(self.environ, self.logger)
        self.myos = self.environ.getostype().lower()
        self.sethelptext()

    def report(self):
        '''@author: Eric Ball

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

        '''
        try:
            compliant = True
            results = ""

            if os.path.exists("/bin/systemctl"):
                self.initver = "systemd"
                compliant, results = self.reportSystemd()
            elif re.search("debian", self.myos):
                self.initver = "debian"
                compliant, results = self.reportDebian()
            elif re.search("ubuntu", self.myos):
                self.initver = "ubuntu"
                compliant, results = self.reportUbuntu()
            else:
                self.initver = "inittab"
                compliant, results = self.reportInittab()

            # NSA guidance specifies disabling of X Font Server (xfs),
            # however, this guidance seems to be obsolete as of RHEL 6,
            # and does not apply to the Debian family.
            if self.sh.auditService("xfs", _="_"):
                compliant = False
                results += "xfs is currently enabled\n"

            xremoved = True
            self.xservSecure = True
            if re.search("debian|ubuntu", self.myos):
                if self.ph.check("xserver-xorg-core"):
                    compliant = False
                    xremoved = False
                    results += "Core X11 components are present\n"
            elif re.search("opensuse", self.myos):
                if self.ph.check("xorg-x11-server"):
                    compliant = False
                    xremoved = False
                    results += "Core X11 components are present\n"
            else:
                if self.ph.check("xorg-x11-server-Xorg"):
                    compliant = False
                    xremoved = False
                    results += "Core X11 components are present\n"
            # Removing X will take xserverrc with it. If X is not present, we
            # do not need to check for xserverrc
            if not xremoved:
                self.serverrc = "/etc/X11/xinit/xserverrc"
                self.xservSecure = False
                if os.path.exists(self.serverrc):
                    serverrcText = readFile(self.serverrc, self.logger)
                    if re.search("opensuse", self.myos):
                        for line in serverrcText:
                            reSearch = r'exec (/usr/bin/)?X \$dspnum.*\$args'
                            if re.search(reSearch, line):
                                self.xservSecure = True
                                break
                    else:
                        for line in serverrcText:
                            reSearch = r'^exec (/usr/bin/)?X (:0 )?-nolisten tcp ("$@"|.?\$@)'
                            if re.search(reSearch, line):
                                self.xservSecure = True
                                break
                    if not self.xservSecure:
                        compliant = False
                        results += self.serverrc + " does not contain proper " \
                            + "settings to disable X Window System " + \
                            "Listening/remote display\n"
                else:
                    compliant = False
                    results += self.serverrc + " does not exist; X Window " + \
                        "System Listening/remote display has not " + \
                        "been disabled\n"

            self.detailedresults = results
            self.compliant = compliant
        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 reportInittab(self):
        compliant = False
        results = ""
        inittab = "/etc/inittab"
        if os.path.exists(inittab):
            initData = readFile(inittab, self.logger)
            for line in initData:
                if line.strip() == "id:3:initdefault:":
                    compliant = True
                    break
        else:
            self.logger.log(LogPriority.ERROR, inittab + " not found, init " +
                            "system unknown")
        if not compliant:
            results = "inittab not set to runlevel 3; GUI logon is enabled\n"
        return compliant, results

    def reportSystemd(self):
        compliant = True
        results = ""
        cmd = ["/bin/systemctl", "get-default"]
        self.ch.executeCommand(cmd)
        defaultTarget = self.ch.getOutputString()
        if not re.search("multi-user.target", defaultTarget):
            compliant = False
            results = "systemd default target is not multi-user.target; " + \
                      "GUI logon is enabled\n"
        return compliant, results

    def reportDebian(self):
        compliant = True
        results = ""
        dmlist = ["gdm", "gdm3", "lightdm", "xdm", "kdm"]
        for dm in dmlist:
            if self.sh.auditService(dm, _="_"):
                compliant = False
                results = dm + \
                    " is still in init folders; GUI logon is enabled\n"
        return compliant, results

    def reportUbuntu(self):
        compliant = True
        results = ""
        ldmover = "/etc/init/lightdm.override"
        grub = "/etc/default/grub"
        if os.path.exists(ldmover):
            lightdmText = readFile(ldmover, self.logger)
            if not re.search("manual", lightdmText[0], re.IGNORECASE):
                compliant = False
                results += ldmover + ' exists, but does not contain text ' + \
                                    '"manual". GUI logon is still enabled\n'
        else:
            compliant = False
            results += ldmover + " does not exist; GUI logon is enabled\n"
        if os.path.exists(grub):
            tmppath = grub + ".tmp"
            data = {"GRUB_CMDLINE_LINUX_DEFAULT": '"quiet"'}
            editor = KVEditorStonix(self.statechglogger, self.logger, "conf",
                                    grub, tmppath, data, "present", "closedeq")
            if not editor.report():
                compliant = False
                results += grub + " does not contain the correct values: " + \
                    str(data)
        else:
            compliant = False
            results += "Cannot find file " + grub
        if not compliant:
            results += "/etc/init does not contain proper override file " + \
                      "for lightdm; GUI logon is enabled\n"
        return compliant, results

    def fix(self):
        '''@author: Eric Ball

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

        '''
        try:
            if not self.ci1.getcurrvalue() and not self.ci2.getcurrvalue() \
               and not self.ci3.getcurrvalue():
                return
            success = True
            self.detailedresults = ""
            # Delete past state change records from previous fix
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            # If we are doing DISABLEX or REMOVEX, we want to boot to
            # non-graphical multi-user mode.
            if self.ci1.getcurrvalue() or self.ci3.getcurrvalue():
                success &= self.fixBootMode()

            # Since LOCKDOWNX depends on having X installed, and REMOVEX
            # completely removes X from the system, LOCKDOWNX fix will only be
            # executed if REMOVEX is not.
            if self.ci3.getcurrvalue():
                success &= self.fixRemoveX()
            elif self.ci2.getcurrvalue():
                success &= self.fixLockdownX()

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

    def fixRemoveX(self):
        success = True
        # Due to automatic removal of dependent packages, the full
        # removal of X and related packages cannot be undone
        if re.search("opensuse", self.myos):
            cmd = ["zypper", "-n", "rm", "-u", "xorg-x11*", "kde*",
                   "xinit*"]
            self.ch.executeCommand(cmd)
        elif re.search("debian|ubuntu", self.myos):
            xpkgs = ["unity.*", "xserver-xorg-video-ati",
                     "xserver-xorg-input-synaptics",
                     "xserver-xorg-input-wacom", "xserver-xorg-core",
                     "xserver-xorg", "lightdm.*", "libx11-data"]
            for xpkg in xpkgs:
                self.ph.remove(xpkg)
        elif re.search("fedora", self.myos):
            # Fedora does not use the same group packages as other
            # RHEL-based OSs. Removing this package will remove the X
            # Windows system, just less efficiently than using a group
            self.ph.remove("xorg-x11-server-Xorg")
            self.ph.remove("xorg-x11-xinit*")
        else:
            cmd = ["yum", "groups", "mark", "convert"]
            self.ch.executeCommand(cmd)
            self.ph.remove("xorg-x11-xinit")
            self.ph.remove("xorg-x11-server-Xorg")
            cmd2 = ["yum", "groupremove", "-y", "X Window System"]
            if not self.ch.executeCommand(cmd2):
                success = False
                self.detailedresults += '"yum groupremove -y X Window System" command failed\n'
        return success

    def fixLockdownX(self):
        success = True
        if self.sh.disableService("xfs", _="_"):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype":   "servicehelper",
                     "servicename": "xfs",
                     "startstate":  "enabled",
                     "endstate":    "disabled"}
            self.statechglogger.recordchgevent(myid, event)
        else:
            success = False
            self.detailedresults += "STONIX was unable to disable the " + \
                "xfs service\n"

        if not self.xservSecure:
            serverrcString = "exec X :0 -nolisten tcp $@"
            if not os.path.exists(self.serverrc):
                createFile(self.serverrc, self.logger)
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation",
                         "filepath": self.serverrc}
                self.statechglogger.recordchgevent(myid, event)
                writeFile(self.serverrc, serverrcString, self.logger)
            else:
                open(self.serverrc, "a").write(serverrcString)
        return success
Example #28
0
class RemoveSUIDGames(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 244
        self.rulename = "RemoveSUIDGames"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.applicable = {'type': 'white', 'family': 'linux'}

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

        self.guidance = ["LANL 15.7"]
        self.iditerator = 0
        self.ph = Pkghelper(self.logger, self.environ)

        self.gamelist = [
            'atlantik', 'bomber', 'bovo', 'gnuchess', 'kapman', 'kasteroids',
            'katomic', 'kbackgammon', 'kbattleship', 'kblackbox', 'kblocks',
            'kbounce', 'kbreakout', 'kdiamond', 'kenolaba', 'kfouleggs',
            'kfourinline', 'kgoldrunner', 'killbots', 'kiriki', 'kjumpingcube',
            'klickety', 'klines', 'kmahjongg', 'kmines', 'knetwalk', 'kolf',
            'kollision', 'konquest', 'kpat', 'kpoker', 'kreversi', 'ksame',
            'kshisen', 'ksirk', 'ksirkskineditor', 'ksirtet', 'ksmiletris',
            'ksnake', 'kspaceduel', 'ksquares', 'ksudoku', 'ktron',
            'ktuberling', 'kubrick', 'kwin4', 'kwin4proc', 'lskat',
            'lskatproc', 'gnome-games', 'kdegames', 'gnome-taquin'
        ]

    def report(self):
        try:
            compliant = True
            self.gamesfound = []
            self.gamedirsfound = []

            for game in self.gamelist:
                if self.ph.check(game):
                    self.gamesfound.append(game)
                    compliant = False
                    self.detailedresults += game + " installed on system\n"
                if os.path.exists("/usr/bin/" + game):
                    self.gamedirsfound.append("/usr/bin/" + game)
                    compliant = False
                    self.detailedresults += "/usr/bin/" + game + "path exists\n"
            if (os.path.exists("/usr/games")):
                usrgames = os.listdir("/usr/games")
                if len(usrgames) > 0:
                    compliant = False
                    self.detailedresults += "/usr/games directory is not empty\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():
                return
            success = True
            debug = ""
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)

            for game in self.gamesfound:
                # pkgName = self.ph.getPackageFromFile(game)
                # if pkgName is not None and str(pkgName) is not "":
                if self.ph.remove(game):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {
                        "eventtype": "pkghelper",
                        "pkgname": game,
                        "startstate": "installed",
                        "endstate": "removed"
                    }
                    self.statechglogger.recordchgevent(myid, event)
                else:
                    success = False
                    self.detailedresults += "Unable to remove " + game + "\n"
            for game in self.gamedirsfound:
                #did not put state change event recording here due to python
                #not sending valid return code back for success or failure
                #with os.remove command therefore not knowing if successful
                #to record event
                if os.path.exists(game):
                    os.remove(game)
            if os.path.exists("/usr/games/"):
                if not self.__cleandir("/usr/games/"):
                    success = False
                    self.detailedresults += "Unable to remove all games from /usr/games\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

    def __cleandir(self, directory, depth=0):
        '''Recursively finds the package name for each file in a directory,
        and all child directories, and uninstalls the package. Ignores links.
        This is best-effort; no error checking is done for the uninstall
        requests.

        @param directory: Name of the directory to search
        @return: False if a package name is not found for any file, True
                 otherwise.
        @author: Eric Ball
        '''
        success = True
        dirlist = os.listdir(directory)
        for path in dirlist:
            path = directory + path
            if os.path.isfile(path):
                pkgName = self.ph.getPackageFromFile(path)
                if pkgName is not None:
                    if self.ph.remove(pkgName):
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "pkghelper",
                            "pkgname": pkgName,
                            "startstate": "installed",
                            "endstate": "removed"
                        }
                        self.statechglogger.recordchgevent(myid, event)
                    else:
                        debug = "Could not remove package " + pkgName
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                else:
                    debug = "Could not find package name for " + path
                    self.logger.log(LogPriority.DEBUG, debug)
                    success = False
            elif os.path.isdir(path):
                if depth < 6:
                    success &= self.__cleandir(path, depth + 1)
        dirlist = os.listdir(directory)
        if dirlist:
            # There is a possibility that removing some packages will result in
            # other packages being installed. We will attempt to remove these
            # additional packages, but limit this to avoid infinite loops.
            if depth < 6:
                success &= self.__cleandir(directory, depth + 1)
        return success
Example #29
0
class RemoveSoftware(Rule):
    """This class removes any unnecessary software installed on the system"""
    def __init__(self, config, environ, logger, statechglogger):
        """
        Constructor
        """

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

        # Configuration item instantiation
        datatype1 = "bool"
        key1 = "REMOVESOFTWARE"
        instructions1 = "To disable this rule set the value of REMOVESOFTWARE TO False."
        default1 = False
        self.ci = self.initCi(datatype1, key1, instructions1, default1)

        datatype2 = "list"
        key2 = "PACKAGES"
        instructions2 = "Enter the package(s) that you wish to remove.  By " + \
            "default we already list packages that we recommend for removal."
        default2 = [
            "squid", "telnet-server", "rsh-server", "rsh", "rsh-client",
            "talk", "talk-server", "talkd", "libpam-ccreds", "pam_ccreds",
            "tftp-server", "tftp", "tftpd", "udhcpd", "dhcpd", "dhcp",
            "dhcp-server", "yast2-dhcp-server", "vsftpd", "httpd", "dovecot",
            "dovecot-imapd", "dovecot-pop3d", "snmpd", "net-snmpd", "net-snmp",
            "ipsec-tools", "irda-utils", "slapd", "openldap-servers",
            "openldap2", "bind9", "bind9.i386", "bind", "dnsutils",
            "bind-utils", "redhat-access-insights", "insights-client"
        ]

        self.pkgci = self.initCi(datatype2, key2, instructions2, default2)

    def report(self):
        """
        report whether any of the packages listed in the self.pkgci ci are installed
        return False if any are installed
        return True if none of them are installed

        :returns: self.compliant
        :rtype: bool
        """

        self.detailedresults = ""
        self.compliant = True
        self.ph = Pkghelper(self.logger, self.environ)
        packages = self.pkgci.getcurrvalue()
        self.remove_packages = []

        try:

            if packages:
                for pkg in packages:
                    if self.ph.check(pkg):
                        self.remove_packages.append(pkg)
                        self.detailedresults += pkg + " is installed\n"
                        self.compliant = False

        except Exception:
            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 fix(self):
        """
        remove all software packages listed in the self.pkgci list

        :returns: self.rulesuccess
        :rtype: bool
        """

        self.rulesuccess = True
        self.detailedresults = ""
        self.iditerator = 0
        enabled = self.ci.getcurrvalue()

        try:

            if not enabled:
                self.formatDetailedResults("fix", self.rulesuccess,
                                           self.detailedresults)
                return self.rulesuccess
            elif not self.remove_packages:
                self.formatDetailedResults("fix", self.rulesuccess,
                                           self.detailedresults)
                return self.rulesuccess

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

            for pkg in self.remove_packages:
                try:
                    if self.ph.remove(pkg):
                        self.iditerator += 1
                        self.detailedresults += "\nRemoved package: " + str(
                            pkg)
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {
                            "eventtype": "pkghelper",
                            "pkgname": pkg,
                            "startstate": "installed",
                            "endstate": "removed"
                        }
                        self.statechglogger.recordchgevent(myid, event)
                    else:
                        self.rulesuccess = False
                        self.detailedresults += "\nFailed to remove package: " + str(
                            pkg)
                except Exception:
                    continue

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

        return self.rulesuccess
Example #30
0
class PasswordExpiration(Rule):

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

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

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

                self.shadowfile = "/etc/shadow"
                self.logdeffile = "/etc/login.defs"
                self.useraddfile = "/etc/default/useradd"
                self.libuserfile = "/etc/libuser.conf"
                self.compliant = self.reportLinux()
            elif self.environ.getosfamily() == "solaris":
                self.specs = {"PASSLENGTH": "8",
                              "MINWEEKS": "1",
                              "MAXWEEKS": "26",
                              "WARNWEEKS": "4"}
                self.shadowfile = "/etc/shadow"
                self.logdeffile = "/etc/default/passwd"
                self.compliant = self.reportSolaris()
            elif self.environ.getosfamily() == "freebsd":
                self.specs = {"warnpassword": "******",
                              "minpasswordlen": "8",
                              "passwordtime": "180d"}
                self.shadowfile = "/etc/master.passwd"
                self.loginfile = "/etc/login.conf"
                self.compliant = self.reportFreebsd()
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

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

    def reportLinux(self):
        compliant1 = self.checklogindefs()
        compliant2 = self.chkShadow()
        compliant3 = self.chkUserAdd()
        compliant4 = self.checklibuser()
        if compliant1 and compliant2 and compliant3 and compliant4:
            return True
        else:
            return False

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

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

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

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

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

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

        return compliant

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

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

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

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

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

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

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


        :returns: bool

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


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

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

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

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

            if self.environ.getosfamily() == "linux":
                self.rulesuccess = self.fixLinux()
            if self.environ.getosfamily() == "solaris":
                self.rulesuccess = self.fixSolaris()
            if self.environ.getosfamily() == "freebsd":
                self.rulesuccess = self.fixFreebsd()
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

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

    def fixLinux(self):
        success1 = self.fixLogDef(self.specs)
        success2 = self.fixShadow()
        success3 = self.fixUserAdd()
        success4 = self.setlibuser()
        if success1 and success2 and success3 and success4:
            return True
        else:
            return False

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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