예제 #1
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
예제 #2
0
class SHchkconfig(ServiceHelperTemplate):
    """
    SHchkconfig is the Service Helper for systems using the chkconfig command to
    configure services. (RHEL up to 6, SUSE, Centos up to 6, etc)

    @author: David Kennel
    """

    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHchkconfig, self).__init__(environment, logdispatcher)
        self.environ = environment
        self.logger = logdispatcher
        self.initobjs()
        self.localize()

    def initobjs(self):
        """
        initialize class objects

        @return:
        """

        self.ch = CommandHelper(self.logger)

    def localize(self):
        """
        set base command paths (chkconfig and service) based on OS

        @return:
        """

        self.svc = ""
        self.chk = ""

        chk_paths = ["/sbin/chkconfig", "/usr/sbin/chkconfig"]
        for cp in chk_paths:
            if os.path.exists(cp):
                self.chk = cp
                break
        service_paths = ["/sbin/service", "/usr/sbin/service"]
        for sp in service_paths:
            if os.path.exists(sp):
                self.svc = sp
                break

        if not self.svc:
            raise IOError("Could not locate the service utility on this system")
        if not self.chk:
            raise IOError("Could not locate the chkconfig utility on this system")

    def startService(self, service, **kwargs):
        """
        start a given service

        @param service: string; name of service
        @param kwargs: 
        @return: success
        @rtype: bool
        @author: Breen Malmberg
        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " start")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if not self.isRunning(service):
            success = False

        return success

    def stopService(self, service, **kwargs):
        """
        stop a given service

        @param service: 
        @param kwargs: 
        @return: success
        @rtype: bool
        @author: Breen Malmberg
        """

        success = True

        self.ch.executeCommand(self.svc + " " + service + " stop")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False

        if self.isRunning(service):
            success = False

        return success

    def disableService(self, service, **kwargs):
        """
        Disables the specified service and stops it if
        it is running

        @param service: string; Name of the service to be disabled
        @return: bool
        @author: David Kennel
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        disabled = True

        self.ch.executeCommand(self.chk + " " + service + " off")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            disabled = False

        if self.auditService(service):
            disabled = False

        if not self.stopService(service):
            disabled = False

        return disabled

    def enableService(self, service, **kwargs):
        """
        Enables a service and starts it if it is not running as long as we are
        not in install mode

        @param service: string; Name of the service to be enabled
        @return: enabled
        @rtype: bool
        @author: David Kennel
        @change: Breen Malmberg - 04/10/2019 - 
        """

        enabled = True

        self.ch.executeCommand(self.chk + " " + service + " on")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False

        if not self.auditService(service):
            enabled = False

        if not self.startService(service):
            enabled = False

        return enabled

    def auditService(self, service, **kwargs):
        """
        Checks the status of a service and returns a bool indicating whether or
        not the service is enabled

        @param service: string; Name of the service to audit
        @return: enabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        enabled = True

        if not self.audit_chkconfig_service(service):
            enabled = False

        return enabled

    def audit_chkconfig_service(self, service):
        """
        uses the chkconfig command to check if a given
        service is enabled or not

        @return: enabled
        @rtype: bool
        @author: Breen Malmberg
        """

        enabled = True

        self.ch.executeCommand(self.chk + " --list " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False
            self.logger.log(LogPriority.DEBUG, "Failed to get status of service: " + service)
            return enabled

        output = self.ch.getOutputString()
        if not re.search(":on", output):
            enabled = False

        return enabled

    def isRunning(self, service, **kwargs):
        """
        Check to see if a service is currently running.

        @param service: string; Name of the service to check
        @return: running
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        running = True
        # see: http://refspecs.linuxbase.org/LSB_3.1.0/LSB-generic/LSB-generic/iniscrptact.html
        success_codes = [0, 1, 2, 3]

        self.ch.executeCommand(self.svc + " " + service + " status")
        retcode = self.ch.getReturnCode()
        if retcode not in success_codes:
            running = False
            self.logger.log(LogPriority.DEBUG, "Command error while getting run status of service: " + service)
            return running

        outputlines = self.ch.getOutput()
        # need to parse for either sysv or systemd output
        if not self.parse_running(outputlines):
            running = False

        return running

    def parse_running(self, outputlines):
        """
        check whether given service is running, with the
        service command
        this is the older (classic) systemV case

        @param outputlines: list; list of strings to search
        @return: running
        @rtype: bool
        @author: Breen Malmberg
        """

        running = True
        systemctl_locations = ["/usr/bin/systemctl", "/bin/systemctl"]
        if any(os.path.exists(sl) for sl in systemctl_locations):
            searchterms = ["Active:\s+inactive", "Active:\s+unknown"]
        else:
            searchterms = ["is stopped", "hook is not installed", "is not running"]

        for line in outputlines:
            if any(re.search(st, line) for st in searchterms):
                running = False
                break

        return running

    def reloadService(self, service, **kwargs):
        """
        Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        @param service: string; Name of service to be reloaded
        @return: reloaded
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        reloaded = True

        # force-reload: cause the configuration to be reloaded if the service supports this,
        # otherwise restart the service if it is running
        self.ch.executeCommand(self.svc + " " + service + " force-reload")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            reloaded = False
            self.logger.log(LogPriority.DEBUG, "Failed to reload service: " + service)

        return reloaded

    def listServices(self, **kwargs):
        """
        Return a list containing strings that are service names.

        @return: service_list
        @rtype: list
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                logging edit
        """

        service_list = []

        self.ch.executeCommand(self.chk + " --list")
        outputlines = self.ch.getOutput()
        for line in outputlines:
            try:
                service_list.append(line.split()[0])
            except IndexError:
                pass

        return service_list

    def getStartCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - start command
        @author: dwalker
        '''
        return self.svc + " " + service + " start"

    def getStopCommand(self, service):
        '''
        retrieve the stop command.  Mostly used by event recording
        @return: string - stop command
        @author: dwalker
        '''
        return self.svc + " " + service + " stop"

    def getEnableCommand(self, service):
        '''
        retrieve the enable command.  Mostly used by event recording
        @return: string - enable command
        @author: dwalker
        '''
        return self.chk + " " + service + " on"

    def getDisableCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - disable command
        @author: dwalker
        '''
        return self.chk + " " + service + " off"
예제 #3
0
class SecureHomeDir(Rule):
    '''classdocs'''

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

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

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

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


        :returns: self.compliant

        :rtype: bool
@author: Derek Walker

        '''

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

        try:

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

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

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

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


        :returns: compliant

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

        '''

        compliant = True

        try:

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

                if homedirs:

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

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

        except Exception:
            raise

        return compliant

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

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

        '''

        groupwriteable = False

        try:

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

        except Exception:
            raise

        return groupwriteable

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

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

        '''

        worldreadable = False

        try:

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

        except Exception:
            raise

        return worldreadable

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


        :returns: homedirs

        :rtype: list
@author: Breen Malmberg

        '''

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

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

        try:

            self.cmdhelper.executeCommand(getAccountsList)

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

            AccountsList = self.cmdhelper.getOutput()

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

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

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

        except Exception:
            raise

        return HomeDirs

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


        :returns: homedirs

        :rtype: list
@author: Breen Malmberg

        '''

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

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

        try:

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

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

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

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

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

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

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

        except Exception:
            raise

        return HomeDirs

    def validateHomedirs(self, dirs):
        '''strip out common system (and non-existent) directories from the given list of dirs
        and return the resultant list

        :param dirs: list; list of strings containing directories to search
        and modify
        :returns: validateddirs
        :rtype: list

@author: Breen Malmberg

        '''

        validateddirs = []
        systemdirs = ['/tmp', '/var', '/temp', '/', '/bin', '/sbin', '/etc', '/dev', '/root']

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

        # if the base directory of a given path matches any of the above system directories, then we discard it
        for d in dirs:
            if os.path.exists(d):
                basepath = self.getBasePath(d)
                if basepath not in systemdirs:
                    validateddirs.append(d)
                else:
                    self.logger.log(LogPriority.DEBUG, "An account with a uid in the non-system range had a strange home directory: " + d)
                    self.logger.log(LogPriority.DEBUG, "Excluding this home directory from the list...")
            else:
                self.logger.log(LogPriority.DEBUG, "Home directory: " + d + " does not exist. Excluding it...")

        return validateddirs

    def getBasePath(self, path):
        '''get only the first (base) part of a given path

        :param path: string; full path to get base of
        :returns: basepath
        :rtype: string

@author: Breen Malmberg

        '''

        basepath = "/"

        # break path into list of characters
        pathchars = list(path)

        # remove the first '/' if it is there, to make
        # the list iteration easier
        if pathchars[0] == "/":
            del pathchars[0]

        # iterate over list of characters, adding all characters
        # before the next '/' path divider, to the basepath
        for c in pathchars:
            if c == "/":
                break
            else:
                basepath += c

        return basepath

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


        :returns: uid_min

        :rtype: string
@author: Breen Malmberg

        '''

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

        try:

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

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

        except Exception:
            raise

        return uid_min

    def reportLinux(self):
        '''check all user local home directories, on Linux platforms, for correct permissions


        :returns: compliant

        :rtype: bool

@author: Derek Walker
@change: Breen Malmberg - 06/28/2018 - re-wrote method

        '''

        compliant = True
        passwd = "/etc/passwd"

        try:

            if not os.path.exists(passwd):
                self.logger.log(LogPriority.DEBUG, "You have no passwd file! Cannot get lits of user home directories! Aborting.")
                compliant = False
                return compliant

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

                if homedirs:

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

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

        except Exception:
            raise

        return compliant

    def getMyHomeDir(self):
        '''return the home directory for the currently logged-in user


        :returns: HomeDir

        :rtype: string
@author: Breen Malmberg

        '''

        HomeDir = ""
        findHomeDir = "echo $HOME"
        uuid = self.environ.geteuid()

        try:

            # precautionary check
            if uuid <= 100:
                self.logger.log(LogPriority.DEBUG, "This method should only be run by non-system, user accounts!")
                return HomeDir

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

            HomeDir = self.cmdhelper.getOutputString()

            # backup method (if the $HOME env is not set for the current user)
            if not HomeDir:
                HomeDir = pwd.getpwuid(uuid).pw_dir

        except Exception:
            raise

        return HomeDir

    def fix(self):
        '''remove group-write and other-read permissions on all local user home directories


        :returns: self.rulesuccess

        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 10/13/2015 - will now fix /dev/null permissions when run;
                                        will no longer modify /var/empty or /dev/null

        '''

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

        try:

            if not self.ci.getcurrvalue():
                self.detailedresults += "\nRule was not enabled. Nothing was done."
                return self.rulesuccess

            if self.environ.geteuid() == 0:
                eventlist = self.statechglogger.findrulechanges(self.rulenumber)
                for event in eventlist:
                    self.statechglogger.deleteentry(event)

            if self.GWHomeDirs:
                for hd in self.GWHomeDirs:
                    self.logger.log(LogPriority.DEBUG, "Removing group-write permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod g-w " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False
                        self.detailedresults += "\nUnable to remove group write permission on directory: " + hd

            if self.WRHomeDirs:
                for hd in self.WRHomeDirs:
                    self.logger.log(LogPriority.DEBUG, "Removing world read permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod o-r " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False
                        self.detailedresults += "\nUnable to remove world read permission on directory: " + hd
                    self.logger.log(LogPriority.DEBUG, "Also ensuring no world write permission on directory: " + hd)
                    self.cmdhelper.executeCommand("/bin/chmod o-w " + hd)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                        self.rulesuccess = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
예제 #4
0
class DisableAFPFileSharing(RuleKVEditor):
    '''AFP & SMB start up and stop
    
    AFP Service
    Starting and Stopping AFP Service
    To start AFP service:
    $ sudo serveradmin start afp
    To stop AFP service:
    $ sudo serveradmin stop afp
    Checking AFP Service Status
    To see if AFP service is running:
    $ sudo serveradmin status afp
    To see complete AFP status:
    $ sudo serveradmin fullstatus afp
    Viewing AFP Settings
    To list all AFP service settings:
    $ sudo serveradmin settings afp
    To list a particular setting:
    $ sudo serveradmin settings afp:setting
    This method disables AFP file sharing on mac os x systems
    
    @author: Breen Malmberg


    '''

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

    def __init__(self, config, environ, logger, statechglogger):
        RuleKVEditor.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 164
        self.rulename = 'DisableAFPFileSharing'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.logger = logger
        self.rootrequired = True
        self.guidance = ['CIS 1.4.14.3']

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

        if self.environ.getostype() == "Mac OS X":
            self.addKVEditor(
                "DisableAFPFileSharing", "defaults",
                "/System/Library/LaunchDaemons/com.apple.AppleFileServer", "",
                {"Disabled": ["1", "-bool True"]}, "present", "",
                "Disable AFP File Sharing", None, False, {})

        self.initObjs()
        self.determineOrigAFPstatus()
        self.sethelptext()

    def initObjs(self):
        '''initialize any objects to be used by this class


        :returns: void
        @author: Breen Malmberg

        '''

        self.cmdhelper = CommandHelper(self.logger)

    def determineOrigAFPstatus(self):
        '''store the original operational state/status of
        Apple File Server as a bool


        '''

        # default init
        self.afporigstatus = False

        # if version = 10.10.*, then use KVEditor and ignore the other code
        if not re.search("10\.10.*", self.environ.getosver(), re.IGNORECASE):

            getafpstatuscmd = "launchctl list com.apple.AppleFileServer"
            self.cmdhelper.executeCommand(getafpstatuscmd)
            retcode = self.cmdhelper.getReturnCode()
            if retcode != 0:
                errstr = self.cmdhelper.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
            outputstr = self.cmdhelper.getOutputString()
            if not re.search("Could not find", outputstr, re.IGNORECASE):
                self.afporigstatus = True

    def fix(self):
        '''disable Apple File Sharing service


        :returns: success

        :rtype: bool
@author: Breen Malmberg

        '''

        success = True

        try:

            # if version = 10.10.*, then use KVEditor and ignore the other code
            if re.search("10\.10.*", self.environ.getosver(), re.IGNORECASE):
                RuleKVEditor.fix(self)
            else:

                clientfixpath1 = "/System/Library/LaunchDaemons/com.apple.AppleFileServer"
                clientfixtool = "launchctl"
                clientfixcmd1 = clientfixtool + " unload " + clientfixpath1

                # the below 'path' is actually an alias
                # which is understood by launchctl.
                # in mac terminology, this is called a 'target'
                clientfixpath2 = "system/com.apple.AppleFileServer"
                clientfixcmd2 = clientfixtool + " disable " + clientfixpath2

                self.cmdhelper.executeCommand(clientfixcmd1)
                retcode = self.cmdhelper.getReturnCode()
                if retcode != 0:
                    errstr = self.cmdhelper.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
                    success = False
                self.cmdhelper.executeCommand(clientfixcmd2)
                retcode = self.cmdhelper.getReturnCode()
                if retcode != 0:
                    errstr = self.cmdhelper.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errstr)
                    success = False

            if success:
                self.detailedresults += "\nApple File Server has successfully been disabled."

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

    def undo(self):
        '''restore Apple File Sharing service
        to its original state


        :returns: void
        @author: Breen Malmberg

        '''

        success = True

        try:

            # if version = 10.10.*, then use KVEditor and ignore the other code
            if re.search("10\.10.*", self.environ.getosver(), re.IGNORECASE):
                RuleKVEditor.undo(self)
            else:

                if not self.afporigstatus:
                    undocmd1 = "launchctl enable system/com.apple.AppleFileServer"
                    undocmd2 = "launchctl load /System/Library/LaunchDaemons/com.apple.AppleFileServer.plist"
                    self.cmdhelper.executeCommand(undocmd1)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        success = False
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)
                    self.cmdhelper.executeCommand(undocmd2)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode != 0:
                        success = False
                        errstr = self.cmdhelper.getErrorString()
                        self.logger.log(LogPriority.DEBUG, errstr)

                if not success:
                    self.detailedresults += "\nUndo failed to restore Apple File Sharing to its original state on this system."
                else:
                    self.detailedresults += "\nUndo has successfully restored Apple File Sharing to its original state on this system."

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults('undo', success, self.detailedresults)
        return success
예제 #5
0
class DisablePrelinking(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 89
        self.rulename = "DisablePrelinking"
        self.sethelptext()
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.applicable = {'type': 'white', 'family': ['linux']}

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

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

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

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

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

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

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

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

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

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

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

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

    """

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

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

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

    def report(self):
        """

        :return: 
        """

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

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

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

        try:

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

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

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

        return self.compliant

    def set_selinux_conf_path(self):
        """

        :return:
        """

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

    def reportSelinux(self):
        """

        :return:
        """

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

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

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

        self.set_selinux_conf_path()

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

        return compliant

    def reportApparmor(self):
        """

        :return:
        """

        compliant = True

        aa_enabled = "Yes"

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

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

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

        return compliant

    def fix(self):
        """

        :return: 
        """

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

        try:

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

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

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

        return self.rulesuccess

    def fixSelinux(self):
        """

        :return:
        """

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

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

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

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

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

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

        return success

    def fixApparmor(self):
        """

        :return:
        """

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

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

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

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

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

            os.rename(tmp_grub_file, grub_file)

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

        return success
예제 #7
0
class Dnf(object):

    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.  Specifically for Fedora
    :version:
    :author:Derek T Walker 08-13-2015'''
    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/bin/dnf install -y "
        self.remove = "/usr/bin/dnf remove -y "
        self.search = "/usr/bin/dnf search "
        self.rpm = "/bin/rpm -q "
###############################################################################

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

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.
        @param string package : Name of the package to be removed, must be
        recognizable to the underlying package manager.
        @return bool :
        @author'''
        try:
            removed = False
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                removed = True
                self.detailedresults += package + " pkg removed successfully\n"
            else:
                self.detailedresults += package + \
                    " pkg not able to be removed\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return removed
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.
        @param: string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: bool :
        @author: dwalker'''
        try:
            found = False
            self.ch.executeCommand(self.rpm + package)
            if self.ch.getReturnCode() == 0:
                found = True
                self.detailedresults += package + " pkg found\n"
            else:
                self.detailedresults += package + " pkg not found\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################

    def checkAvailable(self, package):
        try:
            found = False
            self.ch.executeCommand(self.search + package)
            output = self.ch.getOutputString()
            if re.search("no matches found", output.lower()):
                self.detailedresults += package + " pkg is not available " + \
                    " or may be misspelled\n"
            elif re.search("matched", output.lower()):
                self.detailedresults += package + " pkg is available\n"
                found = True
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################

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

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

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

    def getRemove(self):
        return self.remove
예제 #8
0
class PasswordExpiration(Rule):

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

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

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

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

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

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

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

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

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

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

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

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

        return compliant

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

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

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

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

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

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

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


        :returns: bool

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def fixLogin(self):
        success = True
        tempstring = ""
        debug = ""
        if not os.path.exists(self.loginfile):
            self.detailedresults = self.loginfile + "does not exist. \
Will not perform fix on useradd file\n"
            return False
        if not checkPerms(self.loginfile, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.loginfile, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                success = False
        contents = readFile(self.loginfile, self.logger)
        iterator1 = 0
        for line in contents:
            if re.search("^#", line) or re.match('^\s*$', line):
                iterator1 += 1
            elif re.search('^default:\\\\$', line.strip()):
                contents1 = contents[:iterator1 + 1]
                temp = contents[iterator1 + 1:]
                length2 = len(temp) - 1
                iterator2 = 0
                for line2 in temp:
                    if re.search('^[^:][^:]*:\\\\$', line2):
                        contents3 = temp[iterator2:]
                        contents2 = temp[:iterator2]
                        break
                    elif iterator2 < length2:
                        iterator2 += 1
                    elif iterator2 == length2:
                        contents2 = temp[:iterator2]
                break
            else:
                iterator1 += 1
        if contents2:
            for key in self.Fspecs:
                iterator = 0
                found = False
                for line in contents2:
                    if re.search("^#", line) or re.match('^\s*$', line):
                        iterator += 1
                        continue
                    elif re.search('^:' + key, line.strip()):
                        if re.search('=', line):
                            temp = line.split('=')
                            if re.search(str(self.Fspecs[key]) +
                                         '(:\\\\|:|\\\\|\s)',
                                         temp[1]):
                                iterator += 1
                                found = True
                            else:
                                contents2.pop(iterator)
                    else:
                        iterator += 1
                if not found:
                    contents2.append('\t' + key + '=' + str(self.Fspecs[key]) +
                                     ':\\\\\n')
        final = []
        for line in contents1:
            final.append(line)
        for line in contents2:
            final.append(line)
        for line in contents3:
            final.append(line)
        for line in final:
            tempstring += line
        debug += "tempstring to be written to: " + self.loginfile + "\n"
        self.logger.log(LogPriority.DEBUG, debug)
        tmpfile = self.loginfile + ".tmp"
        if writeFile(tmpfile, tempstring, self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {'eventtype': 'conf',
                     'filepath': self.loginfile}
            self.statechglogger.recordchgevent(myid, event)
            self.statechglogger.recordfilechange(self.loginfile, tmpfile, myid)
            shutil.move(tmpfile, self.loginfile)
            os.chown(self.loginfile, 0, 0)
            os.chmod(self.loginfile, 0o644)
            resetsecon(self.loginfile)
        else:
            success = False
        return success
예제 #9
0
파일: zypper.py 프로젝트: CSD-Public/stonix
class Zypper(object):
    '''
    The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

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

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

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

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

        installed = True
        maxtries = 12
        trynum = 0

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

        try:

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

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

        except Exception:
            raise
        return installed

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

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

        removed = True
        maxtries = 12
        trynum = 0

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

        try:

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

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

        except Exception:
            raise
        return removed

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

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

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

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

        try:

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

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

        except Exception:
            raise
        return installed

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

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

        available = True
        maxtries = 12
        trynum = 0

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

        try:

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

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

        except Exception:
            raise
        return available

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

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

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

        updatesavail = True

        try:

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

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

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

        except Exception:
            raise
        return updatesavail

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

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

        updated = True

        try:

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

        except Exception:
            raise
        return updated

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

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

        packagename = ""

        try:

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

        except Exception:
            raise

        return packagename

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

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

        return self.install

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

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

        return self.remove
예제 #10
0
class DisableUncommonProtocols(Rule):
    def __init__(self, config, enviro, logger, statechglogger):
        Rule.__init__(self, config, enviro, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 132
        self.rulename = "DisableUncommonProtocols"
        self.formatDetailedResults("initialize")
        self.mandatory = False
        self.applicable = {'type': 'white', "family": ["linux"]}

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

        datatype = "list"
        key = "PROTOCOLS"
        instructions = "List all network protocols to disable"
        default = ["dccp", "sctp", "rds", "tipc"]
        self.ci2 = self.initCi(datatype, key, instructions, default)

        self.guidance = [
            "NSA 2.5.7", "CIS 4.6", "CCE-26448-1", "CCE-26410-1",
            "CCE-26239-4", "CCE-26696-5", "CCE-26828-4", "CCE-27106-4"
        ]
        self.iditerator = 0
        self.ch = CommandHelper(self.logger)
        self.sethelptext()

    def report(self):
        try:
            protocols = self.ci2.getcurrvalue()
            self.compliant = True
            self.detailedresults = ""
            mpdir = "/etc/modprobe.d/"

            for proto in protocols:
                if type(proto) is bytes:
                    proto = proto.decode('utf-8')
                cmd = ["grep", "-R", proto, mpdir]
                self.ch.executeCommand(cmd)
                if not re.search(":install " + proto + " /bin/true",
                                 self.ch.getOutputString()):
                    self.compliant = False
                    self.detailedresults += proto + " is not disabled\n"
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

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

            success = True
            self.detailedresults = ""
            protocols = self.ci2.getcurrvalue()
            mpdir = "/etc/modprobe.d/"
            protoconf = mpdir + "stonix-protocols.conf"

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

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

            for proto in protocols:
                cmd = ["grep", "-R", proto, mpdir]
                self.ch.executeCommand(cmd)
                if not re.search(":install " + proto + " /bin/true",
                                 self.ch.getOutputString()):
                    open(protoconf,
                         "a").write("install " + proto + " /bin/true\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
class DisableSIRIandContinuityFeatures(Rule):
    def __init__(self, config, environ, logdispatch, statechglogger):
        '''Constructor'''
        Rule.__init__(self, config, environ, logdispatch, statechglogger)

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

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

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

    def fix(self):
        try:
            if not self.ci.getcurrvalue():
                return
            success = True
            self.detailedresults = ""
            self.iditerator = 0
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            if not self.defaults1:
                cmd = ["/usr/bin/defaults", "write", self.home + self.siripath1,
                       self.siriparams1, "-bool", "yes"]
                if self.ch.executeCommand(cmd):
                    if self.ch.getReturnCode() != 0:
                        success = False
                    else:
                        undocmd = ["/usr/bin/defaults", "write", self.home + \
                                   self.siripath1, self.siriparams1, self.undo1]
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "comm",
                                 "command": undocmd}
                        self.statechglogger.recordchgevent(myid, event)
            if not self.defaults2:
                cmd = "/usr/bin/defaults write " + self.home + \
                self.siripath2 + " " + self.siriparams2 + " -bool no"
                if self.ch.executeCommand(cmd):
                    if self.ch.getReturnCode() != 0:
                        success = False
                    else:
                        undocmd = "/usr/bin/defaults write " + self.home + \
                            self.siripath2 + " " + self.siriparams2 + \
                            " " + self.undo2
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "commandstring",
                                 "command": undocmd}
                        self.statechglogger.recordchgevent(myid, event)
            if not self.profilecomp:
                if os.path.exists(self.profile):
                    cmd = ["/usr/bin/profiles", "-I", "-F", self.profile]
                    if not self.ch.executeCommand(cmd):
                        success = False
                    else:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        cmd = ["/usr/bin/profiles", "-R", "-p", self.identifier]
                        event = {"eventtype": "comm",
                                 "command": cmd}
                        self.statechglogger.recordchgevent(myid, event)
                else:
                    success = False
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess
예제 #12
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
예제 #13
0
class SetSSCorners(Rule):
    '''classdocs'''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 188
        self.rulename = 'SetSSCorners'
        self.compliant = True
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = False
        self.sethelptext()
        self.guidance = ['CIS', '1.4.8.1', '1.4.8.2']
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'noroot': True
        }

        # set up configuration items for this rule
        datatype = 'bool'
        key = 'SETSSCORNERS'
        instructions = 'To disable this rule, set the value of ' + \
            'SetSSCorners to False'
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)

    def setVars(self):
        ''' '''

        ssfound = False

        try:

            self.homedir = self.environ.geteuidhome()
            self.conffile = self.homedir + \
                '/Library/Preferences/com.apple.dock.plist'
            self.readcmd = '/usr/bin/defaults read ' + '\"' + \
                self.conffile + '\"'
            self.optlist = [
                "wvous-bl-corner", "wvous-br-corner", "wvous-tl-corner",
                "wvous-tr-corner"
            ]
            self.optdict = {}
            self.writecmd = '/usr/bin/defaults write ' + '\"' + \
                self.conffile + '\"'
            self.detailedresults = ""
            self.cmdhelper = CommandHelper(self.logger)
            self.compliant = True
            self.moddict = {}
            for opt in self.optlist:
                self.cmdhelper.executeCommand(self.readcmd + ' ' + opt)
                errout = self.cmdhelper.getErrorString()
                output = self.cmdhelper.getOutputString()
                if not re.search('^6', output) and not errout:
                    self.optdict[opt] = output
                if re.search('^5', output):
                    ssfound = True

            for opt in self.optlist:
                if opt not in self.optdict:
                    self.optdict[opt] = 1
            if not ssfound:
                self.optdict["wvous-tl-corner"] = 5
            for opt in self.optdict:
                if self.optdict[opt] == 6:
                    self.optdict[opt] = 1

        except Exception:
            raise

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

        found = False
        self.detailedresults = ""

        try:

            if self.environ.geteuid() == 0:
                self.detailedresults += '\nYou are running SetSSCorners ' + \
                    'in Admin mode. This rule must be run in regular ' + \
                    'user context.'
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False

            self.setVars()

            if os.path.exists(self.conffile):
                for item in self.optdict:
                    self.cmdhelper.executeCommand(self.readcmd + ' ' + item)
                    output = self.cmdhelper.getOutputString()
                    errout = self.cmdhelper.getErrorString()
                    sitem = item.split('-')
                    location = str(sitem[1])
                    if errout:
                        self.compliant = False
                        self.detailedresults += '\nSpecified key not found : ' \
                            + str(item)
                    elif re.search('^6', output):
                        self.compliant = False
                        self.detailedresults += '\nIncorrect configuration ' + \
                            'value for key: ' + str(item)
                        self.moddict['wvous-' + location +
                                     '-modifier'] = 1048576
                    elif re.search('^5', output):
                        found = True
                        self.moddict['wvous-' + location + '-modifier'] = 0
                if not found:
                    self.compliant = False
                    self.detailedresults += '\nNo corner is configured to ' + \
                        'activate screen saver'

            else:
                self.compliant = False
                self.detailedresults += '\nRequired configuration file ' + \
                    'com.apple.dock.plist could not be found'

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

        success = True
        self.detailedresults = ""

        try:

            if self.environ.geteuid() == 0:
                self.detailedresults += '\nYou are running SetSSCorners ' + \
                    'in Admin mode. This rule must be run in regular ' + \
                    'user context.'
                self.logger.log(LogPriority.INFO, self.detailedresults)
                return False

            if self.ci.getcurrvalue():

                if os.path.exists(self.conffile):
                    for item in self.optdict:
                        cmd = self.writecmd + ' ' + item + ' -int ' + \
                            str(self.optdict[item])
                        self.cmdhelper.executeCommand(cmd)
                        errout = self.cmdhelper.getErrorString()

                        if errout:
                            success = False
                            self.detailedresults += '\nUnable to execute ' + \
                                'command ' + str(cmd)
                if self.moddict:
                    for item in self.moddict:
                        cmd = self.writecmd + ' ' + item + ' -int ' + \
                            str(self.moddict[item])
                        self.cmdhelper.executeCommand(cmd)
                        errout = self.cmdhelper.getErrorString()
                        if errout:
                            success = False

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults += 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
예제 #14
0
class BootSecurity(Rule):
    """The Boot Security rule configures the system to run a job at system boot
    time that handles turning off potential vulnerability points such as: wifi,
    bluetooth, microphones, and cameras.

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

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

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

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

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

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

        self.set_paths()

    def set_paths(self):
        """

        """

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

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

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

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

        compliant = True

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

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

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

        try:

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

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

        except:
            raise

        return compliant

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

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

        compliant = True

        try:

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

        except:
            raise

        return compliant

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

        :return:
        """

        compliant = True

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

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

        try:

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

        except:
            raise

        return compliant

    def report_boot_fips(self):
        """

        :return:
        """

        found_fips = False
        compliant = True

        try:

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

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

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

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

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

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

        except:
            raise

        return compliant

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

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

        self.detailedresults = ""
        self.compliant = True

        try:

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

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

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

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

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

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

        return self.compliant

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

        """

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

        try:

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

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

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

        except:
            raise

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

        """

        success = True

        try:

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

        except:
            raise

        return success

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

        """

        success = True

        try:

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

        except:
            raise

        return success

    def fix_boot_fips(self):
        """

        :return:
        """

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

        try:

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

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

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

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

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

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

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

                self.ch.executeCommand(self.grub_updater_cmd)

        except:
            raise

        return success

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

        :return:
        """

        success = True

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

        tmpfile = self.grub_file + ".stonixtmp"

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

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

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

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

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

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

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

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

        return success

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

        :return: luks_encrypted
        :rtype: bool
        """

        luks_encrypted = False

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

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

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

        try:

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

        except:
            raise

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

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

        return luks_encrypted

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

        :return:
        """

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

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

        try:

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

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

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

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

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

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

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

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

        except:
            raise

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

        :return: success
        :rtype: bool
        """

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

        try:

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

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

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

        except:
            raise

        return success

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

        """

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

        try:

            if self.bootci.getcurrvalue():

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

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

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

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

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

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

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

        return self.rulesuccess
예제 #15
0
class AptGet(object):
    '''Linux specific package manager for distributions that use the apt-get
    command to install packages.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def getRemove(self):
        return self.remove
예제 #16
0
class ConfigureLinuxFirewall(Rule):
    '''The configureLinuxFirewall class attempts to audit and configure firewalls
    for Linux OS based systems. Note: there is tremendous variations in the
    approach taken by the various distributions on how to manage firewalls,
    this code should work effectively for debian, ubuntu, RHEL and close
    derivatives. Note: unlike many other rules this behaves as a binary state
    manager, the undo will set the system back to an as new state with no
    firewalls.


    '''

    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 92
        self.rulename = 'ConfigureLinuxFirewall'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.applicable = {'type': 'white',
                           'family': ['linux']}
        self.servicehelper = ServiceHelper(self.environ, self.logger)
        self.serviceTarget = ""
        self.cmdhelper = CommandHelper(self.logger)
        self.guidance = ['NIST 800-53 AC-4', 'DISA RHEL 7 STIG 2.5.7.1',
                         'DISA RHEL 7 STIG 2.5.7.1.1',
                         'DISA RHEL 7 STIG 2.5.8.1.1',
                         'DISA RHEL 7 STIG 2.5.8.1.2',
                         'DISA RHEL 7 STIG 2.5.8.1.3',
                         'DISA RHEL 7 STIG 2.5.8.2.1',
                         'DISA RHEL 7 STIG 2.5.8.2.2',
                         'DISA RHEL 7 STIG 2.5.8.2.3',
                         'DISA RHEL 7 STIG 2.5.8.2.4']
        datatype = 'bool'
        key = 'CONFIGURELINUXFIREWALL'
        instructions = '''To disable this rule set the value of \
CONFIGURELINUXFIREWALL to False.'''
        default = False
        self.clfci = self.initCi(datatype, key, instructions, default)
        self.scriptType = ""
        self.iptScriptPath = ""
        self.iptables, self.ip6tables, self.iprestore, self.ip6restore = "", "", "", ""
        self.checkIptables()
        self.iditerator = 0

    def report(self):
        '''Report on whether the firewall meets baseline expectations.


        :returns: bool
        @author: D.Kennel
        @change: dwalker - updating rule to check for every possible firewall
            implementation and configure it rather than mutually exclusively
            checking based on system.

        '''
        try:
            compliant = True
            iptablesrunning = False
            ip6tablesrunning = False
            iptablesenabled = False
            ip6tablesenabled = False
            catchall = False
            catchall6 = False
            self.detailedresults = ""
            scriptExists = ""

            if self.checkFirewalld():
                if not self.servicehelper.auditService('firewalld.service', serviceTarget=self.serviceTarget):
                    compliant = False
                    self.detailedresults = 'This system appears to have firewalld but it is not running as required'

            elif self.checkUFW():
                cmdufw = '/usr/sbin/ufw status'
                if not self.cmdhelper.executeCommand(cmdufw):
                    self.detailedresults += "Unable to run " + \
                        "ufw status command\n"
                    compliant = False
                else:
                    outputufw = self.cmdhelper.getOutputString()
                    if re.search('Status: inactive', outputufw):
                        compliant = False
                        self.detailedresults += 'This system appears to have ' + \
                            'ufw but it is not running as required'
                    elif re.search('Status: active', outputufw):
                        cmdufw = "/usr/sbin/ufw status verbose"
                        if not self.cmdhelper.executeCommand(cmdufw):
                            compliant = False
                            self.detailedresults += "Cannot retrieve firewall rules\n"
                        else:
                            outputufw = self.cmdhelper.getOutputString()
                            if not re.search("Default\:\ deny\ \(incoming\)", outputufw):
                                compliant = False
                                self.detailedresults += "The default value for " + \
                                    "incoming unspecified packets is not deny\n"
            else:
                if os.path.exists("/etc/network/if-pre-up.d"):
                    self.iptScriptPath = "/etc/network/if-pre-up.d/iptables"
                    self.scriptType = "debian"
                elif os.path.exists("/etc/sysconfig/scripts"):
                    self.iptScriptPath = "/etc/sysconfig/scripts/SuSEfirewall2-custom"
                    self.scriptType = "suse"
                else:
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "No acceptable path for a startup " +
                                     "script found"])
                if self.iptScriptPath:
                    if os.path.exists(self.iptScriptPath):
                        scriptExists = True
                    else:
                        scriptExists = False
                else:
                    scriptExists = True

                if self.scriptType != "debian":
                    self.servicehelper.stopService('iptables')
                    self.servicehelper.startService('iptables')
                    self.servicehelper.stopService('ip6tables')
                    self.servicehelper.startService('ip6tables')
                    if self.servicehelper.isRunning('iptables'):
                        iptablesrunning = True
                    if self.servicehelper.isRunning('ip6tables'):
                        ip6tablesrunning = True
                    if self.servicehelper.auditService('iptables'):
                        iptablesenabled = True
                    if self.servicehelper.auditService('ip6tables'):
                        ip6tablesenabled = True
                else:
                    iptablesrunning = True
                    ip6tablesrunning = True
                    iptablesenabled = True
                    ip6tablesenabled = True

                if self.iptables:
                    cmd = [self.iptables, "-L"]
                    if not self.cmdhelper.executeCommand(cmd):
                        self.detailedresults += "Unable to run " + \
                            "iptables -L command\n"
                        compliant = False
                    else:
                        output = self.cmdhelper.getOutput()
                        for line in output:
                            if re.search('Chain INPUT \(policy REJECT\)|REJECT' +
                                         '\s+all\s+--\s+anywhere\s+anywhere', line):
                                catchall = True
                                break

                # check to see if the kernel was compiled with ipv6 support first
                lsmod_paths = ["/sbin/lsmod", "/usr/sbin/lsmod"]
                lsmod_path = ""
                for p in lsmod_paths:
                    if os.path.exists(p):
                        lsmod_path = p
                if lsmod_path:
                    check_ipv6_kernel = "/sbin/lsmod | grep -w 'ipv6'"
                    self.cmdhelper.executeCommand(check_ipv6_kernel)
                    retcode = self.cmdhelper.getReturnCode()
                    if retcode == 0:
                        # if system has kernel support for ipv6, then check for ip6tables bin, then do ip6tables rules check
                        if self.ip6tables:
                            cmd6 = [self.ip6tables, "-L"]
                            if not self.cmdhelper.executeCommand(cmd6):
                                self.detailedresults += "Unable to run " + \
                                    "ip6tables -L command\n"
                                compliant = False
                            else:
                                output6 = self.cmdhelper.getOutput()
                                for line in output6:
                                    if re.search('Chain INPUT \(policy REJECT\)|REJECT' +
                                                 '\s+all\s+anywhere\s+anywhere', line):
                                        catchall6 = True
                                        break
                        if not catchall6:
                            compliant = False
                            self.detailedresults += 'This system appears to use ' + \
                                                    'ip6tables but does not contain the expected rules. ' + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            ['ConfigureLinuxFirewall.report',
                                             "Missing v6 deny all."])
                        if not ip6tablesenabled:
                            compliant = False
                            self.detailedresults += "ip6tables not enabled " + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                        if not ip6tablesrunning:
                            compliant = False
                            self.detailedresults += 'This system appears to use ' + \
                                                    'ip6tables but it is not running as required. ' + \
                                                    'If the DisableIPV6 rule was run before this rule, this is ' + \
                                                    'acceptable behavior.\n'
                            self.logger.log(LogPriority.DEBUG,
                                            ['ConfigureLinuxFirewall.report',
                                             "RHEL 6 type system. IP6tables not running."])
                    else:
                        self.logger.log(LogPriority.DEBUG, "This system's kernel was compiled without ipv6 support. Skipping ip6tables checks...")
                        self.detailedresults += "\nThis system does not have support for ipv6. Skipping ip6tables checks..."

                else:
                    self.logger.log(LogPriority.DEBUG, "Unable to detect lsmod utility - which is required to perform a necessary pre-check before attempting to run ip6tables commands.")
                    self.detailedresults += "\nUnable to determine if this system has support for ipv6. Skipping ip6tables checks..."

                if not catchall:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but does not contain the expected rules\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "Missing v4 deny all."])

                if not iptablesrunning:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but it is not running as required.\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "RHEL 6 type system. IPtables not running."])
                if not iptablesenabled:
                    compliant = False
                    self.detailedresults += "iptables not enabled\n"

                if not scriptExists:
                    compliant = False
                    self.detailedresults += 'This system appears to use ' + \
                        'iptables but the startup script is not present\n'
                    self.logger.log(LogPriority.DEBUG,
                                    ['ConfigureLinuxFirewall.report',
                                     "Missing startup script"])
            self.compliant = compliant
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

    def fix(self):
        '''Enable the firewall services and establish basic rules if needed.

        @author: D. Kennel


        '''
        try:
            if not self.clfci.getcurrvalue():
                return
            self.iditerator = 0
            self.detailedresults = ""
            success = True
            # delete past state change records from previous fix
            eventlist = self.statechglogger.findrulechanges(self.rulenumber)
            for event in eventlist:
                self.statechglogger.deleteentry(event)
            #firewall-cmd portion
            if self.checkFirewalld():
                if self.servicehelper.enableService('firewalld.service', serviceTarget=self.serviceTarget):
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    cmd = "/usr/bin/systemctl disable firewalld.service"
                    event = {"eventtype": "commandstring",
                             "command": cmd}
                    self.statechglogger.recordchgevent(myid, event)
                    self.detailedresults += "Firewall configured.\n "
                else:
                    success = False
                    self.detailedresults += "Unable to enable firewall\n"
                    debug = "Unable to enable firewall\n"
                    self.logger.log(LogPriority.DEBUG, debug)

            #ufw command portion
            elif self.checkUFW():
                self.logger.log(LogPriority.DEBUG, "System uses ufw. Running ufw commands...")
                cmdufw = '/usr/sbin/ufw status'
                if not self.cmdhelper.executeCommand(cmdufw):
                    self.detailedresults += "Unable to run " + \
                        "ufw status command\n"
                    success = False
                else:
                    outputufw = self.cmdhelper.getOutputString()
                    if re.search('Status: inactive', outputufw):
                        ufwcmd = '/usr/sbin/ufw --force enable'
                        if not self.cmdhelper.executeCommand(ufwcmd):
                            self.detailedresults += "Unable to run " + \
                                "ufw enable command\n"
                            success = False
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            undocmd = "/usr/sbin/ufw --force disable"
                            event = {"eventtype": "commandstring",
                                     "command": undocmd}
                            self.statechglogger.recordchgevent(myid, event)
                            cmdufw = "/usr/sbin/ufw status verbose"
                            if not self.cmdhelper.executeCommand(cmdufw):
                                self.detailedresults += "Unable to retrieve firewall rules\n"
                                success = False
                            else:
                                outputfw = self.cmdhelper.getOutputString()
                                if not re.search("Default\:\ deny\ \(incoming\)", outputfw):
                                    ufwcmd = "/usr/sbin/ufw default deny incoming"
                                    if not self.cmdhelper.executeCommand(ufwcmd):
                                        self.detailedresults += "Unable to set default " + \
                                            "rule for incoming unspecified packets\n"
                                        success = False
                                    else:
                                        self.iditerator += 1
                                        myid = iterate(self.iditerator, self.rulenumber)
                                        undocmd = "/usr/sbin/ufw default allow incoming"
                                        event = {"eventtype": "commandstring",
                                                 "command": undocmd}
                                        self.statechglogger.recordchgevent(myid, event)
                    elif re.search('Status: active', outputufw):
                        cmdufw = "/usr/sbin/ufw status verbose"
                        if not self.cmdhelper.executeCommand(cmdufw):
                            self.detailedresults += "Cannot retrieve firewall rules\n"
                            success = False
                        else:
                            outputufw = self.cmdhelper.getOutputString()
                            if not re.search("Default\:\ deny\ \(incoming\)", outputufw):
                                ufwcmd = "/usr/sbin/ufw default deny incoming"
                                if not self.cmdhelper.executeCommand(ufwcmd):
                                    self.detailedresults += "Unable to set default " + \
                                        "rule for incoming unspecified packets\n"
                                    success = False
                                else:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    undocmd = "/usr/sbin/ufw default allow incoming"
                                    event = {"eventtype": "commandstring",
                                             "command": undocmd}
                                    self.statechglogger.recordchgevent(myid, event)
            else:
                #following portion is mainly for debian and opensuse systems only

                if os.path.exists("/etc/network/if-pre-up.d"):
                    self.iptScriptPath = "/etc/network/if-pre-up.d/iptables"
                    self.scriptType = "debian"
                    servicename = "networking"
                elif os.path.exists("/etc/sysconfig/scripts"):
                    self.iptScriptPath = "/etc/sysconfig/scripts/SuSEfirewall2-custom"
                    self.scriptType = "suse"
                    servicename = "network"
                #this script will ensure that iptables gets configured
                #each time the network restarts
                iptables = self.getScriptValues("iptables")
                ip6tables = self.getScriptValues("ip6tables")
                iptScript = ""
                created = False
                if self.iptScriptPath:
                    if self.scriptType == "debian":
                        if self.iprestore and self.ip6restore:
                            iptScript = '#!/bin/bash\n' + self.iprestore + \
                                        ' <<< "' + iptables + '"\n' + self.ip6restore + \
                                        ' <<< "' + ip6tables + '"'
                    else:
                        iptScript = self.getScriptValues("iptscript")
                    if iptScript:
                        if not os.path.exists(self.iptScriptPath):
                            if not createFile(self.iptScriptPath, self.logger):
                                success = False
                                self.detailedresults += "Unable to create file " + self.iptScriptPath + "\n"
                            else:
                                created = True
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                event = {"eventtype": "creation",
                                         "filepath": self.iptScriptPath}
                                self.statechglogger.recordchgevent(myid, event)
                        if os.path.exists(self.iptScriptPath):
                            if not checkPerms(self.iptScriptPath, [0, 0, 0o755], self.logger):
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    if not setPerms(self.iptScriptPath, [0, 0, 0o755], self.logger, self.statechglogger, myid):
                                        success = False
                                        self.detailedresults += "Unable to set permissions on " + self.iptScriptPath + "\n"
                            contents = readFile(self.iptScriptPath, self.logger)
                            if contents != iptScript:
                                tempfile = self.iptScriptPath + ".tmp"
                                if not writeFile(tempfile, iptScript, self.logger):
                                    success = False
                                    self.detailedresults += "Unable to write contents to " + self.iptScriptPath + "\n"
                                else:
                                    if not created:
                                        self.iditerator += 1
                                        myid = iterate(self.iditerator, self.rulenumber)
                                        event = {"eventtype": "conf",
                                                 "filepath": self.iptScriptPath}
                                        self.statechglogger.recordchgevent(myid, event)
                                        self.statechglogger.recordfilechange(self.iptScriptPath, tempfile, myid)
                                    os.rename(tempfile, self.iptScriptPath)
                                    os.chown(self.iptScriptPath, 0, 0)
                                    os.chmod(self.iptScriptPath, 0o755)
                                    resetsecon(self.iptScriptPath)

                            stonixfilepath = "/var/db/stonix/"
                            savecmd = "/sbin/iptables-save > " + stonixfilepath + "user-firewall-pre-stonix"
                            if not self.cmdhelper.executeCommand(savecmd):
                                success = False
                                self.detailedresults += "Unable to save current ipv4 " + \
                                                        "firewall rules for revert\n"
                                debug = "Unable to save current ipv4 " + \
                                        "firewall rules for revert\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            save6cmd = "/sbin/ip6tables-save > " + stonixfilepath + "user-firewall6-pre-stonix"
                            if not self.cmdhelper.executeCommand(save6cmd):
                                success = False
                                self.detailedresults += "Unable to save current ipv6 " + \
                                                        "firewall rules for revert\n"
                                debug = "Unable to save current ipv6 " + \
                                        "firewall rules for revert\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            self.servicehelper.stopService(servicename)

                            if not self.servicehelper.startService(servicename):
                                success = False
                                self.detailedresults += "Unable to restart networking\n"
                                debug = "Unable to restart networking\n"
                                self.logger.log(LogPriority.DEBUG, debug)
                            else:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                cmd = "/sbin/iptables-restore < " + stonixfilepath + "user-firewall-pre-stonix"
                                event = {"eventtype": "commandstring",
                                         "command": cmd}
                                self.statechglogger.recordchgevent(myid, event)
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                cmd = "/sbin/ip6tables-restore < " + stonixfilepath + "user-firewall6-pre-stonix"
                                event = {"eventtype": "commandstring",
                                         "command": cmd}
                                self.statechglogger.recordchgevent(myid, event)

                    else:
                        success = False
                        self.detailedresults += "There is no iptables startup script\n"
                        debug = "There is no iptables startup script\n"
                        self.logger.log(LogPriority.DEBUG, debug)

                #this portion mostly applies to RHEL6 and Centos6
                if os.path.exists('/usr/bin/system-config-firewall') or \
                        os.path.exists('/usr/bin/system-config-firewall-tui'):
                    systemconfigfirewall = self.getScriptValues("systemconfigfirewall")
                    sysconfigiptables = self.getScriptValues("sysconfigiptables")
                    sysconfigip6tables = self.getScriptValues("sysconfigip6tables")

                    fwpath = '/etc/sysconfig/system-config-firewall'
                    iptpath = '/etc/sysconfig/iptables'
                    ip6tpath = '/etc/sysconfig/ip6tables'
                    #portion to handle the system-config-firewall file
                    created = False
                    if not os.path.exists(fwpath):
                        if not createFile(fwpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + fwpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": fwpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(fwpath):
                        if not checkPerms(fwpath, [0, 0, 0o600], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(fwpath, [0, 0, 0o600], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + fwpath + "\n"
                        contents = readFile(fwpath, self.logger)
                        if contents != systemconfigfirewall:
                            print("contents don't equal systemconfigurefirewall contents\n")
                            tempfile = fwpath + ".tmp"
                            if not writeFile(tempfile, systemconfigfirewall, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + fwpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": fwpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(fwpath, tempfile, myid)
                                os.rename(tempfile, fwpath)
                                os.chown(fwpath, 0, 0)
                                os.chmod(fwpath, 0o600)
                                resetsecon(fwpath)
                    created = False
                    #portion to handle the iptables rules file
                    if not os.path.exists(iptpath):
                        if not createFile(iptpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + iptpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": iptpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(iptpath):
                        if not checkPerms(iptpath, [0, 0, 0o644], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(iptpath, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + iptpath + "\n"
                        contents = readFile(iptpath, self.logger)
                        if contents != sysconfigiptables:
                            tempfile = iptpath + ".tmp"
                            if not writeFile(tempfile, sysconfigiptables, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + iptpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": iptpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(iptpath, tempfile, myid)
                                os.rename(tempfile, iptpath)
                                os.chown(iptpath, 0, 0)
                                os.chmod(iptpath, 0o644)
                                resetsecon(iptpath)
                    created = False
                    #portion to handle ip6tables rules file
                    if not os.path.exists(ip6tpath):
                        if not createFile(ip6tpath, self.logger):
                            success = False
                            self.detailedresults += "Unable to create file " + ip6tpath + "\n"
                        else:
                            created = True
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {"eventtype": "creation",
                                     "filepath": ip6tpath}
                            self.statechglogger.recordchgevent(myid, event)
                    if os.path.exists(ip6tpath):
                        if not checkPerms(ip6tpath, [0, 0, 0o644], self.logger):
                            if not created:
                                self.iditerator += 1
                                myid = iterate(self.iditerator, self.rulenumber)
                                if not setPerms(ip6tpath, [0, 0, 0o644], self.logger, self.statechglogger, myid):
                                    success = False
                                    self.detailedresults += "Unable to set permissions on " + ip6tpath + "\n"
                        contents = readFile(ip6tpath, self.logger)
                        if contents != sysconfigip6tables:
                            tempfile = ip6tpath + ".tmp"
                            if not writeFile(tempfile, sysconfigip6tables, self.logger):
                                success = False
                                self.detailedresults += "Unable to write contents to " + ip6tpath + "\n"
                            else:
                                if not created:
                                    self.iditerator += 1
                                    myid = iterate(self.iditerator, self.rulenumber)
                                    event = {"eventtype": "conf",
                                             "filepath": ip6tpath}
                                    self.statechglogger.recordchgevent(myid, event)
                                    self.statechglogger.recordfilechange(ip6tpath, tempfile, myid)
                                os.rename(tempfile, ip6tpath)
                                os.chown(ip6tpath, 0, 0)
                                os.chmod(ip6tpath, 0o644)
                                resetsecon(ip6tpath)
                    # check if iptables is enabled to run at start
                    if not self.servicehelper.auditService('iptables'):
                        # enable service to run at start if not
                        if not self.servicehelper.enableService('iptables'):
                            self.detailedresults += "Unable to enable iptables service\n"
                            debug = "Unable to enable iptables service\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                        else:
                            # record event if successful
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = self.servicehelper.getDisableCommand('iptables')
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)

                    self.servicehelper.stopService('iptables')
                    # start iptables if not
                    if not self.servicehelper.startService('iptables'):
                        self.detailedresults += "Unable to start iptables service\n"
                        debug = "Unable to start iptables service\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False
                    else:
                        stonixfilepath = "/var/db/stonix/"
                        savecmd = "/sbin/iptables-save > " + stonixfilepath + "user-firewall-pre-stonix"
                        if not self.cmdhelper.executeCommand(savecmd):
                            success = False
                            self.detailedresults += "Unable to save current ipv4 " + \
                                                    "firewall rules for revert\n"
                            debug = "Unable to save current ipv4 " + \
                                    "firewall rules for revert\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = "/sbin/iptables-restore < " + stonixfilepath + "user-firewall-pre-stonix"
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)
                        savecmd = "/sbin/ip6tables-save > " + stonixfilepath + "user-firewall6-pre-stonix"
                        if not self.cmdhelper.executeCommand(savecmd):
                            success = False
                            self.detailedresults += "Unable to save current ipv6 " + \
                                                    "firewall rules for revert\n"
                            debug = "Unable to save current ipv6 " + \
                                    "firewall rules for revert\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = "/sbin/ip6tables-restore < " + stonixfilepath + "user-firewall6-pre-stonix"
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)
                    # check if ip6tables is enabled to run at start
                    if not self.servicehelper.auditService('ip6tables'):
                        # enable service to run at start if not
                        if not self.servicehelper.enableService('ip6tables'):
                            self.detailedresults += "Unable to enable ip6tables service\n"
                            debug = "Unable to enable ip6tables service\n"
                            self.logger.log(LogPriority.DEBUG, debug)
                            success = False
                        else:
                            # record event if successful
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            cmd = self.servicehelper.getDisableCommand('ip6tables')
                            event = {"eventtype": "commandstring",
                                     "command": cmd}
                            self.statechglogger.recordchgevent(myid, event)

                    self.servicehelper.stopService('ip6tables')
                    # start ip6tables if not
                    if not self.servicehelper.startService('ip6tables'):
                        self.detailedresults += "Unable to start ip6tables service\n"
                        debug = "Unable to start ip6tables service\n"
                        self.logger.log(LogPriority.DEBUG, debug)
                        success = False

                     # Sleep for a bit to let the restarts occur
                    time.sleep(10)
            self.rulesuccess = success
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

    def checkFirewalld(self):
        """

        :return: bool; True if the firewall-cmd path exists, False if not
        """

        firewalld_paths = ["/bin/firewall-cmd", "/usr/bin/firewall-cmd"]
        for p in firewalld_paths:
            if os.path.exists(p):
                return True

    def checkUFW(self):
        #for Ubuntu systems mostly
        if os.path.exists('/usr/sbin/ufw'):
            return True

    def checkIsOther(self):
        #for debian and opensuse mostly
        if "iptables" not in self.servicehelper.listServices():
            return True

    def checkIptables(self):
        # mostly pertains to RHEL6, Centos6

        if os.path.exists("/usr/sbin/iptables"):
            self.iptables = "/usr/sbin/iptables"
        elif os.path.exists("/sbin/iptables"):
            self.iptables = "/sbin/iptables"

        if os.path.exists("/usr/sbin/ip6tables"):
            self.ip6tables = "/usr/sbin/ip6tables"
        elif os.path.exists("/sbin/ip6tables"):
            self.ip6tables = "/sbin/ip6tables"

        if os.path.exists("/usr/sbin/iptables-restore"):
            self.iprestore = "/usr/sbin/iptables-restore"
        elif os.path.exists("/sbin/iptables-restore"):
            self.iprestore = "/sbin/iptables-restore"

        if os.path.exists("/usr/sbin/ip6tables-restore"):
            self.ip6restore = "/usr/sbin/ip6tables-restore"
        elif os.path.exists("/sbin/ip6tables-restore"):
            self.ip6restore = "/sbin/ip6tables-restore"

    def getScriptValues(self, scriptname):
        if scriptname == "iptscript":
            iptScript = '''fw_custom_after_chain_creation() {
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
    true
}

fw_custom_before_port_handling() {
    true
}

fw_custom_before_masq() {
    true
}

fw_custom_before_denyall() {
    true
}

fw_custom_after_finished() {
    true
}
'''
            return iptScript
        elif scriptname == "iptables":
            iptables = '''*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
'''
            return iptables
        elif scriptname == "ip6tables":
            ip6tables = '''*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
'''
            return ip6tables
        elif scriptname == "systemconfigfirewall":
            systemconfigfirewall = '''# Configuration file for system-config-firewall

--enabled
--service=ssh
'''
            return systemconfigfirewall
        elif scriptname == "sysconfigiptables":
            sysconfigiptables = '''# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
'''
            return sysconfigiptables
        elif scriptname == "sysconfigip6tables":
            sysconfigip6tables = '''# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 546 -d fe80::/64 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
'''
            return sysconfigip6tables
예제 #17
0
class SecureIPV6(Rule):

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

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


        :returns: compliant

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

        '''

        compliant = True

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

        try:

            self.cmdhelper = CommandHelper(self.logger)

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

        except Exception:
            raise

        return compliant

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

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

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

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

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

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

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

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

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

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


        :returns: success

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

        '''

        success = True
        created = False

        try:

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

        except Exception:
            raise

        return success

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

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

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

        # correct the network file if it exists
        if netwrkfile:
            created = False
            if not os.path.exists(netwrkfile):
                if not createFile(netwrkfile, self.logger):
                    success = False
                    debug = "Unable to create " + netwrkfile + " file\n"
                    self.logger.log(LogPriority.DEBUG, debug)
                else:
                    created = True
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    event = {"eventtype": "creation",
                             "filepath": netwrkfile}
                    self.statechglogger.recordchgevent(myid, event)
                    tmpfile = netwrkfile + ".tmp"
                    self.editor2 = KVEditorStonix(self.statechglogger, self.logger,
                                                  "conf", netwrkfile, tmpfile,
                                                  self.interface1, "present", "closedeq")
                    self.editor2.report()
            if os.path.exists(netwrkfile):
                if not checkPerms(netwrkfile, [0, 0, 0o644], self.logger):
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        if not setPerms(netwrkfile, [0, 0, 0o644], self.logger,
                                        self.statechglogger, myid):
                            success = False
                if self.editor2:
                    if self.editor2.fixables:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        self.editor2.setEventID(myid)
                        if not self.editor2.fix():
                            success = False
                        elif not self.editor2.commit():
                            success = False
                        os.chown(netwrkfile, 0, 0)
                        os.chmod(netwrkfile, 0o644)
                        resetsecon(netwrkfile)
        if ifacefile:
            if os.path.exists(ifacefile):
                dirs = glob.glob(ifacefile + "*")
                if dirs:
                    for loc in dirs:
                        interface2 = {"IPV6_PRIVACY": "rfc3041"}
#                                       "IPV6_DEFAULTGW": self.gateway,
#                                       "IPV6ADDR":self.ipaddr}
                        interface3 = {"IPV6_PRIVACY": "rfc3041"}
#                                       "IPV6_DEFAULTGW": self.gateway,
#                                       "IPV6ADDR":self.ipaddr}
                        found = False
                        tempstring = ""
                        if re.search('^' + ifacefile + 'ifcfg', loc):
                            filename = loc
                            tmpfile = filename + ".tmp"
                            contents = readFile(filename, self.logger)
                            if not checkPerms(filename, [0, 0, 0o644],
                                              self.logger):
                                self.iditerator += 1
                                myid = iterate(self.iditerator,
                                               self.rulenumber)
                                if not setPerms(filename, [0, 0, 0o644],
                                                self.logger,
                                                self.statechglogger, myid):
                                    return False
                            for key in interface2:
                                found = False
                                for line in contents:
                                    if re.search("^#", line) or \
                                            re.match("^\s*$", line):
                                        continue
                                    if re.search("^" + key, line):
                                        if re.search("=", line):
                                            temp = line.split("=")
                                            if temp[1].strip() == \
                                                    interface2[key]:
                                                if found:
                                                    continue
                                                found = True
                                            else:
                                                contents.remove(line)
                                if found:
                                    del interface3[key]
                            for line in contents:
                                tempstring += line
                            tempstring += universal
                            for key in interface3:
                                tempstring += key + "=" + interface3[key] + \
                                    "\n"
                            if not writeFile(tmpfile, tempstring, self.logger):
                                return False
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {'eventtype': 'conf',
                                     'filepath': filename}
                            self.statechglogger.recordchgevent(myid, event)
                            self.statechglogger.recordfilechange(filename,
                                                                 tmpfile, myid)
                            os.rename(tmpfile, filename)
                            os.chown(filename, 0, 0)
                            os.chmod(filename, 0o644)
                            resetsecon(filename)
            elif not os.path.exists(ifacefile) and ifacefile != "":
                # will not attempt to create the interface files
                self.detailedresults += "Interface directory which holds interface \
                files, doesn't exist. Stonix will not attempt to make this \
                directory or the files contained therein."
                success = False
        return success
예제 #18
0
class DisableRemoteAppleEvents(Rule):
    '''classdocs'''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logger
        self.statechglogger = statechglogger
        self.rulenumber = 213
        self.rulename = 'DisableRemoteAppleEvents'
        self.formatDetailedResults("initialize")
        self.compliant = False
        self.mandatory = True
        self.rootrequired = True
        self.guidance = ['CIS 1.4.14.10']
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.sethelptext()

        # set up CIs
        datatype = 'bool'
        key = 'DISABLEREMOTEAPPLEEVENTS'
        instructions = 'To allow the use of remote apple events on this system, set the value of DisableRemoteAppleEvents to False.'
        default = True
        self.disableremoteevents = self.initCi(datatype, key, instructions,
                                               default)

        datatype2 = 'list'
        key2 = 'REMOTEAPPLEEVENTSUSERS'
        instructions2 = 'If you have a business requirement to have remote apple events turned on, enter a list of users who will be allowed access to remote apple events on this system'
        default2 = []
        self.secureremoteevents = self.initCi(datatype2, key2, instructions2,
                                              default2)

    def report(self):
        '''return the compliance status of the system with this rule


        :returns: self.compliant

        :rtype: bool

@author: Breen Malmberg

        '''

        self.detailedresults = ""
        self.cmhelper = CommandHelper(self.logger)
        self.compliant = True

        try:

            print(("Value of disableremoteevents CI = " +
                   str(self.disableremoteevents.getcurrvalue())))

            if self.disableremoteevents.getcurrvalue():
                if not self.reportDisabled():
                    self.compliant = False

            print(("Value of secureremoteevents CI = " +
                   str(self.secureremoteevents.getcurrvalue())))

            if self.secureremoteevents.getcurrvalue() != []:
                if not self.reportSecured():
                    self.compliant = False

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

        return self.compliant

    def reportDisabled(self):
        ''' '''

        self.logger.log(LogPriority.DEBUG,
                        "Checking if remote apple events are disabled...")

        retval = False
        get_remote_ae = "/usr/sbin/systemsetup -getremoteappleevents"
        searchphrase = "Remote Apple Events: Off"

        self.cmhelper.executeCommand(get_remote_ae)
        outputlist = self.cmhelper.getOutput()
        for line in outputlist:
            if re.search(searchphrase, line, re.IGNORECASE):
                retval = True

        if not retval:
            self.detailedresults += "\nRemote Apple Events are On"
        else:
            self.detailedresults += "\nRemote Apple Events are Off"

        return retval

    def reportSecured(self):
        ''' '''

        self.logger.log(LogPriority.DEBUG,
                        "Checking if remote apple events is secured...")

        retval = True
        uuid_list = self.getUUIDs(self.secureremoteevents.getcurrvalue())
        remote_ae_users = self.getRemoteAEUsers()

        difference = list(set(uuid_list) - set(remote_ae_users))
        if difference:
            retval = False
            self.detailedresults += "\nThe current list of allowed remote access users does not match the desired list of remote access users"
            self.detailedresults += "\nDifference: " + " ".join(difference)

        return retval

    def getRemoteAEUsers(self):
        '''return a list of uuid's of current remote ae users
        (mac os x stores the remote ae users as uuid's in a plist)


        '''

        get_remote_ae_users = "/usr/bin/dscl . read /Groups/com.apple.access_remote_ae GroupMembers"
        remote_ae_users = []

        # it is possible that the key "GroupMembers" does not exist
        # this is because when you remove the last remote ae user from the list,
        # mac os x deletes this key from the plist as well..
        self.cmhelper.executeCommand(get_remote_ae_users)
        retcode = self.cmhelper.getReturnCode()
        if retcode == 0:
            remote_ae_users = self.cmhelper.getOutputString().split()
        else:
            errmsg = self.cmhelper.getErrorString()
            self.logger.log(LogPriority.DEBUG, str(errmsg))
        if "GroupMembers:" in remote_ae_users:
            remote_ae_users.remove("GroupMembers:")

        return remote_ae_users

    def fix(self):
        '''run commands needed to bring the system to a compliant state with this
        rule


        :returns: self.rulesuccess

        :rtype: bool

@author: Breen Malmberg

        '''

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

        try:

            if self.disableremoteevents.getcurrvalue():
                if not self.disable_remote_ae():
                    self.rulesuccess = False

            if self.secureremoteevents.getcurrvalue():
                if not self.secure_remote_ae():
                    self.rulesuccess = False

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

        return self.rulesuccess

    def disable_remote_ae(self):
        ''' '''

        retval = True

        disable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents off"
        undocmd = "/usr/sbin/systemsetup setremoteappleevents on"
        self.cmhelper.executeCommand(disable_remote_ae_cmd)
        retcode = self.cmhelper.getReturnCode()
        if retcode != 0:
            retval = False
        else:
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "commandstring", "command": undocmd}

            self.statechglogger.recordchgevent(myid, event)

        return retval

    def secure_remote_ae(self):
        ''' '''

        retval = True
        desired_remote_ae_users = self.getUUIDs(
            self.secureremoteevents.getcurrvalue())
        securecmd = "/usr/bin/dscl . create /Groups/com.apple.access_remote_ae GroupMembers " + " ".join(
            desired_remote_ae_users)
        original_remote_ae_users = self.getRemoteAEUsers()
        if original_remote_ae_users:
            undocmd = "/usr/bin/dscl . create /Groups/com.apple.access_remote_ae GroupMembers " + " ".join(
                original_remote_ae_users)
        else:
            undocmd = "/usr/bin/dscl . delete /Groups/com.apple.access_remote_ae GroupMembers"

        self.cmhelper.executeCommand(securecmd)
        retcode = self.cmhelper.getReturnCode()
        if retcode == 0:
            self.iditerator += 1

            myid = iterate(self.iditerator, self.rulenumber)
            event = {"eventtype": "commandstring", "command": undocmd}

            self.statechglogger.recordchgevent(myid, event)
        else:
            retval = False
            self.detailedresults += "\nFailed to properly configure the desired remote apple events users"

        # we assume the user wants to use the service if they configure the user list for it
        # and turn off the disable events CI
        if not self.disableremoteevents.getcurrvalue():
            undo_enable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents off"
            enable_remote_ae_cmd = "/usr/sbin/systemsetup setremoteappleevents on"
            self.cmhelper.executeCommand(enable_remote_ae_cmd)
            retcode = self.cmhelper.getReturnCode()
            if retcode != 0:
                retval = False
                self.detailedresults += "\nFailed to enable remote apple events service"
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {
                    "eventtype": "commandstring",
                    "command": undo_enable_remote_ae_cmd
                }
                self.statechglogger.recordchgevent(myid, event)

        return retval

    def getUUIDs(self, userlist):
        '''convert the desired (user-specified) list of ae user names
        into uuid's; return as list

        :param userlist: 

        '''

        uuidlist = []

        for user in userlist:
            output = ""
            get_uuid = "/usr/bin/dsmemberutil getuuid -U " + user
            self.cmhelper.executeCommand(get_uuid)
            output = self.cmhelper.getOutputString()
            if re.search("no uuid", output, re.IGNORECASE):
                continue
            else:
                if output:
                    uuidlist.append(output.strip())

        return uuidlist
예제 #19
0
class Yum(object):
    '''The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

    @author: Derek T Walker
    @change: 2012/08/06 dwalker - Original Implementation
    @change: 2015/08/20 eball - Added getPackageFromFile
    '''
    def __init__(self, logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/bin/yum install -y "
        self.remove = "/usr/bin/yum remove -y "
        self.search = "/usr/bin/yum search "
        self.rpm = "/bin/rpm -q "
###############################################################################

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

    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.
        @param string package : Name of the package to be removed, must be
        recognizable to the underlying package manager.
        @return bool :
        @author'''
        try:
            removed = False
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                removed = True
                self.detailedresults += package + " pkg removed successfully\n"
            else:
                self.detailedresults += package + \
                    " pkg not able to be removed\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return removed
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if
        the package is installed.
        @param string package : Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return bool :
        @author'''
        try:
            found = False
            self.ch.executeCommand(self.rpm + package)
            if self.ch.getReturnCode() == 0:
                found = True
                self.detailedresults += package + " pkg found\n"
            else:
                self.detailedresults += package + " pkg not found\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

    def checkAvailable(self, package):
        try:
            found = False
            self.ch.executeCommand(self.search + package)
            output = self.ch.getOutputString()
            if re.search("no matches found", output.lower()):
                self.detailedresults += package + " pkg is not available " + \
                    " or may be misspelled\n"
            elif re.search("matched", output.lower()):
                self.detailedresults += package + " pkg is available\n"
                found = True
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise (self.detailedresults)
###############################################################################

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

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

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

    def getRemove(self):
        return self.remove
예제 #20
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
예제 #21
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
예제 #22
0
파일: yum.py 프로젝트: CSD-Public/stonix
class Yum(object):
    '''
    The template class that provides a framework that must be implemented by
    all platform specific pkgmgr classes.

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

    def __init__(self, logger):
        self.environ = Environment()
        self.logger = logger
        self.ch = CommandHelper(self.logger)
        self.yumloc = "/usr/bin/yum"
        self.install = self.yumloc + " install -y "
        self.remove = self.yumloc + " remove -y "
        self.search = self.yumloc + " list "
        self.checkupdates = self.search + "updates "
        self.listavail = self.search + "available "
        self.listinstalled = self.search + "installed "
        self.updatepkg = self.yumloc + " update -y --obsoletes "
        myos = self.environ.getostype().lower()
        if re.search("red hat.*?release 6", myos) or \
                re.search("^centos$", myos.strip()):
            self.rpmloc = "/bin/rpm"
        else:
            self.rpmloc = "/usr/bin/rpm"
        self.provides = self.rpmloc + " -qf "
        self.query = self.rpmloc + " -qa "

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

        @param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        @return: installed
        @rtype: bool
        @author: Derek T. Walker
        @change: Breen Malmberg - 4/24/2017 - refactored method; added logging; replaced
                detailedresults with logging
        @change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop
        '''

        installed = True
        maxtries = 12
        trynum = 0

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

        try:

            try:

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

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

        except Exception:
            raise
        return installed

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

        @param package: string; Name of the package to be removed, must be
                recognizable to the underlying package manager.
        @return: removed
        @rtype: bool
        @author: Derek T. Walker
        @change: Breen Malmberg - 4/24/2017 - refactored method; added logging; replaced
                detailedresults with logging
        '''

        removed = True
        maxtries = 12
        trynum = 0

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

        try:

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

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

        except Exception:
            raise
        return removed

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

        @param package: string; Name of the package whose installation status
            is to be checked, must be recognizable to the underlying package
            manager.
        @return: found
        @rtype: bool
        @author: Derek T. Walker
        @change: Breen Malmberg - 4/24/2017 - refactored method; added logging; replaced
                detailedresults with logging
        '''

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

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

        try:

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

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

        except Exception:
            raise
        return installed

    def Update(self, package=""):
        '''
        update specified package if any updates
        are available for it
        if no package is specified, update all
        packages which can be updated on the system

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

        updated = True

        try:

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

            if package:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was successfully updated")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates were found for package " + str(package))
            else:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "All packages were successfully updated")
                else:
                    self.logger.log(LogPriority.DEBUG, "No updates were found for this system")

        except Exception:
            raise
        return updated

    def checkUpdate(self, package=""):
        '''
        check if there are any updates available for
        specified package
        if no package is specified, check if any updates
        are available for the current system

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

        updatesavail = False

        try:

            try:
                self.ch.executeCommand(self.checkupdates + package)
                retcode = self.ch.getReturnCode()
                output = self.ch.getOutputString()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('yum', retcode, str(errstr))
                else:
                    if re.search("Updated packages", output, re.IGNORECASE):
                        updatesavail = True
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                else:
                    if re.search("Updated packages", output, re.IGNORECASE):
                        updatesavail = True

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

        except Exception:
            raise
        return updatesavail

    def checkAvailable(self, package):
        '''
        check if specified package is available to install
        return True if it is
        return False if not

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

        available = True
        maxtries = 12
        trynum = 0

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

        try:

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

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

        except Exception:
            raise
        return available

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

        @param filename: string; The name or path of the file to resolve
        @return: packagename
        @rtype: string
        @author: Eric Ball
        @change: Breen Malmberg - 4/24/2017 - refactored method; added logging; replaced
                detailedresults with logging
        '''

        packagename = ""

        try:

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

        except Exception:
            raise
        return packagename

    def getInstall(self):
        return self.install

    def getRemove(self):
        return self.remove
예제 #23
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
예제 #24
0
class AptGet(object):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def getInstall(self):
        return self.install

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

    def getRemove(self):
        return self.remove
예제 #25
0
class DisableInactiveAccounts(Rule):
    '''This rule will set the global policy for inactive accounts so that any
    account not accessed/used within 35 days will be automatically disabled.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 4
        self.rulename = 'DisableInactiveAccounts'
        self.compliant = True
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.rootrequired = True
        self.sethelptext()
        self.guidance = ['CNSSI 1253', 'DISA STIG']

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

        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            },
            'fisma': 'high'
        }

        self.initobjs()

    def initobjs(self):
        '''initialize objects for use by this class


        :returns: void
        @author: Breen Malmberg

        '''

        self.cmdhelper = CommandHelper(self.logger)

    def getEnabledAccounts(self):
        '''return a list of all currently enabled accounts


        :returns: enabledaccounts

        :rtype: list
@author: Breen Malmberg

        '''

        allaccounts = []
        enabledaccounts = []
        getallaccounts = "/usr/bin/dscl . -list /Users"
        getenabled = "/usr/bin/pwpolicy -u {username} --get-effective-policy"

        try:

            self.cmdhelper.executeCommand(getallaccounts)
            allaccounts = self.cmdhelper.getOutput()
            if allaccounts:
                for acc in allaccounts:
                    self.cmdhelper.executeCommand(
                        getenabled.replace("{username}", acc))
                    outputstr = self.cmdhelper.getOutputString()
                    if re.search("isDisabled=false", outputstr, re.IGNORECASE):
                        enabledaccounts.append(acc)

        except Exception:
            raise

        return enabledaccounts

    def report(self):
        '''get a list of users
        determine each user's password last set time
        determine if each user is inactive


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

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

        self.compliant = True
        self.detailedresults = ''

        # do not check any accounts with these regex terms found in them
        # these are accounts which would not have the passwordlastsettime key
        # in their accountpolicydata, and accounts which we do not want to
        # disable
        accexcludere = ['_', 'nobody', 'daemon', 'root']
        for account in EXCLUDEACCOUNTS:
            accexcludere.append(account)
        self.inactiveaccounts = []

        try:

            userlist = self.getEnabledAccounts()

            if self.cmdhelper.getReturnCode() != 0:
                self.rulesuccess = False
                self.compliant = False
                self.detailedresults += '\nThere was a problem retrieving ' + \
                    'the list of users on this system.'

            userlistnew = []
            for user in userlist:
                removeuser = False
                for element in accexcludere:
                    if re.search(element, user):
                        removeuser = True
                if not removeuser:
                    userlistnew.append(user)

            for user in userlistnew:
                inactivedays = self.getinactivedays(user.strip())
                if int(inactivedays) > 35:
                    self.compliant = False
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + \
                        '" has been inactive for more than 35 days.'
                    self.inactiveaccounts.append(user.strip())
                elif int(inactivedays) > 0 and int(inactivedays) <= 35:
                    daysleft = 35 - int(inactivedays)
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + '" has been inactive for ' + \
                        str(inactivedays) + ' days. You have ' + \
                        str(daysleft) + \
                        ' days left before this account will be disabled.'
                    self.logger.log(
                        LogPriority.DEBUG, '\nThe user account "' +
                        user.strip() + '" has been inactive for ' +
                        str(inactivedays) + ' days. You have ' +
                        str(daysleft) + ' days left before this ' +
                        'account will be disabled.')
                else:
                    self.detailedresults += '\nThe user account "' + \
                        user.strip() + '" is not inactive. No problems.'

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

    def getinactivedays(self, user):
        '''Get and return the number of days a given user account has been
        inactive

        :param user: string the name of the account to check
        @author: Breen Malmberg
        :returns: inactivedays
        :rtype: int

        '''

        inactivedays = 0
        date_format = "%a %b %d %H:%M:%S %Y"

        try:

            if not user:
                self.logger.log(
                    LogPriority.DEBUG, "The given value for " +
                    "parameter user was None, or blank!")
                return inactivedays

            if not isinstance(user, str):
                self.logger.log(
                    LogPriority.DEBUG,
                    "The given value for parameter user was not " +
                    "of the correct type (int)!")
                return inactivedays

            self.cmdhelper.executeCommand('/usr/bin/dscl . readpl /Users/' +
                                          user + ' accountPolicyData ' +
                                          'passwordLastSetTime')
            epochchangetimestr = self.cmdhelper.getOutputString()
            retcode = self.cmdhelper.getReturnCode()
            outstr = self.cmdhelper.getOutputString()
            if retcode != 0:
                if retcode == 181:  # this is the mac os x error code when a plist path does not exist
                    if re.search("No such plist path: passwordLastSetTime",
                                 outstr, re.IGNORECASE):
                        self.detailedresults += "The local account: " + str(
                            user
                        ) + " has never had a password set for it! We will now disable this local account on this machine."
                        self.logger.log(
                            LogPriority.DEBUG,
                            "The local user account: " + str(user) +
                            " had no password for it. STONIX will disable it now."
                        )
                        inactivedays = 9999  # this will ensure it gets added to the list of accounts to disable
                    return inactivedays
                else:
                    self.detailedresults += '\nThere was an issue reading ' + \
                        user + '\'s accountPolicyData passwordLastSetTime'
                    self.compliant = False
                    return inactivedays

            epochchangetimelist = epochchangetimestr.split(':')
            epochchangetimestropr = epochchangetimelist[1].strip()
            epochchangetime = Decimal(epochchangetimestropr)
            pwchangedate = time.ctime(epochchangetime)
            a = datetime.strptime(pwchangedate, date_format)
            now = time.ctime()
            b = datetime.strptime(now, date_format)
            diff = b - a
            if int(diff.days) > 180:
                inactivedays = int(diff.days) - 180

        except Exception:
            raise
        return inactivedays

    def fix(self):
        '''check if ci is enabled
        if it is, run fix actions for this rule
        if not, report that it is disabled


        :returns: fixsuccess

        :rtype: bool
@author: Breen Malmberg

        '''

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

        # defaults
        fixsuccess = True
        self.detailedresults = ''
        self.iditerator = 0
        disabledaccounts = []

        try:

            if self.ci.getcurrvalue():

                if self.inactiveaccounts:
                    for user in self.inactiveaccounts:
                        self.cmdhelper.executeCommand(
                            '/usr/bin/pwpolicy -disableuser -u ' + user)
                        errout = self.cmdhelper.getErrorString()
                        rc = self.cmdhelper.getReturnCode()
                        if rc != 0:
                            self.detailedresults += '\nThere was an issue trying to disable user account: ' + user
                            self.logger.log(LogPriority.DEBUG, errout)
                            fixsuccess = False
                        else:
                            self.iditerator += 1
                            myid = iterate(self.iditerator, self.rulenumber)
                            event = {
                                'eventtype':
                                'commandstring',
                                'command':
                                '/usr/bin/pwpolicy -enableuser -u ' + user
                            }
                            self.statechglogger.recordchgevent(myid, event)
                            disabledaccounts.append(user)
                            self.logger.log(
                                LogPriority.DEBUG, "Disabling user account: " +
                                str(user) + " ...")
                    if disabledaccounts:
                        self.detailedresults += "\nDisabled the following accounts: " + "\n- ".join(
                            disabledaccounts)
                else:
                    self.detailedresults += '\nNo inactive accounts detected. No accounts were disabled.'

            else:
                self.detailedresults += '\nThe CI for this rule was not enabled. Nothing was done.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", fixsuccess, self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return fixsuccess
예제 #26
0
class DisableWebSharing(Rule):
    '''Web Sharing uses the Apache 2.2.x web server to turn the Mac into an HTTP/Web
    server. As with file sharing, web sharing is best left off and a dedicated,
    well-managed web server is recommended.


    '''
    def __init__(self, config, environ, logger, statechglogger):
        '''
        Constructor
        '''
        Rule.__init__(self, config, environ, logger, statechglogger)
        self.rulenumber = 208
        self.rulename = 'DisableWebSharing'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.compliant = False
        self.rootrequired = True
        self.guidance = ['CIS 1.4.14.6']
        self.applicable = {
            'type': 'white',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }
        self.logger = logger
        # set up CIs
        datatype = 'bool'
        key = 'DISABLEWEBSHARING'
        instructions = 'To prevent web sharing from being disabled, set the value of DisableWebSharing to False.'
        default = True
        self.disableWebSharing = self.initCi(datatype, key, instructions,
                                             default)

        # set up class var's
        self.maclongname = '/System/Library/LaunchDaemons/org.apache.httpd.plist'
        self.macshortname = 'org.apache.httpd'
        self.svchelper = ServiceHelper(self.environ, self.logger)
        self.cmhelper = CommandHelper(self.logger)
        self.sethelptext()

    def report(self):
        '''Report status of web sharing and compliance


        :returns: self.compliant

        :rtype: bool
@author: Breen Malmberg

        '''

        # defaults
        self.detailedresults = ''
        self.compliant = False

        # init servicehelper object
        if not os.path.exists(self.maclongname):
            self.compliant = True
            self.detailedresults += '\norg.apache.httpd.plist does not exist. Nothing to configure'
            self.formatDetailedResults("report", self.compliant,
                                       self.detailedresults)
            self.logdispatch.log(LogPriority.INFO, self.detailedresults)
            return self.compliant

        try:

            self.logger.log(
                LogPriority.DEBUG,
                "starting audit service for service: " + str(self.maclongname))
            if not self.svchelper.auditService(
                    self.maclongname, serviceTarget=self.macshortname):
                self.logger.log(
                    LogPriority.DEBUG,
                    str(self.maclongname) + " is not running/loaded")

                self.logger.log(
                    LogPriority.DEBUG, "Checking if " + str(self.maclongname) +
                    " is disabled in the plist")
                self.cmhelper.executeCommand(
                    'defaults read /System/Library/LaunchDaemons/org.apache.httpd Disabled'
                )
                retcode = self.cmhelper.getReturnCode()
                if retcode != 0:
                    errout = self.cmhelper.getErrorString()
                    self.logger.log(LogPriority.DEBUG, errout)
                else:
                    output = self.cmhelper.getOutputString()
                    if re.search('1', output):
                        self.logger.log(
                            LogPriority.DEBUG,
                            str(self.maclongname) +
                            " is disabled in the plist")
                        self.compliant = True
                    else:
                        self.logger.log(
                            LogPriority.DEBUG,
                            str(self.maclongname) +
                            " is NOT disabled in the plist")
            else:
                self.detailedresults += '\n' + str(
                    self.maclongname) + ' is still loaded/enabled'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            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):
        '''Perform operations to disable web sharing


        :returns: self.rulesuccess

        :rtype: bool
@author: Breen Malmberg

        '''

        # defaults
        self.detailedresults = ''
        self.rulesuccess = True
        self.id = 0

        try:

            if self.disableWebSharing.getcurrvalue():

                #if not self.cmhelper.executeCommand('defaults write /System/Library/LaunchDaemons/org.apache.httpd Disabled -bool true'):
                #    self.rulesuccess = False
                if not self.svchelper.disableService(
                        self.maclongname, servicename=self.macshortname):
                    self.rulesuccess = False
                    self.logger.log(
                        LogPriority.DEBUG,
                        "Failed to disable service: " + str(self.maclongname))
                else:
                    self.id += 1
                    myid = iterate(self.id, self.rulenumber)
                    event = {
                        'eventtype':
                        'commandstring',
                        'command':
                        'defaults delete /System/Library/LaunchDaemons/org.apache.httpd Disabled'
                    }

                    self.statechglogger.recordchgevent(myid, event)

            else:
                self.detailedresults += '\nRule was not enabled, so nothing was done.'

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.rulesuccess = False
            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 afterfix(self):
        afterfixsuccessful = True
        afterfixsuccessful &= self.svchelper.auditService(self.maclongname)
        return afterfixsuccessful
예제 #27
0
파일: yum.py 프로젝트: aes512/stonix
class Yum(object):

    '''The template class that provides a framework that must be implemented by 
    all platform specific pkgmgr classes.
    :version:
    :author:Derek T Walker 08-06-2012'''
    
    def __init__(self,logger):
        self.logger = logger
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)
        self.install = "/usr/bin/yum install -y "
        self.remove = "/usr/bin/yum remove -y "
        self.search = "/usr/bin/yum search "
        self.rpm = "/bin/rpm -q "
###############################################################################
    def installpackage(self, package):
        '''Install a package. Return a bool indicating success or failure.
        @param string package : Name of the package to be installed, must be 
        recognizable to the underlying package manager.
        @return bool :
        @author'''
        try:
            installed = False
            self.ch.executeCommand(self.install + package)
            output = self.ch.getOutputString()
            if self.ch.getReturnCode() == 0:
                installed = True
                self.detailedresults = package + " pkg installed successfully\n"
            else:
                self.detailedresults = package + " pkg not able to install\n"
            self.logger.log(LogPriority.DEBUG,self.detailedresults)
            return installed
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################
    def removepackage(self, package):
        '''Remove a package. Return a bool indicating success or failure.
        @param string package : Name of the package to be removed, must be 
        recognizable to the underlying package manager.
        @return bool :
        @author'''
        method = "yum.remove"
        try:
            removed = False
            self.ch.executeCommand(self.remove + package)
            if self.ch.getReturnCode() == 0:
                removed = True
                self.detailedresults += package + " pkg removed successfully\n"
            else:
                self.detailedresults += package + " pkg not able to be removed\n"
            self.logger.log(LogPriority.DEBUG,[method,self.detailedresults])
            return removed
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR,[method,self.detailedresults])
            raise(self.detailedresults)
###############################################################################
    def checkInstall(self, package):
        '''Check the installation status of a package. Return a bool; True if 
        the package is installed.
        @param string package : Name of the package whose installation status 
            is to be checked, must be recognizable to the underlying package 
            manager.
        @return bool :
        @author'''
        try:
            found = False
            self.ch.executeCommand(self.rpm + package)
            output = self.ch.getOutputString()
            #for redhat systems only
            if self.ch.getReturnCode() == 0:
                found = True
                self.detailedresults += package + " pkg found\n"
            else:
                self.detailedresults += package + " pkg not found\n"
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################
    def checkAvailable(self,package):
        try:
            found = False
            self.ch.executeCommand(self.search + package)
            output = self.ch.getOutputString()
            if re.search("no matches found", output.lower()):
                self.detailedresults += package + " pkg is not available " + \
                " or may be misspelled\n"
            elif re.search("matched", output.lower()):
                self.detailedresults += package + " pkg is available\n"
                found = True
            self.logger.log(LogPriority.DEBUG, self.detailedresults)
            return found
        except(KeyboardInterrupt,SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
            raise(self.detailedresults)
###############################################################################
    def getInstall(self):
        return self.install
###############################################################################
    def getRemove(self):
        return self.remove
예제 #28
0
class Zypper(object):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return self.install

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

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

        return self.remove
예제 #29
0
파일: aptGet.py 프로젝트: CSD-Public/stonix
class AptGet(object):
    '''
    Linux specific package manager for distributions that use the apt-get
    command to install packages.

    @author: Derek T Walker
    @change: 2012/08/06 Derek Walker - Original Implementation
    @change: 2015/08/20 eball - Added getPackageFromFile
    @change: 2017/04/27 Breen Malmberg - added two methods checkUpdate
            and Update; fixed doc string formatting; removed detailedresults
            reset in init; replaced with --force-yes flag with --assume-yes
            (from the man page for apt-get: Force yes. This is a dangerous
            option that will cause apt-get to continue without prompting
            if it is doing something potentially harmful. It should not
            be used except in very special situations. Using --force-yes
            can potentially destroy your system!)
    @change: 2017/08/16 bgonz12 - Added DEBIAN_FRONTEND=noninteractive env var
            to remove function
    @change: 2017/10/18 Breen Malmberg - changed class var names to be more self-explanatory;
            changed command to check whether there are available packages to use the canonical 
            debian/ubuntu method; added calls to repoError exception to determine exact nature 
            and cause of any errors with querying or calling repositories on the system (this adds 
            logging of the nature and cause(s) as well); changed log messaging to be more consistent 
            in style/format; removed calls to validateParam due to concerns about the stability and 
            reliability of that method
    '''

    def __init__(self, logger):

        self.logger = logger
        self.ch = CommandHelper(self.logger)

        self.aptgetloc = "/usr/bin/apt-get"
        self.aptcacheloc = "/usr/bin/apt-cache"
        self.dpkgloc = "/usr/bin/dpkg"

        self.aptinstall = "DEBIAN_FRONTEND=noninteractive " + self.aptgetloc + " -y --assume-yes install "
        self.aptremove = "DEBIAN_FRONTEND=noninteractive " + self.aptgetloc + " -y remove "

        self.dpkgsearch = self.dpkgloc + " -S "
        self.dpkgchkinstalled = self.dpkgloc + " -l "
        self.aptchkupdates = self.aptgetloc + " -u upgrade --assume-no "
        self.aptupgrade = self.aptgetloc + " -u upgrade --assume-yes "
        self.aptchkavail = self.aptcacheloc +  " policy "

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

        @param package: string; Name of the package to be installed, must be
                recognizable to the underlying package manager.
        @return: installed
        @rtype: bool
        @author: Derek Walker
        @change: Breen Malmberg - 4/27/2017 - fixed doc string formatting;
                method now returns a variable; parameter validation added
                detailedresults replaced with logging
        @change: Breen Malmberg - 10/1/2018 - added check for package manager lock and retry loop
        '''

        installed = True
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

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

        try:

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

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

        except Exception:
            raise
        return installed

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

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

        removed = True
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

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

        try:

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

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

        except Exception:
            raise
        return removed

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

        @param: package: string; Name of the package whose installation status
                is to be checked, must be recognizable to the underlying package
                manager.
        @return: installed
        @rtype: bool
        @author: Derek Walker
        @change: Breen Malmberg - 4/27/2017 - fixed doc string formatting;
                method now returns a variable; replaced detailedresults with
                logging
        '''

        installed = False
        stringToMatch = "ii\s+" + str(package)
        outputstr = ""
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

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

        try:

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

            if re.search(stringToMatch, outputstr, re.IGNORECASE):
                installed = True

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

        except Exception:
            raise
        return installed

    def checkAvailable(self, package):
        '''
        check if a given package is available

        @param package: string; Name of package to check
        @return: found
        @rtype: bool
        @author: Derek T. Walker
        @change: Breen Malmberg - 4/27/2017 - created doc string;
                pulled result logging out of conditional 
        '''

        found = False
        repoerr = ""
        outputstr = ""
        maxtries = 12
        trynum = 0
        pslist = ["apt", "apt-get", "dpkg"]

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

        try:

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

            if re.search(package, outputstr, re.IGNORECASE):
                found = True

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

        except Exception:
            raise
        return found

    def Update(self, package=""):
        '''
        update the specified package if any
        updates are available for it
        if no package is specified, apply
        all available updates for the system

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

        updated = True

        try:

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

            if package:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "Package " + str(package) + " was updated successfully")
                else:
                    self.logger.log(LogPriority.DEBUG, "Failed to apply updates to package " + str(package))
            else:
                if updated:
                    self.logger.log(LogPriority.DEBUG, "All updates were installed successfully")
                else:
                    self.logger.log(LogPriority.DEBUG, "Failed to apply updates")

        except Exception:
            raise
        return updated

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

        @param package: string; (OPTIONAL) Name of package to check
        @return: updatesavail
        @rtype: bool
        @author: Breen Malmberg
        '''

        updatesavail = False

        try:

            try:
                self.ch.executeCommand(self.aptchkupdates + package)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('apt', retcode, str(errstr))
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    return False
                else:
                    updatesavail = True

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

        except Exception:
            raise
        return updatesavail

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

        @param: filename: string; The name or path of the file to resolve
        @return: packagename
        @rtype: string
        @author: Eric Ball
        @change: Breen Malmberg - 4/17/2017 - fixed doc string formatting;
                method now returns a variable; added param validation
        '''

        packagename = ""

        try:

            try:
                self.ch.executeCommand(self.dpkgsearch + filename)
                retcode = self.ch.getReturnCode()
                errstr = self.ch.getErrorString()
                if retcode != 0:
                    raise repoError('apt', retcode, str(errstr))
                if self.ch.getReturnCode() == 0:
                    output = self.ch.getOutputString()
                    packagename = output.split(":")[0]
            except repoError as repoerr:
                if not repoerr.success:
                    self.logger.log(LogPriority.WARNING, str(errstr))
                    pass

        except Exception:
            raise
        return packagename

    def getInstall(self):
        return self.aptinstall

    def getRemove(self):
        return self.aptremove
예제 #30
0
class SHsystemctl(ServiceHelperTemplate):
    """
    SHsystemctl is the Service Helper for systems using the systemctl command to
    configure services. (Fedora and future RHEL and variants)
    """

    def __init__(self, environment, logdispatcher):
        """
        Constructor
        """
        super(SHsystemctl, self).__init__(environment, logdispatcher)
        self.environment = environment
        self.logdispatcher = logdispatcher
        self.ch = CommandHelper(self.logdispatcher)

        self.localize()

    def localize(self):
        """

        @return:
        """

        systemctl_paths = ["/usr/bin/systemctl", "/bin/systemctl"]
        self.sysctl = ""

        for sp in systemctl_paths:
            if os.path.exists(sp):
                self.sysctl = sp
                break

        if not self.sysctl:
            raise IOError("Cannot find systemctl utility on this system!")

        # do not attempt to manipulate any service which has a status in this list
        self.handsoff = ["static", "transient", "generated", "masked", "masked-runtime"]

    def disableService(self, service, **kwargs):
        """
        Disables the service and terminates it if it is running.

        @param service: string; Name of the service to be disabled
        @return: disabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor;
                doc string edit; logging edit
        """

        disabled = True
    
        self.ch.executeCommand(self.sysctl + " disable " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            disabled = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        if not self.stopService(service):
            disabled = False

        return disabled

    def enableService(self, service, **kwargs):
        """
        Enables a service and starts it if it is not running as long as we are
        not in install mode

        @param service: string; Name of the service to be disabled
        @return: enabled
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor;
                doc string edit; logging edit
        """

        enabled = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            enabled = False
            return enabled

        self.ch.executeCommand(self.sysctl + " enable " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            enabled = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return enabled

    def auditService(self, service, **kwargs):
        """
        Checks the status of a service and returns a bool indicating whether or
        not the service is configured to run or not.

        @param service: string; Name of the service to audit
        @return: enabled
        @rtype: bool
        """

        enabled = False

        self.ch.executeCommand(self.sysctl + " is-enabled " + service)

        if self.ch.findInOutput("not a native service"):
            self.logdispatcher.log(LogPriority.DEBUG, "Attempted to audit a non-systemd service with systemctl commands")
            return enabled
        elif self.ch.findInOutput("enabled"):
            enabled = True

        return enabled

    def isRunning(self, service, **kwargs):
        """
        Check to see if a service is currently running. The enable service uses
        this so that we're not trying to start a service that is already
        running.

        @param service: string; name of service to check
        @return: running
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                debug logging edit
        """

        running = True
        inactive_keys = ["inactive", "unknown"]

        self.ch.executeCommand(self.sysctl + " is-active " + service)
        for k in inactive_keys:
            if self.ch.findInOutput(k):
                running = False

        return running

    def reloadService(self, service, **kwargs):
        """
        Reload (HUP) a service so that it re-reads it's config files. Called
        by rules that are configuring a service to make the new configuration
        active.

        @param service: string; Name of the service to reload
        @return: success
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; doc string edit;
                debug logging edit
        """

        success = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            success = False
            return success

        self.ch.executeCommand(self.sysctl + " reload-or-restart " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)
        if not self.isRunning(service):
            success = False

        return success

    def listServices(self, **kwargs):
        """
        Return a list containing strings that are service names.

        @return: service_list
        @rtype: bool
        @author: ???
        @change: Breen Malmberg - 04/10/2019 - method refactor; debug logging
                edit; doc string edit
        """

        service_list = []

        # list all installed, service-type service units on the system
        self.ch.executeCommand(self.sysctl + " -a -t service --no-pager list-unit-files")
        output = self.ch.getOutput()

        for line in output:
            try:
                service_list.append(line.split()[0])
            except IndexError:
                pass
            except:
                raise

        if not service_list:
            errmsg = self.ch.getErrorString()
            if errmsg:
                self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return service_list

    def startService(self, service, **kwargs):
        """
        start given service

        @param service:
        @param kwargs:
        @return: started
        @rtype: bool
        @author: Breen Malmberg
        """

        started = True

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            started = False
            return started

        self.ch.executeCommand(self.sysctl + " start " + service)
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            started = False
            errmsg = self.ch.getErrorString()
            self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return started

    def stopService(self, service, **kwargs):
        """
        stop given service

        @param service:
        @param kwargs:
        @return: stopped
        @rtype: bool
        @author: Breen Malmberg
        """

        stopped = True

        if not self.isRunning(service):
            return stopped # nothing to do

        if self.getServiceStatus(service, **kwargs) in self.handsoff:
            stopped = False
            return stopped

        else:
            self.ch.executeCommand(self.sysctl + " stop " + service)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                stopped = False
                errmsg = self.ch.getErrorString()
                self.logdispatcher.log(LogPriority.DEBUG, errmsg)

        return stopped

    def getServiceStatus(self, service, **kwargs):
        """
        return is-enabled status output
        possible return values:

        enabled
        enabled-runtime
        linked
        linked-runtime
        masked
        masked-runtime
        static
        indirect
        disabled
        generated
        transient
        unknown (custom status defined in STONIX; not generated by systemctl)

        @param service:
        @param kwargs:
        @return: status
        @rtype: string
        @author: Breen Malmberg
        """

        status = ""
        known_statuses = ["enabled", "enabled-runtime", "linked", "linked-runtime",
                          "masked", "masked-runtime", "static", "indirect", "disabled",
                          "generated", "transient"]

        self.ch.executeCommand(self.sysctl + " is-enabled " + service)
        output = self.ch.getOutputString()

        try:
            if len(output.split()) == 1:
                status = str(output)
            else:
                status = str(output.split()[0])
        except IndexError:
            pass
        except:
            raise

        if status not in known_statuses:
            status = "unknown"
        elif not isinstance(status, basestring):
            status = "unknown"

        if status in self.handsoff:
            self.logdispatcher.log(LogPriority.DEBUG, "Status of service: " + service + " indicates it is either protected, required or immutable. Will not perform operation on this service!")

        return status

    def getStartCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - start command
        @author: dwalker
        '''
        return self.sysctl + " start " + service

    def getStopCommand(self, service):
        '''
        retrieve the stop command.  Mostly used by event recording
        @return: string - stop command
        @author: dwalker
        '''
        return self.sysctl + " stop " + service

    def getEnableCommand(self, service):
        '''
        retrieve the enable command.  Mostly used by event recording
        @return: string - enable command
        @author: dwalker
        '''
        return self.sysctl + " enable " + service

    def getDisableCommand(self, service):
        '''
        retrieve the start command.  Mostly used by event recording
        @return: string - disable command
        @author: dwalker
        '''
        return self.sysctl + " disable " + service
예제 #31
0
class SecureIPV4(Rule):

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

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

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

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

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

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


        :returns: bool

        '''
        try:
            self.detailedresults = ""
            if self.environ.getosfamily() == "linux":
                self.path = "/etc/sysctl.conf"
                self.tmpPath = "/etc/sysctl.conf.tmp"
                self.original = readFile(self.path, self.logger)
                rep1success = self.reportLinux1()
                rep2success = self.reportLinux2()
            elif self.environ.getosfamily() == "freebsd":
                self.path = "/etc/sysctl.conf"
                self.tmpPath = "/etc/sysctl.conf.tmp"
                self.original = readFile(self.path, self.logger)
                rep1success = self.reportFreebsd1()
                rep2success = self.reportFreebsd2()
            elif self.environ.getostype() == "Mac OS X":
                self.path = "/private/etc/sysctl.conf"
                self.tmpPath = "/private/etc/sysctl.conf.tmp"
                rep1success = True
                rep2success = self.reportMac()
            if rep1success and rep2success:
                self.compliant = True
            else:
                self.compliant = False
        except (KeyboardInterrupt, SystemExit):
            # User initiated exit
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant
        return self.rulesuccess

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


        :returns: bool

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

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

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


        :returns: bool

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

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

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

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


        :returns: compliant

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

        '''

        compliant = True

        try:
            self.editor = None

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

        except Exception:
            raise

        return compliant

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


        :returns: bool

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

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


        :returns: bool

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

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


        :returns: bool

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

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

        self.rulesuccess = success
        return success

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

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

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


        :returns: success

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

        '''

        success = True

        try:

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

        except Exception:
            raise

        return success

    def fixFreebsd(self):
        if not checkPerms(self.path, [0, 0, 0o644], self.logger):
            self.iditerator += 1
            myid = iterate(self.iditerator, self.rulenumber)
            if not setPerms(self.path, [0, 0, 0o644], self.logger,
                            self.statechglogger, myid):
                return False
        if self.networkTuning1.getcurrvalue() or \
                self.networkTuning2.getcurrvalue():
            if self.editor.fixables:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.editor.setEventID(myid)
                if not self.editor.fix():
                    return False
                elif not self.editor.commit():
                    return False
                os.chown(self.path, 0, 0)
                os.chmod(self.path, 0o644)
                resetsecon(self.path)
                cmd = ["/usr/sbin/service", "sysctl", "restart"]
                self.ch.executeCommand(cmd)
                if self.ch.getReturnCode() != 0:
                    self.detailedresults = "Unable to restart sysctl\n"
                    self.logger.log(LogPriority.DEBUG, self.detailedresults)
                    return False
                else:
                    return True
            else:
                return True
예제 #32
0
class ConfigureSpotlight(Rule):
    '''@author: ekkehard j. koch'''

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

    def __init__(self, config, environ, logdispatcher, statechglogger):
        '''
        This rule should normally be a rulekveditor rule, but there has
        recently been an issue discovered with subprocess for this
        particular rule where a paramaterized list for the command doesn't
        behave as would expected which KVADefault class uses when performing
        the defaults read command.  Can change back if this issue is resolved
        with Mac/Python.
        '''
        Rule.__init__(self, config, environ, logdispatcher, statechglogger)
        self.rulenumber = 17
        self.rulename = 'ConfigureSpotlight'
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = False
        self.guidance = []
        self.applicable = {'type': 'white',
                           'os': {'Mac OS X': ['10.12', 'r', '10.14.10']}}

        datatype1 = "bool"
        key1 = "CONFIGURESPOTLIGHT"
        instructions1 = "To disable this configuration set the value of " + \
            "CONFIGURESPOTLIGHT to False."
        default1 = True
        self.ci1 = self.initCi(datatype1, key1, instructions1, default1)

        datatype2 = "bool"
        key2 = "SAFARISEARCH"
        instructions2 = "To disable this configuration set the value of " + \
            "SAFARISEARCH to False."
        default2 = True
        self.ci2 = self.initCi(datatype2, key2, instructions2, default2)

    def report(self):
        try:
            self.detailedresults = ""
            self.slv = {0: {'enabled': '1', 'name': 'APPLICATIONS'},
                        1: {'enabled': '0', 'name':
                            '\"MENU_SPOTLIGHT_SUGGESTIONS\"'},
                        2: {'enabled': '1', 'name': '\"MENU_CONVERSION\"'},
                        3: {'enabled': '1', 'name': '\"MENU_EXPRESSION\"'},
                        4: {'enabled': '1', 'name': '\"MENU_DEFINITION\"'},
                        5: {'enabled': '1', 'name': '\"SYSTEM_PREFS\"'},
                        6: {'enabled': '1', 'name': 'DOCUMENTS'},
                        7: {'enabled': '1', 'name': 'DIRECTORIES'},
                        8: {'enabled': '1', 'name': 'PRESENTATIONS'},
                        9: {'enabled': '1', 'name': 'SPREADSHEETS'},
                        10: {'enabled': '1', 'name': 'PDF'},
                        11: {'enabled': '1', 'name': 'MESSAGES'},
                        12: {'enabled': '1', 'name': 'CONTACT'},
                        13: {'enabled': '1', 'name': '\"EVENT_TODO\"'},
                        14: {'enabled': '1', 'name': 'IMAGES'},
                        15: {'enabled': '1', 'name': 'BOOKMARKS'},
                        16: {'enabled': '1', 'name': 'MUSIC'},
                        17: {'enabled': '1', 'name': 'MOVIES'},
                        18: {'enabled': '1', 'name': 'FONTS'},
                        19: {'enabled': '1', 'name': '\"MENU_OTHER\"'},
                        20: {'enabled': '0', 'name': '\"MENU_WEBSEARCH\"'}}
            self.spotRead = "/usr/bin/defaults read com.apple.Spotlight " + \
                "orderedItems "
            self.spotwrite = "/usr/bin/defaults write com.apple.Spotlight " + \
                "orderedItems "
            self.safRead = "/usr/bin/defaults read com.apple.Safari " + \
                "UniversalSearchEnabled "
            self.safWrite = "/usr/bin/defaults write com.apple.Safari " + \
                "UniversalSearchEnabled "
            compliant = True
            self.slvlook = "("
            self.slvset = "\'("
            for _, v in sorted(self.slv.items()):
                self.slvset += "{\"enabled\"=" + str(v['enabled']) + "; " + \
                    "\"name\"=" + str(v['name']) + ";},"
            self.slvset += ")\';"
            i = 0
            for _, v in sorted(self.slv.items()):
                if i == 20:
                    self.slvlook += "{enabled=" + str(v["enabled"]) + ";" + \
                        "name=" + str(v["name"]) + ";}"
                    break
                else:
                    self.slvlook += "{enabled=" + str(v["enabled"]) + ";" + \
                        "name=" + str(v["name"]) + ";},"
                i += 1
            self.slvlook += ")"
            lookstring = "The regex we are looking for from the defaults " + \
                "read command: " + self.slvlook + "\n"
            self.logdispatch.log(LogPriority.DEBUG, lookstring)
            setstring = "The plist string we will set defaults write " + \
                "command to: " + self.slvset + "\n"
            self.logdispatch.log(LogPriority.DEBUG, setstring)
            self.ch = CommandHelper(self.logdispatch)
            if self.ch.executeCommand(self.spotRead):
                output = self.ch.getOutputString()
                output = re.sub("(\s)", "", output)
                error = self.ch.getErrorString()
                if output:
                    if not re.search(self.slvlook, output):
                        self.detailedresults += "Output for orderedItems " + \
                            "key in com.apple.Spotlight plist isn't correct\n"
                        compliant = False
                elif error:
                    if re.search("does not exist", error):
                        self.detailedresults += "Either com.apple." + \
                            "Spotlight plist doesn't exist or the key " + \
                            "orderedItems doesn't exist\n"
                        compliant = False
            output, error = "", ""
            if self.ch.executeCommand(self.safRead):
                output = self.ch.getOutputString()
                error = self.ch.getErrorString()
                if output:
                    if not re.search("0", output):
                        self.detailedresults += "Output for " + \
                            "UniversalSearchEnabled key in " + \
                            "com.apple.Safari plist isn't correct\n"
                        compliant = False
                elif error:
                    if re.search("does not exist", error):
                        self.detailedresults += "Either com.apple.Safari " + \
                            "plist doesn't exist or the key " + \
                            "UniversalSearchEnabled doesn't exist\n"
            self.compliant = compliant
            if self.compliant:
                self.detailedresults += "ConfigureSpotlight has been run " + \
                    "and is compliant\n"
            else:
                self.detailedresults += "ConfigureSpotlight has been run " + \
                    "and is not compliant\n"
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("report", self.compliant,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.compliant

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

    def fix(self):
        try:
            if not self.ci1.getcurrvalue() and not self.ci2.getcurrvalue():
                return
            self.detailedresults = ""

            success = True
            if self.ch.executeCommand(self.spotRead):
                output = self.ch.getOutputString()
                output = re.sub("(\s)", "", output)
                re.sub
                error = self.ch.getErrorString()
                if output:
                    if not re.search(self.slvlook, output):
                        cmd = self.spotwrite + self.slvset
                        if not self.ch.executeCommand(cmd):
                            self.detailedresults += "Unable to perform " + \
                                "defaults write command for " + \
                                "com.apple.Spotlight\n"
                            success = False
                elif error:
                    if re.search("does not exist", error):
                        cmd = self.spotwrite + self.slvset
                        if not self.ch.executeCommand(cmd):
                            self.detailedresults += "Unable to perform " + \
                                "defaults write command for " + \
                                "com.apple.Safari\n"
                            success = False

            output, error = "", ""
            if self.ch.executeCommand(self.safRead):
                output = self.ch.getOutputString()
                error = self.ch.getErrorString()
                if output:
                    if not re.search("0", output):
                        cmd = self.safWrite + "-bool no"
                        if not self.ch.executeCommand(cmd):
                            self.detailedresults += "Unable to perform " + \
                                "defaults write command for com.apple.Safari\n"
                            success = False
                elif error:
                    if re.search("does not exist", error):
                        cmd = self.safWrite + "-bool no"
                        if not self.ch.executeCommand(cmd):
                            self.detailedresults += "Unable to perform " + \
                                "defaults write command for com.apple.Safari\n"
            self.rulesuccess = success
            if self.rulesuccess:
                self.detailedresults += "ConfigureSpotlight rule ran to " + \
                    "completion successfully\n"
            else:
                self.detailedresults += "ConfigureSpotlight rule did not " + \
                    "run to completion successfully\n"
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.rulesuccess = False
            self.detailedresults += "\n" + traceback.format_exc()
            self.logdispatch.log(LogPriority.ERROR, self.detailedresults)
        self.formatDetailedResults("fix", self.rulesuccess,
                                   self.detailedresults)
        self.logdispatch.log(LogPriority.INFO, self.detailedresults)
        return self.rulesuccess

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

    def undo(self):
        try:
            self.detailedresults = "no undo available"
            self.logger.log(LogPriority.INFO, self.detailedresults)
        except(KeyboardInterrupt, SystemExit):
            raise
        except Exception:
            self.detailedresults = traceback.format_exc()
            self.logger.log(LogPriority.ERROR, self.detailedresults)
예제 #33
0
class SetDefaultUserUmask(Rule):
    """The SetDefaultUserUmask class sets the default user umask to 077. Also
    accepts user input of alternate 027 umask.
    
    For OS X documentation on this can be found at:
    http://support.apple.com/kb/HT2202
    """
    def __init__(self, config, environ, logdispatch, statechglogger):
        """
        Constructor
        """

        Rule.__init__(self, config, environ, logdispatch, statechglogger)
        self.config = config
        self.environ = environ
        self.logger = logdispatch
        self.statechglogger = statechglogger
        self.rulenumber = 48
        self.rulename = 'SetDefaultUserUmask'
        self.formatDetailedResults("initialize")
        self.compliant = False
        self.mandatory = True
        self.sethelptext()
        self.rootrequired = True
        self.guidance = [
            'CIS', 'NSA(2.3.4.4)', 'CCE-3844-8', 'CCE-4227-5', 'CCE-3870-3',
            'CCE-4737-6'
        ]

        # set up which system types this rule will be applicable to
        self.applicable = {
            'type': 'white',
            'family': 'linux',
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        # decide what the default umask value should be, based on osfamily
        if self.environ.getosfamily() == 'darwin':
            self.userumask = "022"
        else:
            self.userumask = "077"
        self.rootumask = "022"

        self.ci = self.initCi("bool",
                              "SetDefaultUserUmask",
                              "To prevent stonix from setting the " + \
                              "default user umask, set the value of " + \
                              "SetDefaultUserUmask to False.",
                              True)

        # init CIs
        user_ci_type = "string"
        user_ci_name = "DEFAULTUSERUMASK"
        user_ci_instructions = "Set the default user umask value. Correct format is " + "a 3-digit, 0-padded integer. This value will determine the default permissions for every file created by non-privileged users."
        self.userUmask = self.initCi(user_ci_type, user_ci_name,
                                     user_ci_instructions, self.userumask)

        root_ci_type = "string"
        root_ci_name = "DEFAULTROOTUMASK"
        root_ci_instructions = "Set the default root umask value. Correct format is a 3-digit, 0-padded integer. Setting this to a value more restrictive than 022 may cause issues on your system. This value will determine the default permissions for every file created by the root user."
        self.rootUmask = self.initCi(root_ci_type, root_ci_name,
                                     root_ci_instructions, self.rootumask)

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

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

        # defaults
        self.detailedresults = ""
        self.ch = CommandHelper(self.logger)

        # set up list of files which need to be checked and configured
        self.rootfiledict = {
            "/root/.bash_profile": False,
            "/root/.bashrc": False,
            "/root/.cshrc": False,
            "/root/.tcshrc": False
        }

        self.userfiledict = {
            "/etc/profile": False,
            "/etc/csh.login": False,
            "/etc/csh.cshrc": False,
            "/etc/bashrc": False,
            "/etc/zshrc": False,
            "/etc/login.conf": False,
            "/etc/bash.bashrc": False,
            "/etc/login.defs": False
        }

        try:

            # decide which report method to run based on osfamily
            if self.environ.getosfamily() == 'darwin':
                self.compliant = self.reportmac()
            else:
                self.compliant = self.reportnix()

        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception as err:
            self.compliant = 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 reportnix(self):
        """
        private method for reporting compliance status of *nix based systems

        :return: configured
        :rtype: bool
        """

        # defaults
        configured = True

        try:

            # check for presence of umask config line in user files
            for item in self.userfiledict:
                if not os.path.exists(item):
                    self.detailedresults += "\nMissing required configuration file: " + str(
                        item)
                    configured = False
                elif self.searchString(
                        '^umask\s*' + str(self.userUmask.getcurrvalue()),
                        item):
                    self.userfiledict[item] = True

            for item in self.userfiledict:
                if os.path.exists(item):
                    if not self.userfiledict[item]:
                        self.detailedresults += "\nFile: " + str(
                            item
                        ) + " has an incorrect user umask configuration."
                        configured = False

            # check for presence of umask config line in root files
            for item in self.rootfiledict:
                if not os.path.exists(item):
                    self.detailedresults += "\nMissing required configuration file: " + str(
                        item)
                    configured = False
                elif self.searchString(
                        '^umask\s*' + str(self.rootUmask.getcurrvalue()),
                        item):
                    self.rootfiledict[item] = True

            for item in self.rootfiledict:
                if os.path.exists(item):
                    if not self.rootfiledict[item]:
                        self.detailedresults += "\nFile: " + str(
                            item
                        ) + " has an incorrect root umask configuration."
                        configured = False

        except Exception:
            raise

        return configured

    def reportmac(self):
        """the system's default user umask value (if set to something other than 022,
        will be stored in the file /var/db/com.apple.xpc.launchd/config/user.plist
        on versions of mac os x equal to or greater than 10.10


        :return: retval

        :rtype: bool
@author: Breen Malmberg

        """

        valid = False
        pathexists = True
        plistpath = "/var/db/com.apple.xpc.launchd/config/user.plist"
        cmd = "/usr/bin/defaults read " + str(plistpath) + " Umask"
        # mac transforms the umask value to a different integer value
        # the algorithm is unknown, but these values are tested and determined to be
        # 022, 027 and 077 respectively
        umaskTrans = {'022': '18', '027': '23', '077': '63'}

        if not os.path.exists(plistpath):
            pathexists = False
        else:
            self.ch.executeCommand(cmd)
            retcode = self.ch.getReturnCode()
            if retcode != 0:
                errstr = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errstr)
            outputstr = self.ch.getOutputString()
            if self.userumask in umaskTrans:
                if re.search(umaskTrans[self.userumask], outputstr,
                             re.IGNORECASE):
                    valid = True
            else:
                valid = False

        retval = bool(valid and pathexists)

        return retval

    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.
        Method to set the default users umask to 077 (or 027 if specified in
        the related config file.


        :return: bool
        @author: Breen Malmberg

        """

        # defaults
        self.detailedresults = ""
        self.iditerator = 0

        try:

            # if the ci is enabled/True, proceed
            if self.ci.getcurrvalue():

                # decide which fix method to run, based on osfamily
                if self.environ.getosfamily() == 'darwin':
                    self.rulesuccess = self.fixmac()
                else:
                    self.rulesuccess = self.fixnix()

            # if the ci is not enabled, or False, report this in
            # detailedresults
            else:
                self.detailedresults = str(self.ci.getkey()) + \
                " was disabled. No action was taken."

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

    def fixmac(self):
        """Canonical way of setting user umask in mac os x 10.10 and later
        reference: https://support.apple.com/en-us/HT201684


        :return: retval

        :rtype: bool
@author: Breen Malmberg
@change: 01/17/2018 - Breen Malmberg - added this method for newer mac os versions

        """

        retval = True

        user_command = "/bin/launchctl config user umask " + str(
            self.userumask)
        umask_conf_file = "/private/var/db/com.apple.xpc.launchd/config"

        if not os.path.exists(umask_conf_file):
            os.makedirs(umask_conf_file, 0o755)

        self.ch.executeCommand(user_command)
        if self.ch.getReturnCode() != 0:
            errstr = self.ch.getErrorString()
            retval = False
            self.detailedresults += "\nFailed to set user umask"
            self.logger.log(LogPriority.DEBUG,
                            "Failed to set user umask\n" + str(errstr))

        return retval

    def fixnix(self):
        """
        private method to apply umask config changes to *nix systems

        :return: success
        :rtype: bool
        """

        # defaults
        success = True

        try:

            # iterate through list of user files
            # append the umask config line to each file
            for item in self.userfiledict:
                if not self.userfiledict[item]:
                    self.configFile(
                        'umask    ' + str(self.userUmask.getcurrvalue()) +
                        "\n", item, 0o644, [0, 0], True)

            # do any of the root umask conf files exist?
            for item in self.rootfiledict:
                if not self.rootfiledict[item]:
                    self.configFile(
                        'umask    ' + str(self.rootUmask.getcurrvalue()) +
                        "\n", item, 0o644, [0, 0], True)

        except Exception:
            raise
        return success

    def searchString(self, searchRE, filepath):
        """
        private method for searching for a given string in a given file

        :param searchRE: 
        :param filepath: 
        :return: retval
        :rtype: bool
        """

        # defaults
        stringfound = False
        noduplicates = True
        entries_found = 0

        try:

            # check if path exists, then open it and read its contents
            if os.path.exists(filepath):
                self.logger.log(LogPriority.DEBUG,
                                "\nFound configuration file: " + str(filepath))
                f = open(filepath, 'r')
                contentlines = f.readlines()
                f.close()

                self.logger.log(
                    LogPriority.DEBUG,
                    "Checking " + str(filepath) + " for configuration...\n")

                # search for the searchRE; if found, set return val to True
                for line in contentlines:
                    if re.search(searchRE, line, re.IGNORECASE):
                        stringfound = True
                        entries_found += 1
                        self.logger.log(
                            LogPriority.DEBUG,
                            "Found correct configuration in file: " +
                            str(filepath))

                if not stringfound:
                    self.logger.log(
                        LogPriority.DEBUG, "File: " + str(filepath) +
                        " did NOT contain correct config.")
                if entries_found > 1:
                    duplicates = entries_found - 1
                    self.detailedresults += "\n" + str(
                        duplicates
                    ) + " duplicate entries found in file: " + str(filepath)
                    self.logger.log(
                        LogPriority.DEBUG,
                        str(duplicates) +
                        " duplicate entries found in file: " + str(filepath))
                    noduplicates = False
            else:
                self.logger.log(
                    LogPriority.DEBUG,
                    "Specified file: " + str(filepath) + " does not exist.")

        except Exception:
            raise

        retval = stringfound and noduplicates

        return retval

    def configFile(self, configString, filepath, perms, owner, create=False):
        """private method for adding a configString to a given filepath

        :param configString: 
        :param filepath: 
        :param perms: 
        :param owner: 
        :param create:  (Default value = False)

        """

        umask_entries = 0

        try:

            if os.path.exists(filepath):
                tmpfile = filepath + '.stonixtmp'

                # open the file, read its contents
                f = open(filepath, 'r')
                contentlines = f.readlines()
                f.close()

                for line in contentlines:
                    self.logger.log(LogPriority.DEBUG,
                                    "Searching new line for umask entry...")
                    if re.search('^umask', line, re.IGNORECASE):
                        self.logger.log(
                            LogPriority.DEBUG,
                            "Found umask config line. Is it a duplicate?")
                        umask_entries += 1
                        if umask_entries > 1:
                            self.logger.log(
                                LogPriority.DEBUG,
                                "Yes. It is a duplicate. umask_entries = " +
                                str(umask_entries))
                            self.logger.log(LogPriority.DEBUG,
                                            "Deleting duplicate line: " + line)
                            contentlines.remove(line)
                        else:
                            self.logger.log(
                                LogPriority.DEBUG,
                                "No. It is not a duplicate. umask_entries = " +
                                str(umask_entries))
                            self.logger.log(
                                LogPriority.DEBUG,
                                "Replacing existing umask config line with the provided one..."
                            )
                            contentlines = [
                                c.replace(line, configString)
                                for c in contentlines
                            ]

                if umask_entries == 0:
                    # append the config string
                    contentlines.append('\n' + configString)

                # open temporary file, write new contents
                tf = open(tmpfile, 'w')
                tf.writelines(contentlines)
                tf.close()

                # create undo id and dict and save change record
                event = {'eventtype': 'conf', 'filepath': filepath}
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.statechglogger.recordchgevent(myid, event)
                self.statechglogger.recordfilechange(tmpfile, filepath, myid)

                # set permission and ownership on rewritten file
                os.rename(tmpfile, filepath)
                os.chmod(filepath, perms)
                os.chown(filepath, owner[0], owner[1])
            elif create:
                f = open(filepath, 'w')
                f.write(configString)
                f.close()
                event = {'eventtype': 'creation', 'filepath': filepath}
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                self.statechglogger.recordchgevent(myid, event)
                os.chmod(filepath, perms)
                os.chown(filepath, owner[0], owner[1])

        except Exception:
            raise

    def appendDetailedResults(self, message):
        """
        append given message to self.detailedresults

        :param message: string; ?
        """

        self.detailedresults += '\n' + str(message) + '\n'

    def removeStonixUMASKCodeFromFile(self, filelist=[]):
        """
        Removes the STONIX sets default umask block from list
        of files presented

        :param filelist:  (Default value = [])
        :return: success
        :rtype: bool
        """

        success = True
        bakFile = ""

        for myfile in filelist:
            if os.path.exists(myfile):
                removedBlockSuccessfully = False

                try:
                    rfh = open(myfile, "r")
                except Exception:
                    self.appendDetailedResults("File: " + \
                    str(myfile) + " - Open For Reading Failed - " + \
                    str(traceback.format_exc()))
                else:
                    try:
                        bakFile = "/tmp/removeUMASK-" + \
                        os.path.basename(myfile) + ".bak"
                        wfh = open(bakFile, "w")
                    except Exception:
                        self.logdispatch.log(
                            LogPriority.ERROR,
                            "Failed to create backup for umask file")
                    else:
                        startOfUMASKBlock = False
                        endOfUMASKBlock = False
                        for line in rfh:
                            if "# This block added by STONIX sets default umask" in line:
                                startOfUMASKBlock = True
                            if startOfUMASKBlock and not endOfUMASKBlock:
                                self.logdispatch.log(LogPriority.DEBUG,
                                                     "File: " + str(myfile) + \
                                                     "; Removing Line: '" + \
                                                     line.strip() + "'")
                            else:
                                wfh.write(line)
                            if startOfUMASKBlock and "# End STONIX default umask block." in line:
                                endOfUMASKBlock = True

                        if startOfUMASKBlock and endOfUMASKBlock:
                            removedBlockSuccessfully = True
                        rfh.close()
                        wfh.close()
#####
# Using this method as os.rename (which is used in a file "move") is not
# consistent across platforms, and this is.
                if removedBlockSuccessfully:
                    ### delete myfile
                    os.unlink(myfile)
                    ### copy back to real
                    shutil.copyfile(bakFile, myfile)
                    self.appendDetailedResults("File: " + str(myfile) + \
                        " - Removed STONIX sets default umask block!")
                else:
                    self.appendDetailedResults("File: " + str(myfile) + \
                        " - NO STONIX sets default umask block found in!")
### delete bak
                os.unlink(bakFile)
            else:
                self.appendDetailedResults("File: " + str(myfile) + \
                " does not exist.")
        return success
예제 #34
0
class NoCoreDumps(Rule):
    '''classdocs'''
    def __init__(self, config, environ, logger, statechglogger):
        """

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

        Rule.__init__(self, config, environ, logger, statechglogger)
        self.logger = logger
        self.rulenumber = 49
        self.rulename = "NoCoreDumps"
        self.formatDetailedResults("initialize")
        self.mandatory = True
        self.guidance = ["NSA 2.2.4.2"]
        self.applicable = {
            'type': 'white',
            'family': ['linux'],
            'os': {
                'Mac OS X': ['10.15', 'r', '10.15.10']
            }
        }

        datatype = 'bool'
        key = 'NOCOREDUMPS'
        instructions = "To prevent the disabling of core dumps on your system, set the value of NOCOREDUMPS to False."
        default = True
        self.ci = self.initCi(datatype, key, instructions, default)
        self.sethelptext()

    def report(self):
        '''Main parent report method that calls the sub report methods report1
        and report2
        
        @author: Derek Walker


        :returns: self.compliant

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

        '''

        self.detailedresults = ""
        self.compliant = True
        self.ch = CommandHelper(self.logger)
        osfam = self.environ.getosfamily()

        try:
            if osfam == "linux":
                if not self.reportLinux():
                    self.compliant = False
            elif osfam == "darwin":
                if not self.reportMac():
                    self.compliant = False

        except (KeyboardInterrupt, SystemExit):
            raise
        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 reportMac(self):
        '''run report actions for mac systems


        :returns: compliant

        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 1/10/2017 - added doc string; default return var init;
        try/except; logging; minor refactor

        '''

        self.logger.log(
            LogPriority.DEBUG,
            "System has been detected as Mac OS X, running reportMac()...")
        compliant = True

        self.ch.executeCommand("/usr/bin/launchctl limit core")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            self.detailedresults += "\nFailed to run launchctl command to get current value of core dumps configuration"
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)
        else:
            output = self.ch.getOutputString()
            if output:
                if not re.search("0", output):
                    compliant = False
            else:
                compliant = False

        return compliant

    def reportLinux(self):
        '''Sub report method 1 that searches the /etc/security/limits.conf file
        for the following line "* hard core 0"


        :returns: compliant

        :rtype: bool
@author: ???

        '''

        compliant = True

        if not self.check_security_limits():
            compliant = False

        if not self.check_sysctl():
            compliant = False

        return compliant

    def check_security_limits(self):
        '''check the limits.conf file for the configuration line
        * hard core 0


        :returns: compliant

        :rtype: bool
@author: ???

        '''

        compliant = True
        securitylimits = "/etc/security/limits.conf"
        coresetting = "(^\*)\s+hard\s+core\s+0?"

        if os.path.exists(securitylimits):
            if not checkPerms(securitylimits, [0, 0, 0o644], self.logger):
                self.detailedresults += "Permissions incorrect on " + securitylimits + "\n"
                compliant = False
            contents = readFile(securitylimits, self.logger)
            if contents:
                found = False
                for line in contents:
                    if re.search(coresetting, line.strip()):
                        found = True
                if not found:
                    self.detailedresults += "Correct configuration line * hard core 0 " + \
                        "not found in /etc/security/limits.conf\n"
                    compliant = False
        else:
            self.detailedresults += securitylimits + " file doesn't exist\n"

        return compliant

    def check_sysctl(self):
        '''check the systemd configuration setting fs.suid_dumpable
        for value of 0


        :returns: compliant

        :rtype: bool
@author: ???

        '''

        compliant = True

        self.ch.executeCommand("/sbin/sysctl fs.suid_dumpable")
        retcode = self.ch.getReturnCode()

        if retcode != 0:
            self.detailedresults += "Failed to get value of core dumps configuration with sysctl command\n"
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)
            compliant = False
        else:
            output = self.ch.getOutputString()
            if output.strip() != "fs.suid_dumpable = 0":
                compliant = False
                self.detailedresults += "Core dumps are currently enabled\n"

        if not os.path.exists("/etc/sysctl.conf"):
            compliant = False
            self.detailedresults += "/etc/sysctl.conf file doesn't exist\n"
        else:
            self.editor = KVEditorStonix(self.statechglogger, self.logger,
                                         "conf", "/etc/sysctl.conf",
                                         "/etc/sysctl.conf.tmp",
                                         {"fs.suid_dumpable": "0"}, "present",
                                         "openeq")
            if not self.editor.report():
                compliant = False
                self.detailedresults += "Did not find correct contents inside /etc/sysctl.conf\n"
        return compliant

    def fix(self):
        '''parent fix method which calls os-specific private fix methods
        
        @author: Derek Walker


        :returns: self.rulesuccess

        :rtype: bool

        '''

        self.iditerator = 0
        self.detailedresults = ""
        self.rulesuccess = True
        osfam = self.environ.getosfamily()

        try:

            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)

            if osfam == "linux":
                if not self.fixLinux():
                    self.rulesuccess = False

            elif osfam == "darwin":
                if not self.fixMac():
                    self.rulesuccess = False

        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

    def fixLinux(self):
        '''perform linux-specific configuration changes


        :returns: success

        :rtype: bool
@author: ???

        '''

        success = True

        if not self.fix_security_limits():
            success = False

        if not self.fix_sysctl():
            success = False

        return success

    def fix_sysctl(self):
        '''set the systemd configuration setting fs.suid_dumpable
        to 0


        :returns: success

        :rtype: bool
@author: ???

        '''

        success = True

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

        # using sysctl -w command
        self.logger.log(LogPriority.DEBUG,
                        "Configuring /etc/sysctl fs.suid_dumpable directive")
        self.ch.executeCommand("/sbin/sysctl -w fs.suid_dumpable=0")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            self.detailedresults += "Failed to set core dumps variable suid_dumpable to 0\n"
            errmsg = self.ch.getErrorString()
            self.logger.log(LogPriority.DEBUG, errmsg)
        else:
            self.logger.log(LogPriority.DEBUG,
                            "Re-reading sysctl configuration from files")
            self.ch.executeCommand("/sbin/sysctl -q -e -p")
            retcode2 = self.ch.getReturnCode()
            if retcode2 != 0:
                success = False
                self.detailedresults += "Failed to load new sysctl configuration from config file\n"
                errmsg2 = self.ch.getErrorString()
                self.logger.log(LogPriority.DEBUG, errmsg2)
            else:
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                command = "/sbin/sysctl -w fs.suid_dumpable=1"
                event = {"eventtype": "commandstring", "command": command}
                self.statechglogger.recordchgevent(myid, event)
        return success

    def fix_security_limits(self):
        '''ensure the limits.conf file contains the configuration
        setting * hard core 0


        :returns: succcess

        :rtype: bool
@author: ???

        '''

        success = True
        path1 = "/etc/security/limits.conf"
        lookfor1 = "(^\*)\s+hard\s+core\s+0?"
        created = False
        if not os.path.exists(path1):
            if not createFile(path1, self.logger):
                success = False
                self.detailedresults += "Unable to create " + path1 + " file\n"
            else:
                created = True
                self.iditerator += 1
                myid = iterate(self.iditerator, self.rulenumber)
                event = {"eventtype": "creation", "filepath": "path1"}
                self.statechglogger.recordchgevent(myid, event)
        if os.path.exists(path1):
            if not checkPerms(path1, [0, 0, 0o644], self.logger):
                if not created:
                    self.iditerator += 1
                    myid = iterate(self.iditerator, self.rulenumber)
                    if not setPerms(path1, [0, 0, 0o644], self.logger,
                                    self.statechglogger, myid):
                        success = False
                        self.detailedresults += "Unable to correct permissions on " + path1 + "\n"
            contents = readFile(path1, self.logger)
            found = False
            tempstring = ""
            if contents:
                for line in contents:
                    if re.search(lookfor1, line.strip()):
                        found = True
                    else:
                        tempstring += line
            else:
                found = False
            if not found:
                tempstring += "* hard core 0\n"
                tempfile = path1 + ".stonixtmp"
                if not writeFile(tempfile, tempstring, self.logger):
                    success = False
                    self.detailedresults += "Unable to write contents to " + path1 + "\n"
                else:
                    if not created:
                        self.iditerator += 1
                        myid = iterate(self.iditerator, self.rulenumber)
                        event = {"eventtype": "conf", "filepath": path1}
                        self.statechglogger.recordchgevent(myid, event)
                        self.statechglogger.recordfilechange(
                            path1, tempfile, myid)
                    os.rename(tempfile, path1)
                    setPerms(path1, [0, 0, 0o644], self.logger)
                    resetsecon(path1)
        return success

    def fixMac(self):
        '''run fix actions for Mac OS X systems


        :returns: success

        :rtype: bool
@author: Derek Walker
@change: Breen Malmberg - 1/10/2017 - added doc string; default return var init;
        try/except; fixed command being used to restart sysctl on mac; logging

        '''

        self.logger.log(LogPriority.DEBUG,
                        "System detected as Mac OS X. Running fixMac()...")
        success = True
        self.logger.log(LogPriority.DEBUG,
                        "Configuring launchctl limit core directive")
        self.ch.executeCommand("/usr/bin/launchctl limit core 0 0")
        retcode = self.ch.getReturnCode()
        if retcode != 0:
            success = False
            errmsg = self.ch.getErrorString()
            self.detailedresults += "\nFailed to run launchctl command to configure core dumps"
            self.logger.log(LogPriority.DEBUG, errmsg)
        return success
예제 #35
0
class Zypper(object):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return self.install

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

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

        return self.remove