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]

Reply via email to