Hello community,

here is the log from the commit of package rpmlint for openSUSE:Factory checked 
in at 2019-03-13 09:10:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rpmlint (Old)
 and      /work/SRC/openSUSE:Factory/.rpmlint.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rpmlint"

Wed Mar 13 09:10:13 2019 rev:315 rq:682091 version:1.11

Changes:
--------
--- /work/SRC/openSUSE:Factory/rpmlint/rpmlint.changes  2019-02-25 
17:45:20.958907705 +0100
+++ /work/SRC/openSUSE:Factory/.rpmlint.new.28833/rpmlint.changes       
2019-03-13 09:10:35.703415441 +0100
@@ -1,0 +2,22 @@
+Tue Mar  5 12:11:50 UTC 2019 - mvet...@suse.com
+
+- Add user/group 'minetest' for Minetest 5.0.0 (bsc#1127911)
+
+-------------------------------------------------------------------
+Mon Mar 04 14:33:43 UTC 2019 - opensuse-packag...@opensuse.org
+
+- Update to version master:
+  * CheckPolkitPrivs: fix new rules.d check to use extracted rpm path
+
+-------------------------------------------------------------------
+Wed Feb 27 11:33:42 UTC 2019 - opensuse-packag...@opensuse.org
+
+- Update rpmlint-checks to version master (bsc#1125314):
+  * coding style: fix indentation to satisfy flake8 travis-ci test
+  * CheckPolkitPrivs: implement new check for files put into rules.d dirs
+  * CheckPolkitPrivs: separate and refactor check for actions
+  * CheckPolkitPrivs: separate and refactor check of polkit-default-privs.d
+  * CheckPolkitPrivs: remove oudated PolicyKit path
+  * CheckPolkitPrivs: clearer error message for files in 
/etc/polkit-default-privs.d
+
+-------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
rpmlint.spec: same change
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.hghIwU/_old  2019-03-13 09:10:37.619415243 +0100
+++ /var/tmp/diff_new_pack.hghIwU/_new  2019-03-13 09:10:37.619415243 +0100
@@ -3,4 +3,4 @@
             <param 
name="url">https://github.com/openSUSE/rpmlint-tests.git</param>
           <param 
name="changesrevision">8914352f9a703602fbe68224878d4b540bff72c4</param></service><service
 name="tar_scm">
             <param 
name="url">https://github.com/openSUSE/rpmlint-checks.git</param>
-          <param 
name="changesrevision">2fa6dd19efd033b406b56ecbd664497fba602a2d</param></service></servicedata>
\ No newline at end of file
+          <param 
name="changesrevision">f6e3387e381e719deaf753423332881603b51b5e</param></service></servicedata>
\ No newline at end of file

++++++ config ++++++
--- /var/tmp/diff_new_pack.hghIwU/_old  2019-03-13 09:10:37.635415242 +0100
+++ /var/tmp/diff_new_pack.hghIwU/_new  2019-03-13 09:10:37.635415242 +0100
@@ -405,6 +405,7 @@
     'mdom',
     'memcached',
     'messagebus',
+    'minetest',
     'mktex',
     'modem',
     'mumble-server',
@@ -598,6 +599,7 @@
     'mednafen',
     'memcached',
     'messagebus',
+    'minetest',
     'mpd',
     'mumble-server',
     'mysql',

++++++ rpmlint-checks-master.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rpmlint-checks-master/CheckDBusPolicy.py 
new/rpmlint-checks-master/CheckDBusPolicy.py
--- old/rpmlint-checks-master/CheckDBusPolicy.py        2019-01-09 
20:18:29.000000000 +0100
+++ new/rpmlint-checks-master/CheckDBusPolicy.py        2019-03-04 
14:11:03.000000000 +0100
@@ -42,7 +42,7 @@
                                 send_policy_seen = True
                                 printError(pkg, 
'dbus-policy-allow-without-destination', "%(file)s: %(xml)s" % {'file': f, 
'xml': allow.toxml()})
                             elif allow.hasAttribute('send_destination'):
-                                    send_policy_seen = True
+                                send_policy_seen = True
 
                             if (allow.hasAttribute('receive_sender') or
                                     allow.hasAttribute('receive_interface')):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rpmlint-checks-master/CheckLogrotate.py 
new/rpmlint-checks-master/CheckLogrotate.py
--- old/rpmlint-checks-master/CheckLogrotate.py 2019-01-09 20:18:29.000000000 
+0100
+++ new/rpmlint-checks-master/CheckLogrotate.py 2019-03-04 14:11:03.000000000 
+0100
@@ -47,9 +47,9 @@
                     pkg, 'suse-logrotate-user-writable-log-dir',
                     "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
             elif files[d].group != 'root' and mode & 0o20 and (dirs[d] is None 
or dirs[d][1] != files[d].group):
-                    printError(
-                        pkg, 'suse-logrotate-user-writable-log-dir',
-                        "%s %s:%s %04o" % (d, files[d].user, files[d].group, 
mode))
+                printError(
+                    pkg, 'suse-logrotate-user-writable-log-dir',
+                    "%s %s:%s %04o" % (d, files[d].user, files[d].group, mode))
 
     # extremely primitive logrotate parser
     def parselogrotateconf(self, root, f):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rpmlint-checks-master/CheckPolkitPrivs.py 
new/rpmlint-checks-master/CheckPolkitPrivs.py
--- old/rpmlint-checks-master/CheckPolkitPrivs.py       2019-01-09 
20:18:29.000000000 +0100
+++ new/rpmlint-checks-master/CheckPolkitPrivs.py       2019-03-04 
14:11:03.000000000 +0100
@@ -11,23 +11,51 @@
 import Config
 import re
 import os
+import sys
+import json
+import hashlib
 from xml.dom.minidom import parse
 
-
 POLKIT_PRIVS_WHITELIST = Config.getOption('PolkitPrivsWhiteList', ())   # set 
of file names
 POLKIT_PRIVS_FILES = Config.getOption('PolkitPrivsFiles', 
["/etc/polkit-default-privs.standard"])
+# path to JSON files containing whitelistings for files in rules.d directories
+POLKIT_RULES_WHITELIST = Config.getOption('PolkitRulesWhitelist', ())
 
 
 class PolkitCheck(AbstractCheck.AbstractCheck):
     def __init__(self):
         AbstractCheck.AbstractCheck.__init__(self, "CheckPolkitPrivs")
         self.privs = {}
+        self._collect_privs()
+
+        # a structure like this:
+        # {
+        #   "<package>": {
+        #       "skip-digest-check": bool
+        #       "<path>": {
+        #           "audits": [
+        #               {
+        #                   "bug": "bsc#4711",
+        #                   "comment": "note about whitelisting",
+        #                   "digest": "<alg>:<digest>"
+        #               }
+        #           ]
+        #       }
+        #   }
+        # }
+        self.rules = {}
+        self._collect_rules_whitelist()
+
+    def _get_err_prefix(self):
+        """error prefix label to be used for early error printing."""
+        return self.__class__.__name__ + ":"
 
+    def _collect_privs(self):
         for filename in POLKIT_PRIVS_FILES:
             if os.path.exists(filename):
-                self._parsefile(filename)
+                self._parse_privs_file(filename)
 
-    def _parsefile(self, filename):
+    def _parse_privs_file(self, filename):
         with open(filename) as inputfile:
             for line in inputfile:
                 line = line.split('#')[0].split('\n')[0]
@@ -38,42 +66,122 @@
 
                     self.privs[priv] = value
 
-    def check(self, pkg):
+    def _collect_rules_whitelist(self):
+        for filename in POLKIT_RULES_WHITELIST:
+            if os.path.exists(filename):
+                self._parse_rules_whitelist(filename)
 
-        if pkg.isSource():
+    def _parse_rules_whitelist(self, filename):
+        """
+        The JSON data is structured like this:
+
+        [
+            {
+                "package": "polkit-default-privs",
+                "path": "/etc/polkit-1/rules.d/90-default-privs.rules",
+                # can be left out, default is false
+                # if set then the content will not
+                # be checked (only to be used for special cases)
+                "skip-digest-check": true,
+                "audits": [
+                    {
+                        "bug": "bsc#1125314",
+                        "comment": "rules dynamically generated by our own 
polkit profile tooling",
+                        "digest": 
"sha256:aea3041de2c15db8683620de8533206e50241c309eb27893605d5ead17e5e75f"
+                    },
+                    {
+                        "bug": "bsc#4711",
+                        "comment": "no-op changes in comments",
+                        "digest": "<alg>:<digest>"
+                    }
+                ]
+            },
+            {
+                ...
+            }
+        ]
+        """
+
+        try:
+            with open(filename, 'r') as fd:
+                data = json.load(fd)
+
+                for entry in data:
+                    self._parse_rules_whitelist_entry(entry)
+
+        except Exception as e:
+            print(self._get_err_prefix(), "failed to parse json file {}: 
{}".format(
+                filename, str(e)),
+                file=sys.stderr
+            )
+
+    def _parse_rules_whitelist_entry(self, entry):
+        path = entry["path"]
+        package = entry["package"]
+        skip_digest_check = entry.get("skip-digest-check", False)
+
+        audits = entry.get("audits")
+
+        # it is thinkable that the same rules file is shipped by a
+        # different conflicting package, therefore support
+        # multiple packages claiming the same path
+        pkg_dict = self.rules.setdefault(path, {})
+
+        if package in pkg_dict:
+            print(self._get_err_prefix(), "duplicate entry for path {} and 
package {}".format(
+                path, package),
+                file=sys.stderr
+            )
             return
 
+        pkg_dict[package] = {
+            "skip-digest-check": skip_digest_check,
+            "audits": audits
+        }
+
+    def check_perm_files(self, pkg):
+        """Checks files in polkit-default-privs.d."""
+
         files = pkg.files()
+        prefix = "/etc/polkit-default-privs.d/"
+        profiles = ("restrictive", "standard", "relaxed")
 
-        permfiles = {}
+        permfiles = []
         # first pass, find additional files
         for f in files:
             if f in pkg.ghostFiles():
                 continue
 
-            if f.startswith("/etc/polkit-default-privs.d/"):
+            if f.startswith(prefix):
 
-                bn = f[28:]
+                bn = f[len(prefix):]
                 if bn not in POLKIT_PRIVS_WHITELIST:
                     printError(pkg, "polkit-unauthorized-file", f)
 
-                if bn.endswith(".restrictive") or bn.endswith(".standard") or 
bn.endswith(".relaxed"):
-                    bn = bn.split('.')[0]
+                parts = bn.rsplit('.', 1)
+
+                if len(parts) == 2 and parts[-1] in profiles:
+                    bn = parts[0]
 
                 if bn not in permfiles:
-                    permfiles[bn] = 1
+                    permfiles.append(bn)
 
-        for f in permfiles:
-            f = pkg.dirName() + "/etc/polkit-default-privs.d/" + f
+        for f in sorted(permfiles):
+            f = pkg.dirName() + prefix + f
 
-            if os.path.exists(f + ".restrictive"):
-                self._parsefile(f + ".restrictive")
-            elif os.path.exists(f + ".standard"):
-                self._parsefile(f + ".standard")
-            elif os.path.exists(f + ".relaxed"):
-                self._parsefile(f + ".relaxed")
+            for profile in profiles:
+                path = '.'.join(f, profile)
+                if os.path.exists(path):
+                    self._parse_privs_file(path)
+                    break
             else:
-                self._parsefile(f)
+                self._parse_privs_file(f)
+
+    def check_actions(self, pkg):
+        """Checks files in the actions directory."""
+
+        files = pkg.files()
+        prefix = "/usr/share/polkit-1/actions/"
 
         for f in files:
             if f in pkg.ghostFiles():
@@ -81,68 +189,153 @@
 
             # catch xml exceptions
             try:
-                if f.startswith("/usr/share/PolicyKit/policy/")\
-                or f.startswith("/usr/share/polkit-1/actions/"):
+                if f.startswith(prefix):
                     xml = parse(pkg.dirName() + f)
                     for a in xml.getElementsByTagName("action"):
-                        action = a.getAttribute('id')
-                        if action not in self.privs:
-                            iserr = 0
-                            foundno = 0
-                            foundundef = 0
-                            settings = {}
-                            try:
-                                defaults = 
a.getElementsByTagName("defaults")[0]
-                                for i in defaults.childNodes:
-                                    if not i.nodeType == i.ELEMENT_NODE:
-                                        continue
-
-                                    if i.nodeName in ('allow_any', 
'allow_inactive', 'allow_active'):
-                                        settings[i.nodeName] = 
i.firstChild.data
-
-                            except KeyError:
-                                iserr = 1
-
-                            for i in ('allow_any', 'allow_inactive', 
'allow_active'):
-                                if i not in settings:
-                                    foundundef = 1
-                                    settings[i] = '??'
-                                elif settings[i].find("auth_admin") != 0:
-                                    if settings[i] == 'no':
-                                        foundno = 1
-                                    else:
-                                        iserr = 1
-
-                            if iserr:
-                                printError(
-                                    pkg, 'polkit-unauthorized-privilege',
-                                    '%s (%s:%s:%s)' % (
-                                        action,
-                                        settings['allow_any'],
-                                        settings['allow_inactive'],
-                                        settings['allow_active']))
-                            else:
-                                printError(
-                                    pkg, 'polkit-untracked-privilege',
-                                    '%s (%s:%s:%s)' % (
-                                        action,
-                                        settings['allow_any'],
-                                        settings['allow_inactive'],
-                                        settings['allow_active']))
-
-                            if foundno or foundundef:
-                                printInfo(
-                                    pkg, 'polkit-cant-acquire-privilege',
-                                    '%s (%s:%s:%s)' % (
-                                        action,
-                                        settings['allow_any'],
-                                        settings['allow_inactive'],
-                                        settings['allow_active']))
-
+                        self.check_action(pkg, a)
             except Exception as x:
                 printError(pkg, 'rpmlint-exception', "%(file)s raised an 
exception: %(x)s" % {'file': f, 'x': x})
                 continue
 
+    def check_action(self, pkg, action):
+        """Inspect a single polkit action used by an application."""
+        action_id = action.getAttribute('id')
+
+        if action_id in self.privs:
+            # the action is explicitly whitelisted, nothing else to do
+            return
+
+        allow_types = ('allow_any', 'allow_inactive', 'allow_active')
+        foundunauthorized = False
+        foundno = False
+        foundundef = False
+        settings = {}
+        try:
+            defaults = action.getElementsByTagName("defaults")[0]
+            for i in defaults.childNodes:
+                if not i.nodeType == i.ELEMENT_NODE:
+                    continue
+
+                if i.nodeName in allow_types:
+                    settings[i.nodeName] = i.firstChild.data
+        except KeyError:
+            foundunauthorized = True
+
+        for i in allow_types:
+            if i not in settings:
+                foundundef = True
+                settings[i] = '??'
+            elif settings[i].find("auth_admin") != 0:
+                if settings[i] == 'no':
+                    foundno = True
+                else:
+                    foundunauthorized = True
+
+        action_settings = "{} ({}:{}:{})".format(
+            action_id,
+            *(settings[type] for type in allow_types)
+        )
+
+        if foundunauthorized:
+            printError(
+                pkg, 'polkit-unauthorized-privilege', action_settings)
+        else:
+            printError(
+                pkg, 'polkit-untracked-privilege', action_settings)
+
+        if foundno or foundundef:
+            printInfo(
+                pkg, 'polkit-cant-acquire-privilege', action_settings)
+
+    def check_rules(self, pkg):
+        """Process files and whitelist for entries in rules.d dirs."""
+
+        files = pkg.files()
+        rule_dirs = ("/etc/polkit-1/rules.d/", "/usr/share/polkit-1/rules.d/")
+
+        for f in files:
+            if f in pkg.ghostFiles():
+                continue
+
+            for rule_dir in rule_dirs:
+                if f.startswith(rule_dir):
+                    break
+            else:
+                # no match
+                continue
+
+            pkgs = self.rules.get(f, None)
+            wl_entry = pkgs.get(pkg.name, None) if pkgs else None
+
+            # TODO: at the moment these are only warnings while we're newly
+            # implementing this feature.
+            # We should turn these into errors with badness - or change our
+            # enforcement procedure in OBS to not require this any more.
+
+            if not pkgs or not wl_entry:
+                # no whitelist entry exists for this file
+                printWarning(pkg, 'polkit-unauthorized-rules', f)
+                continue
+
+            if wl_entry["skip-digest-check"]:
+                # for this package/file combination no file content digest
+                # verification needs to be performed, so we're already fine
+                continue
+
+            # check the newest entry first it is more likely to match what we
+            # have
+            for audit in reversed(wl_entry["audits"]):
+                digest_matches = self._checkDigest(pkg, f, audit["digest"])
+
+                if digest_matches:
+                    break
+            else:
+                # none of the digest entries matched
+                printWarning(pkg, 'polkit-changed-rules', f)
+                continue
+
+    def _checkDigest(self, pkg, path, digest_spec):
+        if not digest_spec:
+            return False
+
+        parts = digest_spec.split(':', 1)
+        if len(parts) != 2:
+            print(self._get_err_prefix(), "bad digest specification for 
package {} file {}".format(
+                pkg.name, path),
+                file=sys.stderr
+            )
+            return False
+
+        alg, digest = parts
+
+        try:
+            h = hashlib.new(alg)
+        except ValueError:
+            print(self._get_err_prefix(), "bad digest algorithm '{}' for 
package {} file {}".format(
+                alg, pkg.name, path),
+                file=sys.stderr
+            )
+            return False
+
+        with open(pkg.dirName() + path, 'rb') as fd:
+            while True:
+                data = fd.read(4096)
+                if not data:
+                    break
+
+                h.update(data)
+
+        return h.hexdigest() == digest
+
+    def check(self, pkg):
+
+        if pkg.isSource():
+            return
+
+        self.check_perm_files(pkg)
+        self.check_actions(pkg)
+        self.check_rules(pkg)
+
 
 check = PolkitCheck()
 
@@ -150,9 +343,10 @@
 
 addDetails(
 'polkit-unauthorized-file',
-"""If the package is intended for inclusion in any SUSE product
-please open a bug report to request review of the package by the
-security team. Please refer to {} for more 
information""".format(AUDIT_BUG_URL),
+"""A custom polkit rule file is installed by this package. If the package is
+intended for inclusion in any SUSE product please open a bug report to request
+review of the package by the security team. Please refer to {} for more
+information""".format(AUDIT_BUG_URL),
 
 'polkit-unauthorized-privilege',
 """The package allows unprivileged users to carry out privileged
@@ -174,4 +368,16 @@
 """Usability can be improved by allowing users to acquire privileges
 via authentication. Use e.g. 'auth_admin' instead of 'no' and make
 sure to define 'allow_any'. This is an issue only if the privilege
-is not listed in /etc/polkit-default-privs.*""")
+is not listed in /etc/polkit-default-privs.*""",
+
+'polkit-unauthorized-rules',
+"""A polkit rules file installed by this package is not whitelisted in the
+polkit-whitelisting package. If the package is intended for inclusion in any
+SUSE product please open a bug report to request review of the package by the
+security team. Please refer to {} for more 
information.""".format(AUDIT_BUG_URL),
+
+'polkit-changed-rules',
+"""A polkit rules file installed by this package changed in content. Please
+open a bug report to request follow-up review of the introduced changes by
+the security team. Please refer to {} for more 
information.""".format(AUDIT_BUG_URL),
+)


Reply via email to