Hello community, here is the log from the commit of package rpmlint for openSUSE:Factory checked in at 2020-11-26 23:11:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rpmlint (Old) and /work/SRC/openSUSE:Factory/.rpmlint.new.5913 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rpmlint" Thu Nov 26 23:11:48 2020 rev:347 rq:850105 version:1.11 Changes: -------- --- /work/SRC/openSUSE:Factory/rpmlint/rpmlint.changes 2020-11-06 23:43:00.983503830 +0100 +++ /work/SRC/openSUSE:Factory/.rpmlint.new.5913/rpmlint.changes 2020-11-26 23:12:47.832947475 +0100 @@ -1,0 +2,7 @@ +Tue Nov 17 13:48:06 UTC 2020 - [email protected] + +- Update to version master: + * Permissions: be robust against variables.conf not existing + * CheckSUIDPermissions: enhance parser to support new permissions variables + +------------------------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ rpmlint.spec: same change ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.hi1jgq/_old 2020-11-26 23:12:49.012948555 +0100 +++ /var/tmp/diff_new_pack.hi1jgq/_new 2020-11-26 23:12:49.012948555 +0100 @@ -3,4 +3,4 @@ <param name="url">https://github.com/openSUSE/rpmlint-tests.git</param> <param name="changesrevision">3d948bb4c8be26e2ec8922d4c3430b0e0451994b</param></service><service name="tar_scm"> <param name="url">https://github.com/openSUSE/rpmlint-checks.git</param> - <param name="changesrevision">9db2d998028dac60a5c5e16af303693b158b7272</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">f62985c16ad7bc370ea08958a139d23aad4fd94b</param></service></servicedata> \ No newline at end of file ++++++ rpmlint-checks-master.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rpmlint-checks-master/CheckSUIDPermissions.py new/rpmlint-checks-master/CheckSUIDPermissions.py --- old/rpmlint-checks-master/CheckSUIDPermissions.py 2020-07-31 10:46:46.000000000 +0200 +++ new/rpmlint-checks-master/CheckSUIDPermissions.py 2020-11-17 13:15:42.000000000 +0100 @@ -11,10 +11,10 @@ from Filter import printWarning, printError, printInfo import AbstractCheck import Whitelisting +import Permissions + import os -import re import rpm -import sys import stat _permissions_d_whitelist = ( @@ -33,9 +33,22 @@ AbstractCheck.AbstractCheck.__init__(self, "CheckSUIDPermissions") self.perms = {} + self.var_handler = Permissions.VariablesHandler("/usr/share/permissions/variables.conf") + for fname in self._paths_to('permissions', 'permissions.secure'): - if os.path.exists(fname): - self._parsefile(fname) + if not os.path.exists(fname): + continue + + self._parseProfile(fname) + + def _parseProfile(self, path): + parser = Permissions.PermissionsParser(self.var_handler, path) + self.perms.update(parser.getEntries()) + + def _isStaticEntry(self, entry): + # entries coming from the fixed permissions profile are considered + # static + return entry.profile.endswith("/permissions") @staticmethod def _paths_to(*file_names): @@ -49,39 +62,6 @@ yield '/usr/share/permissions/' + name yield '/etc/' + name - def _parsefile(self, fname): - lnr = 0 - lastfn = None - with open(fname) as inputfile: - for line in inputfile: - lnr += 1 - line = line.split('#')[0].split('\n')[0] - line = line.strip() - if not len(line): - continue - - if line.startswith("+capabilities "): - line = line[len("+capabilities "):] - if lastfn: - self.perms[lastfn]['fscaps'] = line - continue - - line = re.split(r'\s+', line.strip()) - if len(line) == 3: - fn = line[0] - owner = line[1].replace('.', ':') - mode = line[2] - - self.perms[fn] = {"owner": owner, - "mode": int(mode, 8) & 0o7777} - # for permissions that don't change and therefore - # don't need special handling - if fname in self._paths_to('permissions'): - self.perms[fn]['static'] = True - else: - print('%s: Malformatted line %d: %s...' % - (fname, lnr, ' '.join(line)), file=sys.stderr) - def check(self, pkg): global _permissions_d_whitelist @@ -112,7 +92,7 @@ # 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._parsefile(pkg.dirName() + path) + self._parseProfile(pkg.dirName() + path) break need_set_permissions = False @@ -144,6 +124,8 @@ 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 @@ -152,8 +134,8 @@ printError(pkg, 'non-position-independent-executable', f) - m = self.perms[f]['mode'] - o = self.perms[f]['owner'] + m = entry.mode + o = ':'.join((entry.owner, entry.group)) if stat.S_IMODE(mode) != m: printError( @@ -204,7 +186,7 @@ break if need_verifyscript and \ - (f not in self.perms or 'static' not in self.perms[f]): + (f not in self.perms or not self._isStaticEntry(self.perms[f])): if not script or not found: printError(pkg, 'permissions-missing-postin', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rpmlint-checks-master/Permissions.py new/rpmlint-checks-master/Permissions.py --- old/rpmlint-checks-master/Permissions.py 1970-01-01 01:00:00.000000000 +0100 +++ new/rpmlint-checks-master/Permissions.py 2020-11-17 13:15:42.000000000 +0100 @@ -0,0 +1,202 @@ +# vim: sw=4 et sts=4 ts=4 : +############################################################################# +# Author : Matthias Gerstner +# Purpose : reusable code for parsing permissions/chkstat profiles +############################################################################# + +import os +import copy + + +class PermissionsEntry: + + # source profile path + profile = None + # source profile line nr + linenr = None + # target path + path = None + owner = None + group = None + # mode as integer + mode = None + caps = [] + # related paths from variable expansions + related_paths = [] + + def __init__(self, _profile, _line_nr): + + self.profile = _profile + self.linenr = _line_nr + + def __str__(self): + + ret = "{}:{}: {path} {owner}:{group} {mode}".format( + self.profile, + self.linenr, + path=self.path, + owner=self.owner, + group=self.group, + mode=oct(self.mode) + ) + + for cap in self.caps: + ret += "\n+capability " + cap + + for related in self.related_paths: + ret += "\nrelated to " + related + + return ret + + +class VariablesHandler: + + def __init__(self, variables_conf_path): + + self.m_variables = {} + + try: + with open(variables_conf_path) as fd: + self._parse(variables_conf_path, fd) + except FileNotFoundError: + # this can happen during migration in OBS when the new permissions + # package is not yet around + pass + + def _parse(self, label, fd): + + for nr, line in enumerate(fd.readlines(), 1): + + line = line.strip() + + if not line or line.startswith('#'): + continue + + parts = line.split('=', 1) + + if len(parts) != 2: + raise Exception("{}:{}: parse error".format(label, nr)) + + varname = parts[0].strip() + values = parts[1].split() + # strip leading or trailing slashes + values = [v.strip(os.path.sep) for v in values] + + self.m_variables[varname] = values + + def getVariables(self): + """Returns a dictionary with variable names as keys and a list of + variable values as values.""" + return self.m_variables + + def expandPaths(self, path): + """Checks for %{...} variables in the given path and expands them, as + necessary. Will return a list of expanded paths, will be only a single + path if no variables are used.""" + + ret = [""] + + for part in path.split(os.path.sep): + if part.startswith('%{') and part.endswith('}'): + # variable found + variable = part[2:-1] + try: + expansions = self.m_variables[variable] + except KeyError: + raise Exception("Undeclared variable '{}' encountered in profile".format(variable)) + + new_ret = [] + + for p in ret: + for value in expansions: + new_ret.append(os.path.sep.join([p, value])) + + ret = new_ret + elif not part: + # a leading slash, ignore + continue + else: + # a regular, fixed string + ret = [os.path.sep.join([p, part]) for p in ret] + + if path.endswith(os.path.sep): + # restore trailing slashes since they signify that we + # expect a directory + ret = [p + os.path.sep for p in ret] + + return ret + + +class PermissionsParser: + + def __init__(self, var_handler, profile_path): + + self.m_var_handler = var_handler + self.m_entries = {} + + with open(profile_path) as fd: + self._parseFile(profile_path, fd) + + def _parseFile(self, _label, fd): + + class ParseContext: + active_entries = [] + label = _label + + context = ParseContext() + + for nr, line in enumerate(fd.readlines(), 1): + line = line.strip() + + if not line or line.startswith('#'): + continue + + context.line_nr = nr + + self._parseLine(context, line) + + def _parseLine(self, context, line): + + if line.startswith('/') or line.startswith('%'): + context.active_entries = [] + + entry = PermissionsEntry(context.label, context.line_nr) + path, ownership, mode = line.split() + # the format supports both "user.group" and + # "user:group" + entry.owner, entry.group = ownership.replace('.', ':').split(':') + entry.mode = int(mode, 8) + expanded = self.m_var_handler.expandPaths(path) + + for p in expanded: + entry.path = p + entry.related_paths = list(filter(lambda e: e != path, expanded)) + key = entry.path.rstrip(os.path.sep) + if not key: + # this is the root node, keep the slash + key = '/' + entry_copy = copy.deepcopy(entry) + self.m_entries[key] = entry_copy + context.active_entries.append(entry_copy) + elif line.startswith('+'): + # capability line + _type, rest = line.split() + _type = _type.lstrip('+') + + if _type != "capabilities": + raise Exception("Unexpected +[line] encountered in {}:{}".format(context.label, context.line_nr)) + + caps = rest.split(',') + + if not context.active_entries: + raise Exception("+capabilities line without active entries in {}:{}".format(context.label, context.line_nr)) + + for entry in context.active_entries: + entry.caps = caps + else: + raise Exception("Unexpected line encountered in {}:{}".format(context.label, context.line_nr)) + + def getEntries(self): + """Returns a dictionary mapping the target file paths to instances of + PermissionsEntry.""" + return self.m_entries _______________________________________________ openSUSE Commits mailing list -- [email protected] To unsubscribe, email [email protected] List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette List Archives: https://lists.opensuse.org/archives/list/[email protected]
