Hello community,

here is the log from the commit of package salt for openSUSE:Factory checked in 
at 2020-11-06 23:43:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/salt (Old)
 and      /work/SRC/openSUSE:Factory/.salt.new.11331 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "salt"

Fri Nov  6 23:43:24 2020 rev:111 rq:846425 version:3000.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/salt/salt.changes        2020-10-18 
16:26:06.288615897 +0200
+++ /work/SRC/openSUSE:Factory/.salt.new.11331/salt.changes     2020-11-06 
23:43:27.719452453 +0100
@@ -1,0 +2,34 @@
+Fri Nov  6 09:19:22 UTC 2020 - Pablo Suárez Hernández 
<pablo.suarezhernan...@suse.com>
+
+- Set passphrase for salt-ssh keys to empty string (bsc#1178485)
+
+- Added:
+  * set-passphrase-for-salt-ssh-keys-to-empty-string-293.patch
+
+-------------------------------------------------------------------
+Wed Nov  4 10:54:32 UTC 2020 - Pablo Suárez Hernández 
<pablo.suarezhernan...@suse.com>
+
+- Properly validate eauth credentials and tokens on SSH calls made by Salt API
+  (bsc#1178319) (bsc#1178362) (bsc#1178361)
+  (CVE-2020-25592) (CVE-2020-17490) (CVE-2020-16846)
+
+- Added:
+  * fix-cve-2020-25592-and-add-tests-bsc-1178319.patch
+
+-------------------------------------------------------------------
+Tue Oct 27 15:23:12 UTC 2020 - Pablo Suárez Hernández 
<pablo.suarezhernan...@suse.com>
+
+- Fix novendorchange handling in zypperpkg module
+
+- Added:
+  * fix-novendorchange-option-284.patch
+
+-------------------------------------------------------------------
+Tue Oct 20 11:43:49 UTC 2020 - Pablo Suárez Hernández 
<pablo.suarezhernan...@suse.com>
+
+- Fix disk.blkid to avoid unexpected keyword argument '__pub_user' 
(bsc#1177867)
+
+- Added:
+  * path-replace-functools.wraps-with-six.wraps-bsc-1177.patch
+
+-------------------------------------------------------------------

New:
----
  fix-cve-2020-25592-and-add-tests-bsc-1178319.patch
  fix-novendorchange-option-284.patch
  path-replace-functools.wraps-with-six.wraps-bsc-1177.patch
  set-passphrase-for-salt-ssh-keys-to-empty-string-293.patch

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

Other differences:
------------------
++++++ salt.spec ++++++
--- /var/tmp/diff_new_pack.I9n5XI/_old  2020-11-06 23:43:30.679446765 +0100
+++ /var/tmp/diff_new_pack.I9n5XI/_new  2020-11-06 23:43:30.683446758 +0100
@@ -363,6 +363,14 @@
 Patch139:     drop-wrong-mock-from-chroot-unit-test.patch
 # PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/280
 Patch140:     ensure-virt.update-stop_on_reboot-is-updated-with-it.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/281
+Patch141:     path-replace-functools.wraps-with-six.wraps-bsc-1177.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58560
+Patch142:     fix-novendorchange-option-284.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58871
+Patch143:     fix-cve-2020-25592-and-add-tests-bsc-1178319.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/293
+Patch144:     set-passphrase-for-salt-ssh-keys-to-empty-string-293.patch
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  logrotate
@@ -1010,6 +1018,10 @@
 %patch138 -p1
 %patch139 -p1
 %patch140 -p1
+%patch141 -p1
+%patch142 -p1
+%patch143 -p1
+%patch144 -p1
 
 %build
 # Putting /usr/bin at the front of $PATH is needed for RHEL/RES 7. Without this

++++++ _lastrevision ++++++
--- /var/tmp/diff_new_pack.I9n5XI/_old  2020-11-06 23:43:30.751446627 +0100
+++ /var/tmp/diff_new_pack.I9n5XI/_new  2020-11-06 23:43:30.755446619 +0100
@@ -1 +1 @@
-3ce95a1b386927b6f8cb27d1a6421018bebccd9a
\ No newline at end of file
+24bd64b440c2c3f0f154a1b7f7216de20dc07df8
\ No newline at end of file

++++++ fix-cve-2020-25592-and-add-tests-bsc-1178319.patch ++++++
>From e7514afcba4f57c5cb8599f561fcefdcc3db7314 Mon Sep 17 00:00:00 2001
From: "Daniel A. Wozniak" <dwozn...@saltstack.com>
Date: Wed, 16 Sep 2020 00:25:10 +0000
Subject: [PATCH] Fix CVE-2020-25592 and add tests (bsc#1178319)

Properly validate eauth credentials and tokens on SSH calls made by Salt API

(bsc#1178319) (bsc#1178362) (bsc#1178361) (CVE-2020-25592) (CVE-2020-17490) 
(CVE-2020-16846)
---
 salt/client/ssh/shell.py                |  26 ++-
 salt/modules/tls.py                     |  18 +-
 salt/netapi/__init__.py                 |  67 ++++++
 tests/integration/netapi/test_client.py | 296 +++++++++++++++++++++++-
 4 files changed, 388 insertions(+), 19 deletions(-)

diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py
index bd55c514ee..27aba7b382 100644
--- a/salt/client/ssh/shell.py
+++ b/salt/client/ssh/shell.py
@@ -8,6 +8,7 @@ from __future__ import absolute_import, print_function, 
unicode_literals
 import re
 import os
 import sys
+import shlex
 import time
 import logging
 import subprocess
@@ -43,10 +44,10 @@ def gen_key(path):
     '''
     Generate a key for use with salt-ssh
     '''
-    cmd = 'ssh-keygen -P "" -f {0} -t rsa -q'.format(path)
+    cmd = ["ssh-keygen", "-P", '""', "-f", path, "-t", "rsa", "-q"]
     if not os.path.isdir(os.path.dirname(path)):
         os.makedirs(os.path.dirname(path))
-    subprocess.call(cmd, shell=True)
+    subprocess.call(cmd)
 
 
 def gen_shell(opts, **kwargs):
@@ -289,8 +290,7 @@ class Shell(object):
         '''
         try:
             proc = salt.utils.nb_popen.NonBlockingPopen(
-                cmd,
-                shell=True,
+                self._split_cmd(cmd),
                 stderr=subprocess.PIPE,
                 stdout=subprocess.PIPE,
             )
@@ -369,6 +369,21 @@ class Shell(object):
 
         return self._run_cmd(cmd)
 
+    def _split_cmd(self, cmd):
+        """
+        Split a command string so that it is suitable to pass to Popen without
+        shell=True. This prevents shell injection attacks in the options passed
+        to ssh or some other command.
+        """
+        try:
+            ssh_part, cmd_part = cmd.split("/bin/sh")
+        except ValueError:
+            cmd_lst = shlex.split(cmd)
+        else:
+            cmd_lst = shlex.split(ssh_part)
+            cmd_lst.append("/bin/sh {}".format(cmd_part))
+        return cmd_lst
+
     def _run_cmd(self, cmd, key_accept=False, passwd_retries=3):
         '''
         Execute a shell command via VT. This is blocking and assumes that ssh
@@ -378,8 +393,7 @@ class Shell(object):
             return '', 'No command or passphrase', 245
 
         term = salt.utils.vt.Terminal(
-                cmd,
-                shell=True,
+                self._split_cmd(cmd),
                 log_stdout=True,
                 log_stdout_level='trace',
                 log_stderr=True,
diff --git a/salt/modules/tls.py b/salt/modules/tls.py
index af845621a3..116b5fe379 100644
--- a/salt/modules/tls.py
+++ b/salt/modules/tls.py
@@ -798,12 +798,13 @@ def create_ca(ca_name,
             if old_key.strip() == keycontent.strip():
                 write_key = False
             else:
-                log.info('Saving old CA ssl key in %s', bck)
-                with salt.utils.files.fopen(bck, 'w') as bckf:
+                log.info('Saving old CA ssl key in {0}'.format(bck))
+                fp = os.open(bck, os.O_CREAT | os.O_RDWR, 0o600)
+                with os.fdopen(fp, 'w') as bckf:
                     bckf.write(old_key)
-                    os.chmod(bck, 0o600)
     if write_key:
-        with salt.utils.files.fopen(ca_keyp, 'wb') as ca_key:
+        fp = os.open(ca_keyp, os.O_CREAT | os.O_RDWR, 0o600)
+        with os.fdopen(fp, 'wb') as ca_key:
             ca_key.write(salt.utils.stringutils.to_bytes(keycontent))
 
     with salt.utils.files.fopen(certp, 'wb') as ca_crt:
@@ -1115,9 +1116,9 @@ def create_csr(ca_name,
     req.sign(key, salt.utils.stringutils.to_str(digest))
 
     # Write private key and request
-    with salt.utils.files.fopen('{0}/{1}.key'.format(csr_path,
-                                                     csr_filename),
-                                'wb+') as priv_key:
+    priv_keyp = '{0}/{1}.key'.format(csr_path, csr_filename)
+    fp = os.open(priv_keyp, os.O_CREAT | os.O_RDWR, 0o600)
+    with os.fdopen(fp, 'wb+') as priv_key:
         priv_key.write(
             salt.utils.stringutils.to_bytes(
                 OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,
@@ -1266,7 +1267,8 @@ def create_self_signed_cert(tls_dir='tls',
     priv_key_path = '{0}/{1}/certs/{2}.key'.format(cert_base_path(),
                                                    tls_dir,
                                                    cert_filename)
-    with salt.utils.files.fopen(priv_key_path, 'wb+') as priv_key:
+    fp = os.open(priv_key_path, os.O_CREAT | os.O_RDWR, 0o600)
+    with os.fdopen(fp, 'wb+') as priv_key:
         priv_key.write(
             salt.utils.stringutils.to_bytes(
                 OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,
diff --git a/salt/netapi/__init__.py b/salt/netapi/__init__.py
index 31a24bb420..4e5b6b093a 100644
--- a/salt/netapi/__init__.py
+++ b/salt/netapi/__init__.py
@@ -3,24 +3,36 @@
 Make api awesomeness
 '''
 from __future__ import absolute_import, print_function, unicode_literals
+
+import copy
+
 # Import Python libs
 import inspect
+import logging
 import os
 
 # Import Salt libs
 import salt.log  # pylint: disable=W0611
+import salt.auth
 import salt.client
 import salt.config
+import salt.daemons.masterapi
 import salt.runner
 import salt.syspaths
 import salt.wheel
 import salt.utils.args
 import salt.client.ssh.client
 import salt.exceptions
+import salt.utils.args
+import salt.utils.minions
+import salt.wheel
+from salt.defaults import DEFAULT_TARGET_DELIM
 
 # Import third party libs
 from salt.ext import six
 
+log = logging.getLogger(__name__)
+
 
 class NetapiClient(object):
     '''
@@ -34,6 +46,15 @@ class NetapiClient(object):
 
     def __init__(self, opts):
         self.opts = opts
+        apiopts = copy.deepcopy(self.opts)
+        apiopts["enable_ssh_minions"] = True
+        apiopts["cachedir"] = os.path.join(opts["cachedir"], "saltapi")
+        if not os.path.exists(apiopts["cachedir"]):
+            os.makedirs(apiopts["cachedir"])
+        self.resolver = salt.auth.Resolver(apiopts)
+        self.loadauth = salt.auth.LoadAuth(apiopts)
+        self.key = salt.daemons.masterapi.access_keys(apiopts)
+        self.ckminions = salt.utils.minions.CkMinions(apiopts)
 
     def _is_master_running(self):
         '''
@@ -55,6 +76,49 @@ class NetapiClient(object):
             self.opts['sock_dir'],
             ipc_file))
 
+    def _prep_auth_info(self, clear_load):
+        sensitive_load_keys = []
+        key = None
+        if "token" in clear_load:
+            auth_type = "token"
+            err_name = "TokenAuthenticationError"
+            sensitive_load_keys = ["token"]
+            return auth_type, err_name, key, sensitive_load_keys
+        elif "eauth" in clear_load:
+            auth_type = "eauth"
+            err_name = "EauthAuthenticationError"
+            sensitive_load_keys = ["username", "password"]
+            return auth_type, err_name, key, sensitive_load_keys
+        raise salt.exceptions.EauthAuthenticationError(
+            "No authentication credentials given"
+        )
+
+    def _authorize_ssh(self, low):
+        auth_type, err_name, key, sensitive_load_keys = 
self._prep_auth_info(low)
+        auth_check = self.loadauth.check_authentication(low, auth_type, 
key=key)
+        auth_list = auth_check.get("auth_list", [])
+        error = auth_check.get("error")
+        if error:
+            raise salt.exceptions.EauthAuthenticationError(error)
+        delimiter = low.get("kwargs", {}).get("delimiter", 
DEFAULT_TARGET_DELIM)
+        _res = self.ckminions.check_minions(
+            low["tgt"], low.get("tgt_type", "glob"), delimiter
+        )
+        minions = _res.get("minions", list())
+        missing = _res.get("missing", list())
+        authorized = self.ckminions.auth_check(
+            auth_list,
+            low["fun"],
+            low.get("arg", []),
+            low["tgt"],
+            low.get("tgt_type", "glob"),
+            minions=minions,
+        )
+        if not authorized:
+            raise salt.exceptions.EauthAuthenticationError(
+                "Authorization error occurred."
+            )
+
     def run(self, low):
         '''
         Execute the specified function in the specified client by passing the
@@ -80,6 +144,9 @@ class NetapiClient(object):
             raise salt.exceptions.EauthAuthenticationError(
                     'Raw shell option not allowed.')
 
+        if low['client'] == 'ssh':
+            self._authorize_ssh(low)
+
         l_fun = getattr(self, low['client'])
         f_call = salt.utils.args.format_call(l_fun, low)
         return l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {}))
diff --git a/tests/integration/netapi/test_client.py 
b/tests/integration/netapi/test_client.py
index 08030f31ec..b99bdfe313 100644
--- a/tests/integration/netapi/test_client.py
+++ b/tests/integration/netapi/test_client.py
@@ -1,26 +1,30 @@
 # encoding: utf-8
-
 # Import Python libs
 from __future__ import absolute_import, print_function, unicode_literals
+import copy
 import logging
 import os
 import time
 
+import salt.config
+import salt.netapi
+import salt.utils.files
+import salt.utils.platform
+import salt.utils.pycrypto
+
 # Import Salt Testing libs
 from tests.support.paths import TMP_CONF_DIR, TMP
 from tests.support.runtests import RUNTIME_VARS
 from tests.support.unit import TestCase, skipIf
 from tests.support.mock import patch
-from tests.support.case import SSHCase
+from tests.support.case import ModuleCase, SSHCase
+from salt.exceptions import EauthAuthenticationError
 from tests.support.helpers import (
     Webserver,
     SaveRequestsPostHandler,
     requires_sshd_server
 )
 
-# Import Salt libs
-import salt.config
-import salt.netapi
 
 from salt.exceptions import (
     EauthAuthenticationError
@@ -174,6 +178,10 @@ class NetapiSSHClientTest(SSHCase):
         '''
         opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, 'master'))
         self.netapi = salt.netapi.NetapiClient(opts)
+        opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, "master"))
+        naopts = copy.deepcopy(opts)
+        naopts["ignore_host_keys"] = True
+        self.netapi = salt.netapi.NetapiClient(naopts)
 
         self.priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test')
         self.rosters = os.path.join(RUNTIME_VARS.TMP_CONF_DIR)
@@ -271,3 +279,281 @@ class NetapiSSHClientTest(SSHCase):
 
         self.assertEqual(ret, None)
         self.assertFalse(os.path.exists('badfile.txt'))
+
+    @staticmethod
+    def cleanup_file(path):
+        try:
+            os.remove(path)
+        except OSError:
+            pass
+
+    @staticmethod
+    def cleanup_dir(path):
+        try:
+            salt.utils.files.rm_rf(path)
+        except OSError:
+            pass
+
+    def test_shell_inject_ssh_priv(self):
+        """
+        Verify CVE-2020-16846 for ssh_priv variable
+        """
+        # ZDI-CAN-11143
+        path = "/tmp/test-11143"
+        self.addCleanup(self.cleanup_file, path)
+        self.addCleanup(self.cleanup_file, "aaa")
+        self.addCleanup(self.cleanup_file, "aaa.pub")
+        self.addCleanup(self.cleanup_dir, "aaa|id>")
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "www.zerodayinitiative.com",
+            "ssh_priv": "aaa|id>{} #".format(path),
+            "fun": "test.ping",
+            "eauth": "auto",
+            "username": "saltdev_auto",
+            "password": "saltdev",
+        }
+        ret = self.netapi.run(low)
+        self.assertFalse(os.path.exists(path))
+
+    def test_shell_inject_tgt(self):
+        """
+        Verify CVE-2020-16846 for tgt variable
+        """
+        # ZDI-CAN-11167
+        path = "/tmp/test-11167"
+        self.addCleanup(self.cleanup_file, path)
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "root|id>{} #@127.0.0.1".format(path),
+            "roster_file": "/tmp/salt-tests-tmpdir/config/roaster",
+            "rosters": "/",
+            "fun": "test.ping",
+            "eauth": "auto",
+            "username": "saltdev_auto",
+            "password": "saltdev",
+        }
+        ret = self.netapi.run(low)
+        self.assertFalse(os.path.exists(path))
+
+    def test_shell_inject_ssh_options(self):
+        """
+        Verify CVE-2020-16846 for ssh_options
+        """
+        # ZDI-CAN-11169
+        path = "/tmp/test-11169"
+        self.addCleanup(self.cleanup_file, path)
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "127.0.0.1",
+            "renderer": "cheetah",
+            "fun": "test.ping",
+            "eauth": "auto",
+            "username": "saltdev_auto",
+            "password": "saltdev",
+            "roster_file": "/tmp/salt-tests-tmpdir/config/roaster",
+            "rosters": "/",
+            "ssh_options": ["|id>{} #".format(path), "lol"],
+        }
+        ret = self.netapi.run(low)
+        self.assertFalse(os.path.exists(path))
+
+    def test_shell_inject_ssh_port(self):
+        """
+        Verify CVE-2020-16846 for ssh_port variable
+        """
+        # ZDI-CAN-11172
+        path = "/tmp/test-11172"
+        self.addCleanup(self.cleanup_file, path)
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "127.0.0.1",
+            "renderer": "cheetah",
+            "fun": "test.ping",
+            "eauth": "auto",
+            "username": "saltdev_auto",
+            "password": "saltdev",
+            "roster_file": "/tmp/salt-tests-tmpdir/config/roaster",
+            "rosters": "/",
+            "ssh_port": "hhhhh|id>{} #".format(path),
+        }
+        ret = self.netapi.run(low)
+        self.assertFalse(os.path.exists(path))
+
+    def test_shell_inject_remote_port_forwards(self):
+        """
+        Verify CVE-2020-16846 for remote_port_forwards variable
+        """
+        # ZDI-CAN-11173
+        path = "/tmp/test-1173"
+        self.addCleanup(self.cleanup_file, path)
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "127.0.0.1",
+            "renderer": "cheetah",
+            "fun": "test.ping",
+            "roster_file": "/tmp/salt-tests-tmpdir/config/roaster",
+            "rosters": "/",
+            "ssh_remote_port_forwards": "hhhhh|id>{} #, lol".format(path),
+            "eauth": "auto",
+            "username": "saltdev_auto",
+            "password": "saltdev",
+        }
+        ret = self.netapi.run(low)
+        self.assertFalse(os.path.exists(path))
+
+
+@requires_sshd_server
+class NetapiSSHClientAuthTest(SSHCase):
+
+    USERA = "saltdev"
+    USERA_PWD = "saltdev"
+
+    def setUp(self):
+        """
+        Set up a NetapiClient instance
+        """
+        opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, "master"))
+        naopts = copy.deepcopy(opts)
+        naopts["ignore_host_keys"] = True
+        self.netapi = salt.netapi.NetapiClient(naopts)
+
+        self.priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test")
+        self.rosters = os.path.join(RUNTIME_VARS.TMP_CONF_DIR)
+        # Initialize salt-ssh
+        self.run_function("test.ping")
+        self.mod_case = ModuleCase()
+        try:
+            add_user = self.mod_case.run_function(
+                "user.add", [self.USERA], createhome=False
+            )
+            self.assertTrue(add_user)
+            if salt.utils.platform.is_darwin():
+                hashed_password = self.USERA_PWD
+            else:
+                hashed_password = 
salt.utils.pycrypto.gen_hash(password=self.USERA_PWD)
+            add_pwd = self.mod_case.run_function(
+                "shadow.set_password", [self.USERA, hashed_password],
+            )
+            self.assertTrue(add_pwd)
+        except AssertionError:
+            self.mod_case.run_function("user.delete", [self.USERA], 
remove=True)
+            self.skipTest("Could not add user or password, skipping test")
+
+    def tearDown(self):
+        del self.netapi
+        self.mod_case.run_function("user.delete", [self.USERA], remove=True)
+
+    @classmethod
+    def setUpClass(cls):
+        cls.post_webserver = Webserver(handler=SaveRequestsPostHandler)
+        cls.post_webserver.start()
+        cls.post_web_root = cls.post_webserver.web_root
+        cls.post_web_handler = cls.post_webserver.handler
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.post_webserver.stop()
+        del cls.post_webserver
+
+    def test_ssh_auth_bypass(self):
+        """
+        CVE-2020-25592 - Bogus eauth raises exception.
+        """
+        low = {
+            "roster": "cache",
+            "client": "ssh",
+            "tgt": "127.0.0.1",
+            "renderer": "cheetah",
+            "fun": "test.ping",
+            "roster_file": "/tmp/salt-tests-tmpdir/config/roaster",
+            "rosters": "/",
+            "eauth": "xx",
+        }
+        with self.assertRaises(salt.exceptions.EauthAuthenticationError):
+            ret = self.netapi.run(low)
+
+    def test_ssh_auth_valid(self):
+        """
+        CVE-2020-25592 - Valid eauth works as expected.
+        """
+        low = {
+            "client": "ssh",
+            "tgt": "localhost",
+            "fun": "test.ping",
+            "roster_file": "roster",
+            "rosters": [self.rosters],
+            "ssh_priv": self.priv_file,
+            "eauth": "pam",
+            "username": "saltdev",
+            "password": "saltdev",
+        }
+        ret = self.netapi.run(low)
+        assert "localhost" in ret
+        assert ret["localhost"]["return"] is True
+
+    def test_ssh_auth_invalid(self):
+        """
+        CVE-2020-25592 - Wrong password raises exception.
+        """
+        low = {
+            "client": "ssh",
+            "tgt": "localhost",
+            "fun": "test.ping",
+            "roster_file": "roster",
+            "rosters": [self.rosters],
+            "ssh_priv": self.priv_file,
+            "eauth": "pam",
+            "username": "saltdev",
+            "password": "notvalidpassword",
+        }
+        with self.assertRaises(salt.exceptions.EauthAuthenticationError):
+            ret = self.netapi.run(low)
+
+    def test_ssh_auth_invalid_acl(self):
+        """
+        CVE-2020-25592 - Eauth ACL enforced.
+        """
+        low = {
+            "client": "ssh",
+            "tgt": "localhost",
+            "fun": "at.at",
+            "args": ["12:05am", "echo foo"],
+            "roster_file": "roster",
+            "rosters": [self.rosters],
+            "ssh_priv": self.priv_file,
+            "eauth": "pam",
+            "username": "saltdev",
+            "password": "notvalidpassword",
+        }
+        with self.assertRaises(salt.exceptions.EauthAuthenticationError):
+            ret = self.netapi.run(low)
+
+    def test_ssh_auth_token(self):
+        """
+        CVE-2020-25592 - Eauth tokens work as expected.
+        """
+        low = {
+            "eauth": "pam",
+            "username": "saltdev",
+            "password": "saltdev",
+        }
+        ret = self.netapi.loadauth.mk_token(low)
+        assert "token" in ret and ret["token"]
+        low = {
+            "client": "ssh",
+            "tgt": "localhost",
+            "fun": "test.ping",
+            "roster_file": "roster",
+            "rosters": [self.rosters],
+            "ssh_priv": self.priv_file,
+            "token": ret["token"],
+        }
+        ret = self.netapi.run(low)
+        assert "localhost" in ret
+        assert ret["localhost"]["return"] is True
-- 
2.28.0


++++++ fix-novendorchange-option-284.patch ++++++
>From f69c1178de003866af412e61e0146597974eec0d Mon Sep 17 00:00:00 2001
From: Martin Seidl <mse...@suse.de>
Date: Tue, 27 Oct 2020 16:12:29 +0100
Subject: [PATCH] Fix novendorchange option (#284)

* Fixed novendorchange handling in zypperpkg

* refactor handling of novendorchange and fix tests
---
 salt/modules/zypperpkg.py            |  19 ++--
 tests/unit/modules/test_zypperpkg.py | 150 ++++++++++++++++++++++++---
 2 files changed, 148 insertions(+), 21 deletions(-)

diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py
index ad11da4ad1..d84a6af6e0 100644
--- a/salt/modules/zypperpkg.py
+++ b/salt/modules/zypperpkg.py
@@ -1617,7 +1617,7 @@ def upgrade(refresh=True,
             dryrun=False,
             dist_upgrade=False,
             fromrepo=None,
-            novendorchange=False,
+            novendorchange=True,
             skip_verify=False,
             no_recommends=False,
             root=None,
@@ -1701,13 +1701,18 @@ def upgrade(refresh=True,
         log.info('Targeting repos: %s', fromrepo)
 
     if dist_upgrade:
-        if novendorchange:
-            # TODO: Grains validation should be moved to Zypper class
-            if __grains__['osrelease_info'][0] > 11:
-                cmd_update.append('--no-allow-vendor-change')
-                log.info('Disabling vendor changes')
+        # TODO: Grains validation should be moved to Zypper class
+        if __grains__["osrelease_info"][0] > 11:
+            if novendorchange:
+                cmd_update.append("--no-allow-vendor-change")
+                log.info("Disabling vendor changes")
             else:
-                log.warning('Disabling vendor changes is not supported on this 
Zypper version')
+                cmd_update.append("--allow-vendor-change")
+                log.info("Enabling vendor changes")
+        else:
+            log.warning(
+                "Enabling/Disabling vendor changes is not supported on this 
Zypper version"
+            )
 
         if no_recommends:
             cmd_update.append('--no-recommends')
diff --git a/tests/unit/modules/test_zypperpkg.py 
b/tests/unit/modules/test_zypperpkg.py
index a3d20f66d5..8cc84485b5 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -480,7 +480,11 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
                 with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])):
                     ret = zypper.upgrade(dist_upgrade=True)
                     self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": 
"1.2"}})
-                    zypper_mock.assert_any_call('dist-upgrade', 
'--auto-agree-with-licenses')
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--no-allow-vendor-change",
+                    )
 
                 with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])):
                     ret = zypper.upgrade(dist_upgrade=True, dryrun=True)
@@ -488,25 +492,138 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
                     zypper_mock.assert_any_call('dist-upgrade', 
'--auto-agree-with-licenses',
                                                 '--dry-run', '--debug-solver')
 
-                with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])):
-                    ret = zypper.upgrade(dist_upgrade=True, dryrun=True,
-                                         fromrepo=["Dummy", "Dummy2"], 
novendorchange=True)
-                    zypper_mock.assert_any_call('dist-upgrade', 
'--auto-agree-with-licenses', '--dry-run',
-                                                '--from', "Dummy", '--from', 
'Dummy2', '--no-allow-vendor-change')
-                    zypper_mock.assert_any_call('dist-upgrade', 
'--auto-agree-with-licenses', '--dry-run',
-                                                '--from', "Dummy", '--from', 
'Dummy2', '--no-allow-vendor-change',
-                                                '--debug-solver')
-
                 with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])):
                     ret = zypper.upgrade(dist_upgrade=False, 
fromrepo=["Dummy", "Dummy2"], dryrun=False)
                     zypper_mock.assert_any_call('update', 
'--auto-agree-with-licenses', '--repo', "Dummy", '--repo', 'Dummy2')
 
                 with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])):
                     ret = zypper.upgrade(dist_upgrade=True, fromrepo=["Dummy", 
"Dummy2"], novendorchange=True)
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--no-allow-vendor-change",
+                    )
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--no-allow-vendor-change",
+                    )
+
+                with patch(
+                    "salt.modules.zypperpkg.list_pkgs",
+                    MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]),
+                ):
+                    ret = zypper.upgrade(
+                        dist_upgrade=True,
+                        dryrun=True,
+                        fromrepo=["Dummy", "Dummy2"],
+                        novendorchange=False,
+                    )
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--from",
+                        "Dummy",
+                        "--from",
+                        "Dummy2",
+                        "--allow-vendor-change",
+                    )
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--from",
+                        "Dummy",
+                        "--from",
+                        "Dummy2",
+                        "--allow-vendor-change",
+                        "--debug-solver",
+                    )
+
+
+                with patch(
+                    "salt.modules.zypperpkg.list_pkgs",
+                    MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]),
+                ):
+                    ret = zypper.upgrade(
+                        dist_upgrade=True,
+                        dryrun=True,
+                        fromrepo=["Dummy", "Dummy2"],
+                        novendorchange=True,
+                    )
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--from",
+                        "Dummy",
+                        "--from",
+                        "Dummy2",
+                        "--no-allow-vendor-change",
+                    )
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--dry-run",
+                        "--from",
+                        "Dummy",
+                        "--from",
+                        "Dummy2",
+                        "--no-allow-vendor-change",
+                        "--debug-solver",
+                    )
+
+                with patch(
+                    "salt.modules.zypperpkg.list_pkgs",
+                    MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]),
+                ):
+                    ret = zypper.upgrade(
+                        dist_upgrade=False, fromrepo=["Dummy", "Dummy2"], 
dryrun=False
+                    )
+                    zypper_mock.assert_any_call(
+                        "update",
+                        "--auto-agree-with-licenses",
+                        "--repo",
+                        "Dummy",
+                        "--repo",
+                        "Dummy2",
+                    )
+
+                with patch(
+                    "salt.modules.zypperpkg.list_pkgs",
+                    MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]),
+                ):
+                    ret = zypper.upgrade(
+                        dist_upgrade=True,
+                        fromrepo=["Dummy", "Dummy2"],
+                        novendorchange=True,
+                    )
                     self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": 
"1.2"}})
                     zypper_mock.assert_any_call('dist-upgrade', 
'--auto-agree-with-licenses', '--from', "Dummy",
                                                 '--from', 'Dummy2', 
'--no-allow-vendor-change')
 
+                with patch(
+                    "salt.modules.zypperpkg.list_pkgs",
+                    MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]),
+                ):
+                    ret = zypper.upgrade(
+                        dist_upgrade=True,
+                        fromrepo=["Dummy", "Dummy2"],
+                        novendorchange=False,
+                    )
+                    self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": 
"1.2"}})
+                    zypper_mock.assert_any_call(
+                        "dist-upgrade",
+                        "--auto-agree-with-licenses",
+                        "--from",
+                        "Dummy",
+                        "--from",
+                        "Dummy2",
+                        "--allow-vendor-change",
+                    )
     def test_upgrade_kernel(self):
         '''
         Test kernel package upgrade success.
@@ -558,10 +675,15 @@ Repository 'DUMMY' not found by its alias, number, or URI.
             with patch('salt.modules.zypperpkg.list_pkgs', 
MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])):
                 with self.assertRaises(CommandExecutionError) as cmd_exc:
                     ret = zypper.upgrade(dist_upgrade=True, fromrepo=["DUMMY"])
-                self.assertEqual(cmd_exc.exception.info['changes'], {})
-                self.assertEqual(cmd_exc.exception.info['result']['stdout'], 
zypper_out)
-                zypper_mock.noraise.call.assert_called_with('dist-upgrade', 
'--auto-agree-with-licenses',
-                                                            '--from', 'DUMMY')
+                self.assertEqual(cmd_exc.exception.info["changes"], {})
+                self.assertEqual(cmd_exc.exception.info["result"]["stdout"], 
zypper_out)
+                zypper_mock.noraise.call.assert_called_with(
+                    "dist-upgrade",
+                    "--auto-agree-with-licenses",
+                    "--from",
+                    "DUMMY",
+                    "--no-allow-vendor-change",
+                )
 
     def test_upgrade_available(self):
         '''
-- 
2.28.0


++++++ path-replace-functools.wraps-with-six.wraps-bsc-1177.patch ++++++
>From 9707eab7452a94e64f77ece707c31c37e43e47f2 Mon Sep 17 00:00:00 2001
From: Alberto Planas <apla...@suse.com>
Date: Tue, 20 Oct 2020 13:13:24 +0200
Subject: [PATCH] path: replace functools.wraps with six.wraps
 (bsc#1177867) (#283)

Python 2.7 functools.wraps decorator do not add the `__wrapped__`
attribute to decorated functions, that is used by Salt to access the
original function and deduce the parameters from the signature.

This patch uses six.wraps, that add this extra attribute.
---
 salt/utils/decorators/path.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/salt/utils/decorators/path.py b/salt/utils/decorators/path.py
index 8ee7fb1d11..a763c92d22 100644
--- a/salt/utils/decorators/path.py
+++ b/salt/utils/decorators/path.py
@@ -4,11 +4,10 @@ Decorators for salt.utils.path
 '''
 from __future__ import absolute_import, print_function, unicode_literals
 
-import functools
-
 # Import Salt libs
 import salt.utils.path
 from salt.exceptions import CommandNotFoundError
+import salt.ext.six
 
 
 def which(exe):
@@ -16,7 +15,7 @@ def which(exe):
     Decorator wrapper for salt.utils.path.which
     '''
     def wrapper(function):
-        @functools.wraps(function)
+        @salt.ext.six.wraps(function)
         def wrapped(*args, **kwargs):
             if salt.utils.path.which(exe) is None:
                 raise CommandNotFoundError(
@@ -34,7 +33,7 @@ def which_bin(exes):
     Decorator wrapper for salt.utils.path.which_bin
     '''
     def wrapper(function):
-        @functools.wraps(function)
+        @salt.ext.six.wraps(function)
         def wrapped(*args, **kwargs):
             if salt.utils.path.which_bin(exes) is None:
                 raise CommandNotFoundError(
-- 
2.28.0


++++++ set-passphrase-for-salt-ssh-keys-to-empty-string-293.patch ++++++
>From 677b7a8881a2e9ebab58cead29b1a6d83850c888 Mon Sep 17 00:00:00 2001
From: Alexander Graul <m...@agraul.de>
Date: Thu, 5 Nov 2020 16:54:44 +0100
Subject: [PATCH] Set passphrase for salt-ssh keys to empty string
 (#293)

Since the cmd is not passed to a shell anymore, the "" are taken
literally and not as an empty string.

Bugzilla report: https://bugzilla.suse.com/show_bug.cgi?id=1178485
---
 salt/client/ssh/shell.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py
index 27aba7b382..27ab9f4f1b 100644
--- a/salt/client/ssh/shell.py
+++ b/salt/client/ssh/shell.py
@@ -44,7 +44,7 @@ def gen_key(path):
     '''
     Generate a key for use with salt-ssh
     '''
-    cmd = ["ssh-keygen", "-P", '""', "-f", path, "-t", "rsa", "-q"]
+    cmd = ["ssh-keygen", "-P", "", "-f", path, "-t", "rsa", "-q"]
     if not os.path.isdir(os.path.dirname(path)):
         os.makedirs(os.path.dirname(path))
     subprocess.call(cmd)
-- 
2.28.0



Reply via email to