def check_url(self, pkg, tag, url): """ Check that URL points to something that seems to exist. Return info() of the response if available. """ if not self.network_enabled: if self.verbose: printInfo(pkg, 'network-checks-disabled', url) return if self.verbose: printInfo(pkg, 'checking-url', url, '(timeout %s seconds)' % self.network_timeout) res = None try: opener = urllib2.build_opener(_HeadRedirectHandler()) opener.addheaders = [('User-Agent', 'rpmlint/%s' % Config.__version__)] res = opener.open(_HeadRequest(url), timeout=self.network_timeout) except Exception as e: errstr = str(e) or repr(e) or type(e) printWarning(pkg, 'invalid-url', '%s:' % tag, url, errstr) info = None if res: with contextlib.closing(res): info = res.info() return info
def check_url(self, pkg, tag, url): """ Check that URL points to something that seems to exist. Return info() of the response if available. """ if not self.network_enabled: if self.verbose: printInfo(pkg, 'network-checks-disabled', url) return if self.verbose: printInfo(pkg, 'checking-url', url, '(timeout %s seconds)' % self.network_timeout) res = None try: opener = urllib2.build_opener(_HeadRedirectHandler()) opener.addheaders = [('User-Agent', 'rpmlint/%s' % Config.__version__)] res = opener.open(_HeadRequest(url), timeout=self.network_timeout) except Exception as e: errstr = str(e) or repr(e) or type(e) printWarning(pkg, 'invalid-url', '%s:' % tag, url, errstr) info = None if res: info = res.info() res.close() return info
def check_url(self, pkg, tag, url): """Check that URL points to something that seems to exist. Return info() of the response if available.""" if not self.network_enabled: if self.verbose: printInfo(pkg, 'network-checks-disabled', url) return if self.verbose: printInfo(pkg, 'checking-url', url, '(timeout %s seconds)' % self.network_timeout) # Could use timeout kwarg to urlopen, but that's python >= 2.6 only socket.setdefaulttimeout(self.network_timeout) res = None try: opener = urllib2.build_opener(_HeadRedirectHandler()) opener.addheaders = [('User-Agent', 'rpmlint/%s' % Config.__version__)] res = opener.open(_HeadRequest(url)) except Exception: e = sys.exc_info()[1] errstr = str(e) or repr(e) or type(e) printWarning(pkg, 'invalid-url', '%s:' % tag, url, errstr) info = None if res: info = res.info() res.close() return info
def check_file(self, pkg, filename): pkgfile = pkg.files()[filename] if not (stat.S_ISREG(pkgfile.mode) and pkgfile.magic.startswith('POSIX shell script')): return try: status, output = Pkg.getstatusoutput(["dash", "-n", filename]) if status == 2: printWarning(pkg, "bin-sh-syntax-error", filename) status, output = Pkg.getstatusoutput( ["checkbashisms", filename]) if status == 1: printInfo(pkg, "potential-bashisms", filename) except (FileNotFoundError, UnicodeDecodeError): pass
def check_url(self, pkg, tag, url): """Check that URL points to something that seems to exist. Return info() of the response if available.""" if not self.network_enabled: if self.verbose: printInfo(pkg, 'network-checks-disabled', url) return if self.verbose: printInfo(pkg, 'checking-url', url, '(timeout %s seconds)' % self.network_timeout) # Could use timeout kwarg to urlopen, but that's python >= 2.6 only socket.setdefaulttimeout(self.network_timeout) res = None try: opener = urllib2.build_opener(_HeadRedirectHandler()) opener.addheaders = [('User-Agent', 'rpmlint/%s' % Config.__version__)] res = opener.open(_HeadRequest(url)) except Exception, e: errstr = str(e) or repr(e) or type(e) printWarning(pkg, 'invalid-url', '%s:' % tag, url, errstr)
def check_binary(self, pkg): files = pkg.files() menus = [] for fname, pkgfile in files.items(): # Check menu files res = menu_file_regex.search(fname) mode = pkgfile.mode if res: basename = res.group(1) if not stat.S_ISREG(mode): printError(pkg, 'non-file-in-menu-dir', fname) else: if basename != pkg.name: printWarning(pkg, 'non-coherent-menu-filename', fname) if mode & 0o444 != 0o444: printError(pkg, 'non-readable-menu-file', fname) if mode & 0o111: printError(pkg, 'executable-menu-file', fname) menus.append(fname) else: # Check old menus from KDE and GNOME res = old_menu_file_regex.search(fname) if res: if stat.S_ISREG(mode): printError(pkg, 'old-menu-entry', fname) else: # Check non transparent xpm files res = xpm_ext_regex.search(fname) if res: if stat.S_ISREG(mode) and not pkg.grep( 'None",', fname): printWarning(pkg, 'non-transparent-xpm', fname) if fname.startswith('/usr/lib64/menu'): printError(pkg, 'menu-in-wrong-dir', fname) if menus: postin = pkg[rpm.RPMTAG_POSTIN] or \ pkg.scriptprog(rpm.RPMTAG_POSTINPROG) if not postin: printError(pkg, 'menu-without-postin') elif not update_menus_regex.search(postin): printError(pkg, 'postin-without-update-menus') postun = pkg[rpm.RPMTAG_POSTUN] or \ pkg.scriptprog(rpm.RPMTAG_POSTUNPROG) if not postun: printError(pkg, 'menu-without-postun') elif not update_menus_regex.search(postun): printError(pkg, 'postun-without-update-menus') directory = pkg.dirName() for f in menus: # remove comments and handle cpp continuation lines cmd = Pkg.getstatusoutput(('/lib/cpp', directory + f), True)[1] for line in cmd.splitlines(): if not line.startswith('?'): continue res = package_regex.search(line) if res: package = res.group(1) if package != pkg.name: printWarning(pkg, 'incoherent-package-value-in-menu', package, f) else: printInfo(pkg, 'unable-to-parse-menu-entry', line) command = True res = command_regex.search(line) if res: command_line = (res.group(1) or res.group(2)).split() command = command_line[0] for launcher in launchers: if not launcher[0].search(command): continue found = False if launcher[1]: found = '/bin/' + command_line[0] in files or \ '/usr/bin/' + command_line[0] in files or \ '/usr/X11R6/bin/' + command_line[0] \ in files if not found: for l in launcher[1]: if l in pkg.req_names(): found = True break if not found: printError( pkg, 'use-of-launcher-in-menu-but-no-requires-on', launcher[1][0]) command = command_line[1] break if command[0] == '/': if command not in files: printWarning(pkg, 'menu-command-not-in-package', command) elif not ('/bin/' + command in files or '/usr/bin/' + command in files or '/usr/X11R6/bin/' + command in files): printWarning(pkg, 'menu-command-not-in-package', command) else: printWarning(pkg, 'missing-menu-command') command = False res = longtitle_regex.search(line) if res: grp = res.groups() title = grp[1] or grp[2] if title[0] != title[0].upper(): printWarning(pkg, 'menu-longtitle-not-capitalized', title) res = version_regex.search(title) if res: printWarning(pkg, 'version-in-menu-longtitle', title) else: printError(pkg, 'no-longtitle-in-menu', f) title = None res = title_regex.search(line) if res: grp = res.groups() title = grp[1] or grp[2] if title[0] != title[0].upper(): printWarning(pkg, 'menu-title-not-capitalized', title) res = version_regex.search(title) if res: printWarning(pkg, 'version-in-menu-title', title) if '/' in title: printError(pkg, 'invalid-title', title) else: printError(pkg, 'no-title-in-menu', f) title = None res = needs_regex.search(line) if res: grp = res.groups() needs = (grp[1] or grp[2]).lower() if needs in ('x11', 'text', 'wm'): res = section_regex.search(line) if res: grp = res.groups() section = grp[1] or grp[2] # don't warn entries for sections if command and section not in valid_sections: printError(pkg, 'invalid-menu-section', section, f) else: printInfo(pkg, 'unable-to-parse-menu-section', line) elif needs not in standard_needs: printInfo(pkg, 'strange-needs', needs, f) else: printInfo(pkg, 'unable-to-parse-menu-needs', line) res = icon_regex.search(line) if res: icon = res.group(1) if not icon_ext_regex.search(icon): printWarning(pkg, 'invalid-menu-icon-type', icon) if icon[0] == '/' and needs == 'x11': printWarning(pkg, 'hardcoded-path-in-menu-icon', icon) else: for path in icon_paths: if (path[0] + icon) not in files: printError( pkg, path[1] + '-icon-not-in-package', icon, f) else: printWarning(pkg, 'no-icon-in-menu', title) res = xdg_migrated_regex.search(line) if res: if not res.group(1).lower() == "true": printError(pkg, 'non-xdg-migrated-menu') else: printError(pkg, 'non-xdg-migrated-menu')
def spell_check(pkg, str, fmt, lang, ignored): dict_found = True warned = set() if enchant: if lang == 'C': lang = 'en_US' checker = _enchant_checkers.get(lang) if not checker and lang not in _enchant_checkers: try: checker = enchant.checker.SpellChecker( lang, filters=[enchant.tokenize.EmailFilter, enchant.tokenize.URLFilter, enchant.tokenize.WikiWordFilter]) except enchant.DictNotFoundError: printInfo(pkg, 'enchant-dictionary-not-found', lang) pass _enchant_checkers[lang] = checker if checker: # squeeze whitespace to ease leading context check checker.set_text(re.sub(r'\s+', ' ', str)) if use_utf8: uppername = Pkg.to_unicode(pkg.header[rpm.RPMTAG_NAME]).upper() else: uppername = pkg.name.upper() upperparts = uppername.split('-') if lang.startswith('en'): ups = [x + "'S" for x in upperparts] upperparts.extend(ups) for err in checker: # Skip already warned and ignored words if err.word in warned or err.word in ignored: continue # Skip all capitalized words that do not start a sentence if err.word[0].isupper() and not \ sentence_break_regex.search(checker.leading_context(3)): continue upperword = err.word.upper() # Skip all uppercase words if err.word == upperword: continue # Skip errors containing package name or equal to a # "component" of it, case insensitively if uppername in upperword or upperword in upperparts: continue # Work around enchant's digit tokenizing behavior: # http://github.com/rfk/pyenchant/issues/issue/3 if checker.leading_context(1).isdigit() or \ checker.trailing_context(1).isdigit(): continue # Warn and suggest sug = ', '.join(checker.suggest()[:3]) if sug: sug = '-> %s' % sug printWarning(pkg, 'spelling-error', fmt % lang, err.word, sug) warned.add(err.word) else: dict_found = False if not enchant or not dict_found: for seq in str.split(): for word in re.split(r'[^a-z]+', seq.lower()): if len(word) == 0: continue correct = BAD_WORDS.get(word) if not correct: continue if word[0] == '\'': word = word[1:] if word[-1] == '\'': word = word[:-1] if word in warned or word in ignored: continue printWarning(pkg, 'spelling-error', fmt % lang, word, '->', correct) warned.add(word)
def check_binary(self, pkg): files = pkg.files() menus = [] for fname, pkgfile in files.items(): # Check menu files res = menu_file_regex.search(fname) mode = pkgfile.mode if res: basename = res.group(1) if not stat.S_ISREG(mode): printError(pkg, 'non-file-in-menu-dir', fname) else: if basename != pkg.name: printWarning(pkg, 'non-coherent-menu-filename', fname) if mode & 0o444 != 0o444: printError(pkg, 'non-readable-menu-file', fname) if mode & 0o111: printError(pkg, 'executable-menu-file', fname) menus.append(fname) else: # Check old menus from KDE and GNOME res = old_menu_file_regex.search(fname) if res: if stat.S_ISREG(mode): printError(pkg, 'old-menu-entry', fname) else: # Check non transparent xpm files res = xpm_ext_regex.search(fname) if res: if stat.S_ISREG(mode) and not pkg.grep('None",', fname): printWarning(pkg, 'non-transparent-xpm', fname) if fname.startswith('/usr/lib64/menu'): printError(pkg, 'menu-in-wrong-dir', fname) if menus: postin = pkg[rpm.RPMTAG_POSTIN] or \ pkg.scriptprog(rpm.RPMTAG_POSTINPROG) if not postin: printError(pkg, 'menu-without-postin') elif not update_menus_regex.search(postin): printError(pkg, 'postin-without-update-menus') postun = pkg[rpm.RPMTAG_POSTUN] or \ pkg.scriptprog(rpm.RPMTAG_POSTUNPROG) if not postun: printError(pkg, 'menu-without-postun') elif not update_menus_regex.search(postun): printError(pkg, 'postun-without-update-menus') directory = pkg.dirName() for f in menus: # remove comments and handle cpp continuation lines cmd = Pkg.getstatusoutput(('/lib/cpp', directory + f), True)[1] for line in cmd.splitlines(): if not line.startswith('?'): continue res = package_regex.search(line) if res: package = res.group(1) if package != pkg.name: printWarning(pkg, 'incoherent-package-value-in-menu', package, f) else: printInfo(pkg, 'unable-to-parse-menu-entry', line) command = True res = command_regex.search(line) if res: command_line = (res.group(1) or res.group(2)).split() command = command_line[0] for launcher in launchers: if not launcher[0].search(command): continue found = False if launcher[1]: found = '/bin/' + command_line[0] in files or \ '/usr/bin/' + command_line[0] in files or \ '/usr/X11R6/bin/' + command_line[0] \ in files if not found: for l in launcher[1]: if l in pkg.req_names(): found = True break if not found: printError(pkg, 'use-of-launcher-in-menu-but-no-requires-on', launcher[1][0]) command = command_line[1] break if command[0] == '/': if command not in files: printWarning( pkg, 'menu-command-not-in-package', command) elif not ('/bin/' + command in files or '/usr/bin/' + command in files or '/usr/X11R6/bin/' + command in files): printWarning(pkg, 'menu-command-not-in-package', command) else: printWarning(pkg, 'missing-menu-command') command = False res = longtitle_regex.search(line) if res: grp = res.groups() title = grp[1] or grp[2] if title[0] != title[0].upper(): printWarning(pkg, 'menu-longtitle-not-capitalized', title) res = version_regex.search(title) if res: printWarning(pkg, 'version-in-menu-longtitle', title) else: printError(pkg, 'no-longtitle-in-menu', f) title = None res = title_regex.search(line) if res: grp = res.groups() title = grp[1] or grp[2] if title[0] != title[0].upper(): printWarning(pkg, 'menu-title-not-capitalized', title) res = version_regex.search(title) if res: printWarning(pkg, 'version-in-menu-title', title) if '/' in title: printError(pkg, 'invalid-title', title) else: printError(pkg, 'no-title-in-menu', f) title = None res = needs_regex.search(line) if res: grp = res.groups() needs = (grp[1] or grp[2]).lower() if needs in ('x11', 'text', 'wm'): res = section_regex.search(line) if res: grp = res.groups() section = grp[1] or grp[2] # don't warn entries for sections if command and section not in valid_sections: printError(pkg, 'invalid-menu-section', section, f) else: printInfo(pkg, 'unable-to-parse-menu-section', line) elif needs not in standard_needs: printInfo(pkg, 'strange-needs', needs, f) else: printInfo(pkg, 'unable-to-parse-menu-needs', line) res = icon_regex.search(line) if res: icon = res.group(1) if not icon_ext_regex.search(icon): printWarning(pkg, 'invalid-menu-icon-type', icon) if icon[0] == '/' and needs == 'x11': printWarning(pkg, 'hardcoded-path-in-menu-icon', icon) else: for path in icon_paths: if (path[0] + icon) not in files: printError( pkg, path[1] + '-icon-not-in-package', icon, f) else: printWarning(pkg, 'no-icon-in-menu', title) res = xdg_migrated_regex.search(line) if res: if not res.group(1).lower() == "true": printError(pkg, 'non-xdg-migrated-menu') else: printError(pkg, 'non-xdg-migrated-menu')
def check(self, pkg): global _permissions_d_whitelist if pkg.isSource(): return files = pkg.files() permfiles = {} # first pass, find and parse permissions.d files for f in files: if f in pkg.ghostFiles(): continue if f.startswith("/etc/permissions.d/"): bn = f[19:] if bn not in _permissions_d_whitelist: printError(pkg, "permissions-unauthorized-file", f) bn = bn.split('.')[0] if bn not in permfiles: permfiles[bn] = 1 for f in permfiles: f = pkg.dirName() + "/etc/permissions.d/" + f if os.path.exists(f + ".secure"): self._parsefile(f + ".secure") else: self._parsefile(f) need_set_permissions = False found_suseconfig = False # second pass, find permissions violations for f, pkgfile in files.items(): if pkgfile.filecaps: printError(pkg, 'permissions-fscaps', '%(fname)s has fscaps "%(caps)s"' % {'fname': f, 'caps': pkgfile.filecaps}) mode = pkgfile.mode owner = pkgfile.user + ':' + pkgfile.group # S_IFSOCK 014 socket # S_IFLNK 012 symbolic link # S_IFREG 010 regular file # S_IFBLK 006 block device # S_IFDIR 004 directory # S_IFCHR 002 character device # S_IFIFO 001 FIFO type = (mode >> 12) & 0o17 mode &= 0o7777 need_verifyscript = False if f in self.perms or (type == 4 and f + "/" in self.perms): if type == 0o12: printWarning(pkg, "permissions-symlink", f) continue need_verifyscript = True m = 0 o = "invalid" if type == 4: if f in self.perms: printWarning(pkg, 'permissions-dir-without-slash', f) else: f += '/' if type == 0o10 and mode & 0o111: # pie binaries have 'shared object' here if (pkgfile.magic.startswith('ELF ') and ('shared object' not in pkgfile.magic) and ('pie executable' not in pkgfile.magic)): printError(pkg, 'non-position-independent-executable', f) m = self.perms[f]['mode'] o = self.perms[f]['owner'] if mode != m: printError( pkg, 'permissions-incorrect', '%(file)s has mode 0%(mode)o but should be 0%(m)o' % {'file': f, 'mode': mode, 'm': m}) if owner != o: printError( pkg, 'permissions-incorrect-owner', '%(file)s belongs to %(owner)s but should be %(o)s' % {'file': f, 'owner': owner, 'o': o}) elif type != 0o12: if f + '/' in self.perms: printWarning( pkg, 'permissions-file-as-dir', f + ' is a file but listed as directory') if mode & 0o6000: need_verifyscript = True msg = '%(file)s is packaged with ' \ 'setuid/setgid bits (0%(mode)o)' % \ {'file': f, 'mode': mode} if type != 0o4: printError(pkg, 'permissions-file-setuid-bit', msg) else: printWarning(pkg, 'permissions-directory-setuid-bit', msg) if type == 0o10: if ('shared object' not in pkgfile.magic and 'pie executable' not in pkgfile.magic): printError(pkg, 'non-position-independent-executable', f) if mode & 0o2: need_verifyscript = True printError(pkg, 'permissions-world-writable', '%(file)s is packaged with world writable permissions (0%(mode)o)' % {'file': f, 'mode': mode}) script = pkg[rpm.RPMTAG_POSTIN] or pkg.scriptprog(rpm.RPMTAG_POSTINPROG) found = False if script: for line in script.split("\n"): if "chkstat -n" in line and f in line: found = True break if "SuSEconfig --module permissions" in line \ or "run_permissions is obsolete" in line: found = True found_suseconfig = True break if need_verifyscript and \ (f not in self.perms or 'static' not in self.perms[f]): if not script or not found: printError(pkg, 'permissions-missing-postin', "missing %%set_permissions %s in %%post" % f) need_set_permissions = True script = pkg[rpm.RPMTAG_VERIFYSCRIPT] or pkg[rpm.RPMTAG_VERIFYSCRIPTPROG] found = False if script: for line in script.split("\n"): if "/chkstat" in line and f in line: found = True break if not script or not found: printWarning(pkg, 'permissions-missing-verifyscript', "missing %%verify_permissions -e %s" % f) if need_set_permissions: if 'permissions' not in map(lambda x: x[0], pkg.prereq()): printError(pkg, 'permissions-missing-requires', "missing 'permissions' in PreReq") if found_suseconfig: printInfo(pkg, 'permissions-suseconfig-obsolete', "%run_permissions is obsolete")
def spell_check(pkg, str, fmt, lang, ignored): dict_found = True warned = set() if enchant: if lang == 'C': lang = 'en_US' checker = _enchant_checkers.get(lang) if not checker and lang not in _enchant_checkers: try: checker = enchant.checker.SpellChecker( lang, filters=[enchant.tokenize.EmailFilter, enchant.tokenize.URLFilter, enchant.tokenize.WikiWordFilter]) except enchant.DictNotFoundError: printInfo(pkg, 'enchant-dictionary-not-found', lang) pass _enchant_checkers[lang] = checker if checker: # squeeze whitespace to ease leading context check checker.set_text(re.sub(r'\s+', ' ', str)) if use_utf8: uppername = Pkg.to_unicode(pkg.header[rpm.RPMTAG_NAME]).upper() else: uppername = pkg.name.upper() upperparts = uppername.split('-') if lang.startswith('en'): ups = [x + "'S" for x in upperparts] upperparts.extend(ups) for err in checker: # Skip already warned and ignored words if err.word in warned or err.word in ignored: continue # Skip all capitalized words that do not start a sentence if err.word[0].isupper() and not \ sentence_break_regex.search(checker.leading_context(3)): continue upperword = err.word.upper() # Skip all uppercase words if err.word == upperword: continue # Skip errors containing package name or equal to a # "component" of it, case insensitively if uppername in upperword or upperword in upperparts: continue # Work around enchant's digit tokenizing behavior: # http://github.com/rfk/pyenchant/issues/issue/3 if checker.leading_context(1).isdigit() or \ checker.trailing_context(1).isdigit(): continue # Warn and suggest sug = ', '.join(checker.suggest()[:3]) if sug: sug = '-> %s' % sug printWarning(pkg, 'spelling-error', fmt % lang, err.word, sug) warned.add(err.word) else: dict_found = False if not enchant or not dict_found: for seq in str.split(): for word in re.split('[^a-z]+', seq.lower()): if len(word) == 0: continue correct = BAD_WORDS.get(word) if not correct: continue if word[0] == '\'': word = word[1:] if word[-1] == '\'': word = word[:-1] if word in warned or word in ignored: continue printWarning(pkg, 'spelling-error', fmt % lang, word, '->', correct) warned.add(word)
def check(self, pkg): global _permissions_d_whitelist if pkg.isSource(): return files = pkg.files() permfiles = set() # first pass, find and parse permissions.d files for f in files: for prefix in self._paths_to("permissions.d/"): if f.startswith(prefix): if f in pkg.ghostFiles(): printError(pkg, 'polkit-ghost-file', f) continue bn = f[len(prefix):] if bn not in _permissions_d_whitelist: printError(pkg, "permissions-unauthorized-file", f) bn = 'permissions.d/' + bn.split('.')[0] if bn not in permfiles: permfiles.add(bn) for f in permfiles: # check for a .secure file first, falling back to the plain file for path in self._paths_to(f + '.secure', f): if path in files: self._parseProfile(pkg.dirName() + path) break need_set_permissions = False found_suseconfig = False # second pass, find permissions violations for f, pkgfile in files.items(): if pkgfile.filecaps: printError( pkg, 'permissions-fscaps', '%(fname)s has fscaps "%(caps)s"' % { 'fname': f, 'caps': pkgfile.filecaps }) mode = pkgfile.mode owner = pkgfile.user + ':' + pkgfile.group need_verifyscript = False if f in self.perms or (stat.S_ISDIR(mode) and f + "/" in self.perms): if stat.S_ISLNK(mode): printWarning(pkg, "permissions-symlink", f) continue need_verifyscript = True m = 0 o = "invalid" if stat.S_ISDIR(mode): if f in self.perms: printWarning(pkg, 'permissions-dir-without-slash', f) else: f += '/' entry = self.perms[f] if stat.S_ISREG(mode) and mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH): # pie binaries have 'shared object' here if (pkgfile.magic.startswith('ELF ') and ('shared object' not in pkgfile.magic) and ('pie executable' not in pkgfile.magic)): printError(pkg, 'non-position-independent-executable', f) m = entry.mode o = ':'.join((entry.owner, entry.group)) if stat.S_IMODE(mode) != m: printError( pkg, 'permissions-incorrect', '%(file)s has mode 0%(mode)o but should be 0%(m)o' % { 'file': f, 'mode': stat.S_IMODE(mode), 'm': m }) if owner != o: printError( pkg, 'permissions-incorrect-owner', '%(file)s belongs to %(owner)s but should be %(o)s' % { 'file': f, 'owner': owner, 'o': o }) elif not stat.S_ISLNK(mode): if f + '/' in self.perms: printWarning(pkg, 'permissions-file-as-dir', f + ' is a file but listed as directory') if mode & (stat.S_ISUID | stat.S_ISGID): need_verifyscript = True msg = '%(file)s is packaged with ' \ 'setuid/setgid bits (0%(mode)o)' % \ {'file': f, 'mode': stat.S_IMODE(mode)} if not stat.S_ISDIR(mode): printError(pkg, 'permissions-file-setuid-bit', msg) else: printWarning(pkg, 'permissions-directory-setuid-bit', msg) if stat.S_ISREG(mode): if ('shared object' not in pkgfile.magic and 'pie executable' not in pkgfile.magic): printError(pkg, 'non-position-independent-executable', f) script = pkg[rpm.RPMTAG_POSTIN] or pkg.scriptprog( rpm.RPMTAG_POSTINPROG) found = False if script: for line in script.split("\n"): if "chkstat -n" in line and f in line: found = True break if "SuSEconfig --module permissions" in line \ or "run_permissions is obsolete" in line: found = True found_suseconfig = True break if need_verifyscript and \ (f not in self.perms or not self._isStaticEntry(self.perms[f])): if not script or not found: printError(pkg, 'permissions-missing-postin', "missing %%set_permissions %s in %%post" % f) need_set_permissions = True script = pkg[rpm.RPMTAG_VERIFYSCRIPT] or pkg[ rpm.RPMTAG_VERIFYSCRIPTPROG] found = False if script: for line in script.split("\n"): if "/chkstat" in line and f in line: found = True break if not script or not found: printWarning(pkg, 'permissions-missing-verifyscript', "missing %%verify_permissions -e %s" % f) if need_set_permissions: if 'permissions' not in map(lambda x: x[0], pkg.prereq()): printError(pkg, 'permissions-missing-requires', "missing 'permissions' in PreReq") if found_suseconfig: printInfo(pkg, 'permissions-suseconfig-obsolete', "%run_permissions is obsolete")
def check(self, pkg): global _permissions_d_whitelist if pkg.isSource(): return files = pkg.files() permfiles = {} # first pass, find and parse permissions.d files for f in files: if f in pkg.ghostFiles(): continue if f.startswith("/etc/permissions.d/"): bn = f[19:] if bn not in _permissions_d_whitelist: printError(pkg, "permissions-unauthorized-file", f) bn = bn.split('.')[0] if bn not in permfiles: permfiles[bn] = 1 for f in permfiles: f = pkg.dirName() + "/etc/permissions.d/" + f if os.path.exists(f + ".secure"): self._parsefile(f + ".secure") else: self._parsefile(f) need_set_permissions = False found_suseconfig = False # second pass, find permissions violations for f, pkgfile in files.items(): if pkgfile.filecaps: printError( pkg, 'permissions-fscaps', '%(fname)s has fscaps "%(caps)s"' % { 'fname': f, 'caps': pkgfile.filecaps }) mode = pkgfile.mode owner = pkgfile.user + ':' + pkgfile.group # S_IFSOCK 014 socket # S_IFLNK 012 symbolic link # S_IFREG 010 regular file # S_IFBLK 006 block device # S_IFDIR 004 directory # S_IFCHR 002 character device # S_IFIFO 001 FIFO type = (mode >> 12) & 0o17 mode &= 0o7777 need_verifyscript = False if f in self.perms or (type == 4 and f + "/" in self.perms): if type == 0o12: printWarning(pkg, "permissions-symlink", f) continue need_verifyscript = True m = 0 o = "invalid" if type == 4: if f in self.perms: printWarning(pkg, 'permissions-dir-without-slash', f) else: f += '/' if type == 0o10 and mode & 0o111: # pie binaries have 'shared object' here if (pkgfile.magic.startswith('ELF ') and ('shared object' not in pkgfile.magic) and ('pie executable' not in pkgfile.magic)): printError(pkg, 'non-position-independent-executable', f) m = self.perms[f]['mode'] o = self.perms[f]['owner'] if mode != m: printError( pkg, 'permissions-incorrect', '%(file)s has mode 0%(mode)o but should be 0%(m)o' % { 'file': f, 'mode': mode, 'm': m }) if owner != o: printError( pkg, 'permissions-incorrect-owner', '%(file)s belongs to %(owner)s but should be %(o)s' % { 'file': f, 'owner': owner, 'o': o }) elif type != 0o12: if f + '/' in self.perms: printWarning(pkg, 'permissions-file-as-dir', f + ' is a file but listed as directory') if mode & 0o6000: need_verifyscript = True msg = '%(file)s is packaged with ' \ 'setuid/setgid bits (0%(mode)o)' % \ {'file': f, 'mode': mode} if type != 0o4: printError(pkg, 'permissions-file-setuid-bit', msg) else: printWarning(pkg, 'permissions-directory-setuid-bit', msg) if type == 0o10: if ('shared object' not in pkgfile.magic and 'pie executable' not in pkgfile.magic): printError(pkg, 'non-position-independent-executable', f) if mode & 0o2: need_verifyscript = True printError( pkg, 'permissions-world-writable', '%(file)s is packaged with world writable permissions (0%(mode)o)' % { 'file': f, 'mode': mode }) script = pkg[rpm.RPMTAG_POSTIN] or pkg.scriptprog( rpm.RPMTAG_POSTINPROG) found = False if script: for line in script.split("\n"): if "chkstat -n" in line and f in line: found = True break if "SuSEconfig --module permissions" in line \ or "run_permissions is obsolete" in line: found = True found_suseconfig = True break if need_verifyscript and \ (f not in self.perms or 'static' not in self.perms[f]): if not script or not found: printError(pkg, 'permissions-missing-postin', "missing %%set_permissions %s in %%post" % f) need_set_permissions = True script = pkg[rpm.RPMTAG_VERIFYSCRIPT] or pkg[ rpm.RPMTAG_VERIFYSCRIPTPROG] found = False if script: for line in script.split("\n"): if "/chkstat" in line and f in line: found = True break if not script or not found: printWarning(pkg, 'permissions-missing-verifyscript', "missing %%verify_permissions -e %s" % f) if need_set_permissions: if 'permissions' not in map(lambda x: x[0], pkg.prereq()): printError(pkg, 'permissions-missing-requires', "missing 'permissions' in PreReq") if found_suseconfig: printInfo(pkg, 'permissions-suseconfig-obsolete', "%run_permissions is obsolete")