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 report(self): """Verify the ownership and permissions of the boot loader config file to be root:root and 600 - respectively Return True if they are both set to these respective values Return False if one or more are not set to the respective owner and permissions values :return: self.compliant - boolean; True if system is compliant, False if not """ # defaults self.compliant = True self.detailedresults = "" try: for path in self.bootloaderpathlist: if os.path.exists(path): perms = getOctalPerms(path) stat_info = os.stat(path) uid = stat_info.st_uid gid = stat_info.st_gid if uid != 0: self.compliant = False self.detailedresults += "\nOwner on file " + str( path) + " is incorrect" self.logger.log( LogPriority.DEBUG, "Owner on file " + str(path) + " is incorrect") if gid != 0: self.compliant = False self.detailedresults += "\nGroup on file " + str( path) + " is incorrect" self.logger.log( LogPriority.DEBUG, "Group on file " + str(path) + " is incorrect") if perms != 600: self.compliant = False self.detailedresults += "\nPermissions on file " + str( path) + " are incorrect" self.logger.log( LogPriority.DEBUG, "Permissions on file " + str(path) + " are incorrect") except (KeyboardInterrupt, SystemExit): raise except Exception: self.rulesuccess = False self.compliant = False self.detailedresults = traceback.format_exc() self.logger.log(LogPriority.ERROR, self.detailedresults) self.formatDetailedResults("report", self.compliant, self.detailedresults) self.logdispatch.log(LogPriority.INFO, self.detailedresults) return self.compliant
def 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 report(self): """check status of private ssh keys (whether they are encrypted with passwords or not) :returns: self.compliant - boolean; True if compliant, False if not compliant """ searchterm = "Proc-Type:" self.searchdirs = [] keylist = [] self.keydict = {} self.compliant = True self.detailedresults = "" self.ch = CommandHelper(self.logger) try: self.logger.log(LogPriority.DEBUG, "Getting list of user home directories...") self.searchdirs = self.get_search_dirs() self.logger.log(LogPriority.DEBUG, "Getting list of ssh keys...") keylist = self.get_key_list(self.searchdirs) if keylist: self.logger.log(LogPriority.DEBUG, "Searching list of ssh keys...") for key in keylist: self.keydict[key] = False f = open(key, "r") contentlines = f.readlines() f.close() for line in contentlines: if re.search(searchterm, line): self.keydict[key] = True for key in self.keydict: if not self.keydict[key]: self.compliant = False self.detailedresults += "\nThe SSH key: " + str(key) + " was made without a password!" if getOctalPerms(key) != 600: self.compliant = False self.detailedresults += "\nThe SSH key: " + str(key) + " has incorrect permissions" if self.compliant: self.detailedresults += "\nAll SSH keys on this system are encrypted" else: self.detailedresults += "\nNo SSH keys were found on this system." if not self.compliant: self.detailedresults += "\n\nThis rule's fix only changes permissions on insecure keys. We cannot fix keys which were made without a password." except (KeyboardInterrupt, SystemExit): raise except Exception: self.compliant = False self.detailedresults = str(traceback.format_exc()) self.logger.log(LogPriority.ERROR, self.detailedresults) self.formatDetailedResults("report", self.compliant, self.detailedresults) return self.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 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 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 getPerms(self, path): startperms = getOwnership(path) octalInt = getOctalPerms(path) octPerms = int(str(octalInt), 8) startperms.append(octPerms) self.permsdict[path] = startperms
def reportLinux(self): """Check for STONIX Cron job entries in the Linux crontab :returns: retval :rtype: bool @author: Breen Malmberg """ retval = True self.cronfileexists = True self.reportjob = False self.fixjob = False self.userjob = True stonixreportjob = ' root nice -n 19 ' + str( self.stonixpath) + '/stonix.py -cr' stonixfixjob = ' root nice -n 19 ' + str( self.stonixpath) + '/stonix.py -cdf' # check for existence of system crontab file if not os.path.exists(self.cronfilelocation): self.cronfileexists = False self.detailedresults += '\nSystem crontab file does not exist' else: # check for STONIX cron job entries contents = self.getFileContents(self.cronfilelocation) if not contents: self.detailedresults += '\nSystem crontab file was empty' else: # report entry for line in contents: self.logger.log( LogPriority.DEBUG, "Comparing line:\n" + str(line) + "to stonixreportjob:\n" + str(stonixreportjob)) if re.search(stonixreportjob, line, re.IGNORECASE): self.logger.log(LogPriority.DEBUG, "REPORT JOB FOUND") self.reportjob = True else: self.logger.log(LogPriority.DEBUG, "Lines do NOT match") if not self.reportjob: self.detailedresults += '\nSTONIX report job not found\n' else: self.detailedresults += '\nSTONIX report job found\n' self.logger.log( LogPriority.DEBUG, "After report entry check: reportjob is " + str(self.reportjob)) # fix entry for line in contents: self.logger.log( LogPriority.DEBUG, "Comparing line:\n" + str(line) + "to stonixfixjob:\n" + str(stonixfixjob)) if re.search(stonixfixjob, line, re.IGNORECASE): self.logger.log(LogPriority.DEBUG, "FIX JOB FOUND") self.fixjob = True else: self.logger.log(LogPriority.DEBUG, "Lines do NOT match") if not self.fixjob: self.detailedresults += '\nSTONIX fix job not found\n' else: self.detailedresults += '\nSTONIX fix job found\n' self.logger.log( LogPriority.DEBUG, "After fix entry check: fixjob is " + str(self.fixjob)) self.logger.log( LogPriority.DEBUG, "Before permissions check: retval is " + str(retval)) # check perms of system crontab crontabperms = getOctalPerms(self.cronfilelocation) if crontabperms != 600: self.logger.log( LogPriority.DEBUG, "crontab perms should be 600. Got: " + str(crontabperms)) self.detailedresults += "\nIncorrect permissions detected on system crontab file" retval = False self.logger.log(LogPriority.DEBUG, "Before user script checks: retval is " + str(retval)) # check user script if not os.path.exists(self.userscriptfile): self.userjob = False self.detailedresults += '\nSTONIX user script not found' else: self.detailedresults += '\nSTONIX user script found' contents = self.getFileContents(self.userscriptfile) userjobline = "os.system(stonixscriptpath + ' -c" + self.usermode + "')" stripped_contents = list(map(str.strip, contents)) if userjobline not in stripped_contents: self.userjob = False self.detailedresults += '\nSTONIX user script has incorrect contents' else: self.detailedresults += '\nSTONIX user script has correct contents' # check perms on user script file userscriptperms = getOctalPerms(self.userscriptfile) if userscriptperms != 755: self.detailedresults += "\nIncorrect permissions detected on user script file: " + str( self.userscriptfile) self.logger.log(LogPriority.DEBUG, "Before Final Checks: retval is " + str(retval)) if not self.cronfileexists: retval = False if not self.reportjob: retval = False if not self.fixjob: retval = False if not self.userjob: retval = False return retval
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 report(self): '''check to see if user account databases have the correct (secure) permissions and ownership set. self.compliant, self.detailed results and self.currstate properties are updated to reflect the system status. self.rulesuccess will be updated if the rule does not succeed. :returns: bool @author bemalmbe ''' #defaults retval = True try: self.detailedresults = "" try: for item in self.file644: if os.path.exists(item): if getOctalPerms(item) != 644: retval = False self.detailedresults += item + " does not have " + \ "the correct permissions\n" for item in self.file400: if os.path.exists(item): if getOctalPerms(item) != 400: retval = False self.detailedresults += item + " does not have " + \ "the correct permissions\n" for item in self.fileall: if os.path.exists(item): owner = os.stat(item).st_uid group = os.stat(item).st_gid if owner != 0: retval = False self.detailedresults += item + " is not owned " + \ "by user \"root\"\n" if group != 0: retval = False self.detailedresults += item + " is not owned " + \ "by group \"root\"\n" except (OSError): self.detailedresults = traceback.format_exc() self.logger.log(LogPriority.DEBUG, self.detailedresults) if retval: self.compliant = True else: self.compliant = False except (KeyboardInterrupt, SystemExit): # User initiated exit raise except Exception as err: self.rulesuccess = False self.detailedresults = self.detailedresults + "\n" + str(err) + \ " - " + str(traceback.format_exc()) self.logdispatch.log(LogPriority.ERROR, self.detailedresults) self.formatDetailedResults("report", self.compliant, self.detailedresults) self.logdispatch.log(LogPriority.INFO, self.detailedresults) return self.compliant
def report(self): '''determine whether the xinetd configuration file contains the correct configuration settings determine whether the xinetd configuration file has the correct permissions set determine whether the xinetd configuration file has the correct ownership set :returns: self.compliant :rtype: bool @author: Breen Malmberg ''' self.detailedresults = "" # UPDATE THIS SECTION IF THE CONSTANTS BEING USED IN THIS CLASS CHANGE if not self.checkConsts(self.constlist): self.compliant = False self.detailedresults += "\nThis rule requires that the following constants, in localize.py, be defined and not None: XINETDALLOW" self.formatDetailedResults("report", self.compliant, self.detailedresults) return self.compliant self.logger.log( LogPriority.DEBUG, "inside report() method for XinetdAccessControl class") self.compliant = True self.detailedresults = "" try: self.logger.log( LogPriority.DEBUG, "checking if configuration " + "file exists at expected location " + str(self.confpath)) if os.path.exists(self.confpath): self.logger.log( LogPriority.DEBUG, "configuration file " + "found; checking for correct configuration") if not self.findopt(self.confpath, self.fullopt): self.compliant = False self.detailedresults += 'correct configuration not ' + \ 'found in ' + str(self.confpath) + '\n' perms = getOctalPerms(self.confpath) self.logger.log( LogPriority.DEBUG, "checking configuration " + "file for correct permissions") self.logger.log( LogPriority.DEBUG, "required perms: 644; current perms: " + str(perms)) if perms != 600: self.compliant = False self.detailedresults += 'permissions not set ' + \ 'correctly on ' + str(self.confpath) + '\n' ownership = getOwnership(self.confpath) self.logger.log( LogPriority.DEBUG, "checking configuration " + "file for correct ownership") if ownership != [0, 0]: self.compliant = False self.detailedresults += 'ownership not set correctly on ' + \ str(self.confpath) + '\n' else: self.detailedresults += 'configuration file does not exist; ' + \ 'xinetd not installed\n' except (KeyboardInterrupt, SystemExit): raise except Exception as err: self.rulesuccess = False self.detailedresults = self.detailedresults + "\n" + str(err) + \ " - " + str(traceback.format_exc()) self.logdispatch.log(LogPriority.ERROR, self.detailedresults) self.formatDetailedResults("report", self.compliant, self.detailedresults) self.logdispatch.log(LogPriority.INFO, self.detailedresults) self.logger.log( LogPriority.DEBUG, "finished running report() " + "method for XinetdAccessControl class\nreturning " + "self.compliant=" + str(self.compliant)) return self.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