def validate_options(self, optionDict): users = ' '.join(tcs_utils.splitNaturally(optionDict['allowUsers'])) groups = ' '.join(tcs_utils.splitNaturally(optionDict['allowGroups'])) if users: self.settings.append(['AllowUsers', users, self._isEqualTo]) if groups: self.settings.append(['AllowGroups', groups, self._isEqualTo])
def validate_input(self, optionDict=None): """ Validate optionDict has required args, within acceptable ranges """ self.__pass_min = self.validate_argument('passwordAgingMindays', optionDict) self.__pass_max = self.validate_argument('passwordAgingMaxdays', optionDict) self.__pass_warn = self.validate_argument('passwordAgingExpireWarning', optionDict) self.__pass_inact = self.validate_argument('passwordAgingInvalidate', optionDict) if optionDict['exemptSystemAccounts'] == '1': self.__exemptSystemAccounts = sb_utils.acctmgt.users.local_SystemUsers( ) else: self.__exemptSystemAccounts = [] self.__exemptSpecificAccounts = tcs_utils.splitNaturally( optionDict['exemptSpecificAccounts']) flag = 0 if self.__pass_min < 1 or self.__pass_max < 1 or \ self.__pass_inact < 0 or self.__pass_warn < 1: flag = 1 if self.__pass_max < self.__pass_min: flag = 1 if flag == 1: msg = 'Invalid option arg provided' self.logger.error(self.module_name, 'Scan Error: ' + msg) raise tcs_utils.ScanError('%s %s' % (self.module_name, msg))
def check_ownership(self, pathname, statinfo, attribs): msg = "" change_rec = {} skip = False what_changed = [] instead_of = [] ifCurrentUname = None ifCurrentUID = None allowedUnames = [] allowedUIDs = [] ifCurrentGname = None ifCurrentGID = None allowedGnames = [] allowedGIDs = [] if os.path.islink(pathname): link_preface = "Link chasing -> " else: link_preface = "" #extract pieces try: if 'owner' in attribs: userList = attribs['owner'] if type(userList) == type(1): userList = "%d" % userList userList = tcs_utils.splitNaturally(userList, wordAdditions="<>*-_", whitespaceAdditions=",", uniq=True) try: idx = userList.index('<SYSTEM>') userList = userList [0:idx] + sb_utils.acctmgt.users.local_SystemUsers() + userList[idx+1:] userList = tcs_utils.splitNaturally(userList, wordAdditions="<>*-_", whitespaceAdditions=",", uniq=True) except ValueError, e: pass for entry in userList: try: allowedUIDs.append(self._map_uid_uname(entry)['uid']) allowedUnames.append(entry) except KeyError,e: msg = "allowed username '%s' not found in local user list - ignoring" % entry self.logger.warn('sb_utils.file.fileperms.check_ownership',msg) # ok, user was given, do we *have* any acceptable entries? If not punt without doing anything if not allowedUnames: msg = "No valid unames found for acceptable file ownership - skipping %s" % pathname raise tcs_utils.ActionError(msg)
def checkGnames(self,passedGnames): self._allowed_gnames = [] for gname in tcs_utils.splitNaturally(passedGnames): try: grp.getgrnam(gname) self._allowed_gnames.append(gname) except KeyError,e: msg = "Group '%s' doesn't exist, not including on allowed list" % gname
def checkUnames(self, passedUnames): self._allowed_unames = [] for uname in tcs_utils.splitNaturally(passedUnames): try: pwd.getpwnam(uname) self._allowed_unames.append(uname) except KeyError,e: msg = "User '%s' doesn't exist, not including on allowed list" % uname
def get_shells(self): # Read /etc/shells try: shells = tcs_utils.splitNaturally(open(self.__shells).read(), wordAdditions="/-_") except Exception, err: msg = "Unable to read '/etc/shells' - may need to use 'Allowed Shells in /etc/shells' Module - %s" % str( err) self.logger.notice(self.module_name, 'Scan Failed: ' + msg) raise tcs_utils.ManualActionReqd('%s %s' % (self.module_name, msg))
def addShadow(self, nameList, listType): names = tcs_utils.splitNaturally(nameList, wordAdditions="<>*-_", whitespaceAdditions=",", uniq=True) if 'shadow' not in names: names.append('shadow') nameList = ' '.join(names) msg = "SUSE/openSUSE OS detected, adding 'shadow' to the approved %s users" % listType self.logger.notice(self.module_name, msg) return nameList
def validate_input(self, optionDict): trueFalse = {'1': True, 'True': True, '0': False, 'False': False} try: self.rpmsToIgnore = set( tcs_utils.splitNaturally(optionDict['packageExemptions'], wordAdditions='-.')) self.filesToIgnore = optionDict['fileExemptions'].splitlines() self.ignoreRootRoot000 = trueFalse[optionDict['honorChangesBySB']] except ValueError: msg = "Invalid option value -> '%s'" % optionDict raise tcs_utils.ScanError('%s %s' % (self.module_name, msg))
def validate_input(self, optionDict): """ Validate option which must be min, max, warn, inactive in an array (i.e., [7,90,7,1]) """ # RMS - solaris locks account on expiration - not allowing a grace period of inactivity like linux # - so we're 'creating' our own grace period by adding INACT to MAX, and # - use this value for pass_inact self.__pass_min = self.validate_argument('passwordAgingMindays', optionDict) self.__pass_max = self.validate_argument('passwordAgingMaxdays', optionDict) self.__pass_warn = self.validate_argument('passwordAgingExpireWarning', optionDict) self.__pass_inact = self.validate_argument( 'passwordAgingInvalidate', optionDict) + self.__pass_max if optionDict['exemptSystemAccounts'] == '1': self.__exemptSystemAccounts = sb_utils.acctmgt.users.local_SystemUsers( ) else: self.__exemptSystemAccounts = [] self.__exemptSpecificAccounts = tcs_utils.splitNaturally( optionDict['exemptSpecificAccounts']) flag = 0 if self.__pass_min < 1 or self.__pass_max < 1 or \ self.__pass_inact < 0 or self.__pass_warn < 1: flag = 1 if self.__pass_max < self.__pass_min: flag = 1 if flag == 1: msg = 'Invalid option arg provided' self.logger.error(self.module_name, 'Scan Error: ' + msg) raise tcs_utils.ScanError('%s %s' % (self.module_name, msg))
def checkContent(self, action, fileName, requiredChanges): messages = [] changes = {fileName: {}} sb_utils.file.exclusion.exlist() # if 'content' is empty, then skip checking the actual file contents and move on to permissions linesLeft = [] linesMissing = [] if requiredChanges['content']: currentLines = [] try: currentLines = open(fileName, 'r').read().splitlines() except Exception, err: msg = "Unable to open file for analysis (%s)." % str(err) self.logger.error(self.module_name, 'Scan Error: ' + msg) raise tcs_utils.ScanError('%s %s' % (self.module_name, msg)) contents = ' '.join(requiredChanges['content'].splitlines()) lexer = shlex.shlex(contents) lexer.whitespace += "," allowedShells = tcs_utils.splitNaturally( requiredChanges['content'], wordAdditions="/-_") requiredSet = set(allowedShells) currentSet = set(currentLines) # if we have anything in currentFile after removing allowedLines, then we've got a problem linesLeft = currentSet - requiredSet linesMissing = requiredSet - currentSet if linesLeft: messages.append( "%s contains entries not on the approved list" % self.__target_file) for entry in linesLeft: msg = "Found '%s' in '%s' " % (entry, fileName) self.logger.notice(self.module_name, "Scan Fail: " + msg) if linesMissing: messages.append( "%s is missing required entries from the approved list" % self.__target_file) for entry in linesMissing: msg = "Missing '%s' from '%s' " % (entry, fileName) self.logger.notice(self.module_name, "Scan Fail: " + msg) if not linesLeft and not linesMissing: msg = "All required lines present in '%s'" % (fileName) self.logger.notice(self.module_name, msg) # In either case, we want to have /etc/shells *match* the approved list. # so for apply if we don't match, simply write out the approved list and # pass the old one back as a list to be restored. No need for a patch file # *but* we our undo will be able to handle a patch set... if action in ['apply', 'undo'] and (linesLeft or linesMissing): try: open(fileName, "w").writelines( [entry + "\n" for entry in requiredSet]) except OSError: msg = "Unable to write '%s' with required lines." % fileName self.logger.error(self.module_name, 'Apply Error: ' + msg) raise tcs_utils.ActionError('%s %s' % (self.module_name, msg))
msg = "allowed username '%s' not found in local user list - ignoring" % entry self.logger.warn('sb_utils.file.fileperms.check_ownership',msg) # ok, user was given, do we *have* any acceptable entries? If not punt without doing anything if not allowedUnames: msg = "No valid unames found for acceptable file ownership - skipping %s" % pathname raise tcs_utils.ActionError(msg) if 'if_user_is' in attribs: ifCurrentUname = attribs['if_user_is'] ifCurrentUID = self._map_uid_uname(ifCurrentUname)['uid'] if 'group' in attribs: groupList = attribs['group'] if type(groupList) == type(1): groupList = "%d" % groupList groupList = tcs_utils.splitNaturally(groupList, wordAdditions="<>*-_", whitespaceAdditions=",", uniq=True) try: idx = groupList.index('<SYSTEM>') groupList = groupList [0:idx] + sb_utils.acctmgt.users.local_SystemGroups() + groupList[idx+1:] groupList = tcs_utils.splitNaturally(groupList, wordAdditions="<>*-_", whitespaceAdditions=",", uniq=True) except ValueError, e: pass for entry in groupList: try: allowedGIDs.append(self._map_gid_gname(entry)['gid']) allowedGnames.append(entry) except KeyError,e: msg = "allowed groupname '%s' not found in local group list - ignoring" % entry self.logger.warn('sb_utils.file.fileperms.check_ownership',msg) if not allowedGnames:
def _restrictCiphers(self, param, cipherLine, restrictions): """ cipherLine holds the settings from the 'Ciphers' field restrictions is a dictionary with: mustStartWith = list of allowed starting fields mustContain = list of allowed interior fields mustEndWith = list of allowed ending fields for each list, if an entry starts with '!' then this indicates negation of that entry each cipher is evaluated against each element of the list, and a non-matching finding == exclusion of that cipher For example, startswith = aes 3des endswith = !cbc would keep any cipher that starts with aes, starts with 3des, and does not end with cbc if the processed list winds up with *no* entries, then a "ManualActionRecq" exception is raised. The method returns the modified list of ciphers, and the list of rejected ciphers """ acceptCiphers = [] rejectCiphers = [] messages = [] if not cipherLine: cipherLine = self.defaultCiphers msg = "No setting found for '%s' - using OS default as per man pages of '%s'" % (param, cipherLine) self.logger.info(self.module_name, msg) cipherList = tcs_utils.splitNaturally(cipherLine, wordAdditions='-:') if 'mustStartWith' in restrictions: allowedStarts, rejectedStarts = self._splitRestrictions(tcs_utils.splitNaturally(restrictions['mustStartWith'], wordAdditions='-:!'), prefix="^") if 'mustContain' in restrictions: allowedContains, rejectedContains = self._splitRestrictions(tcs_utils.splitNaturally(restrictions['mustContain'], wordAdditions='-:!')) if 'mustEndWith' in restrictions: allowedEnds, rejectedEnds = self._splitRestrictions(tcs_utils.splitNaturally(restrictions['mustEndWith'], wordAdditions='-:!'), suffix="$") acceptPattern = ".*".join ([pattern for pattern in [allowedStarts, allowedContains, allowedEnds] if pattern ]) rejectPattern = ".*".join ([pattern for pattern in [rejectedStarts, rejectedContains, rejectedEnds] if pattern ]) acceptRegex = re.compile(acceptPattern) rejectRegex = re.compile(rejectPattern) for cipher in cipherList: # by default we accept, unless the match fails acceptMatch = True if acceptPattern and not acceptRegex.search(cipher) : acceptMatch = False rejectMatch = False if rejectPattern and rejectRegex.search(cipher) : rejectMatch = True if acceptMatch and not rejectMatch: msg = "Cipher '%s' matched acceptable criteria" % cipher self.logger.debug(self.module_name+"._restrictCiphers", str(msg)) acceptCiphers.append(cipher) else: msg = "Cipher '%s' did not match acceptable criteria" % cipher messages.append(msg) rejectCiphers.append(cipher) if not acceptCiphers: msg = "List of acceptable ciphers is empty - please configure '%s' manually " % self.configfile raise tcs_utils.ManualActionReqd("%s %s" % (self.module_name, msg)) return ','.join(acceptCiphers), messages