diff --git a/CVE-2020-10684.patch b/CVE-2020-10684.patch index 4a1d623f94ea4f788df5c2149cc3474c954a24f8..8d8a995288e0a5dd77a85605eaabbb10d4ae3ee0 100644 --- a/CVE-2020-10684.patch +++ b/CVE-2020-10684.patch @@ -51,10 +51,10 @@ index c83709d..0a4fe07 100644 - # exceptions to 'deprefixing' - deprefixed[k] = deepcopy(facts[k]) + if k.startswith('ansible_') and k not in ('ansible_local',): -+ deprefixed[k[8:]] = module_response_deepcopy(facts[k]) ++ deprefixed[k[8:]] = deepcopy(facts[k]) else: - deprefixed[k.replace('ansible_', '', 1)] = deepcopy(facts[k]) -+ deprefixed[k] = module_response_deepcopy(facts[k]) ++ deprefixed[k] = deepcopy(facts[k]) return {'ansible_facts': deprefixed} diff --git a/test/integration/targets/gathering_facts/library/bogus_facts b/test/integration/targets/gathering_facts/library/bogus_facts diff --git a/CVE-2020-1735.patch b/CVE-2020-1735.patch index 7bdaf49406edf195d6f6281768f0dd93b891b0f6..89f539588dc35223f10b5110ea54ea1afe69cd37 100644 --- a/CVE-2020-1735.patch +++ b/CVE-2020-1735.patch @@ -1,53 +1,100 @@ -From 5292482553dc409081f7f4368398358cbf9f8672 Mon Sep 17 00:00:00 2001 -From: Brian Coca -Date: Wed, 8 Apr 2020 14:28:51 -0400 +From c1dedd38cb6a3ad215cf077c533c93bd978acb93 Mon Sep 17 00:00:00 2001 +From: caodongxia <315816521@qq.com> +Date: Tue, 2 Nov 2021 17:26:43 +0800 Subject: [PATCH] fixed fetch traversal from slurp (#68720) +Reference:https://github.com/ansible/ansible/commit/290bfa820d533dc224e0c3fa7dd7c6b907ed0189.patch -* fixed fetch traversal from slurp - - * ignore slurp result for dest - * fixed naming when source is relative - * fixed bug in local connection plugin - * added tests with fake slurp - * moved existing role tests into runme.sh - * normalized on action excepts - * moved dest transform down to when needed - * added is_subpath check -│ * fixed bug in local connection -fixes #67793 - -CVE-2019-3828 - -(cherry picked from commit ba87c225cd13343c35075fe7fc15b4cf1343fed6) ---- - changelogs/fragments/fetch_no_slurp.yml | 2 ++ - lib/ansible/plugins/action/fetch.py | 23 ++++++--------- - .../fetch/injection/avoid_slurp_return.yml | 26 +++++++++++++++++ + changelogs/fragments/fetch_no_slurp.yml | 2 + + lib/ansible/plugins/action/fetch.py | 44 ++++++++----------- + lib/ansible/utils/path.py | 22 ++++++++++ + test/integration/targets/fetch/aliases | 1 + + .../fetch/injection/avoid_slurp_return.yml | 26 +++++++++++ .../targets/fetch/injection/here.txt | 1 + - .../targets/fetch/injection/library/slurp.py | 29 +++++++++++++++++++ - .../targets/fetch/run_fetch_tests.yml | 5 ++++ - test/integration/targets/fetch/runme.sh | 12 ++++++++ - 7 files changed, 84 insertions(+), 14 deletions(-) + .../targets/fetch/injection/library/slurp.py | 28 ++++++++++++ + .../targets/fetch/run_fetch_tests.yml | 5 +++ + test/integration/targets/fetch/runme.sh | 12 +++++ + 9 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 changelogs/fragments/fetch_no_slurp.yml create mode 100644 test/integration/targets/fetch/injection/avoid_slurp_return.yml create mode 100644 test/integration/targets/fetch/injection/here.txt create mode 100644 test/integration/targets/fetch/injection/library/slurp.py create mode 100644 test/integration/targets/fetch/run_fetch_tests.yml - create mode 100755 test/integration/targets/fetch/runme.sh + create mode 100644 test/integration/targets/fetch/runme.sh diff --git a/changelogs/fragments/fetch_no_slurp.yml b/changelogs/fragments/fetch_no_slurp.yml new file mode 100644 -index 0000000..c742d40 ---- /dev/null +index 0000000000000..c742d40c3ba82 +--- /dev/null +++ b/changelogs/fragments/fetch_no_slurp.yml @@ -0,0 +1,2 @@ +bugfixes: + - In fetch action, avoid using slurp return to set up dest, also ensure no dir traversal CVE-2019-3828. diff --git a/lib/ansible/plugins/action/fetch.py b/lib/ansible/plugins/action/fetch.py -index fbaf992..471732c 100644 +index fbaf992..7db5b43 100644 --- a/lib/ansible/plugins/action/fetch.py +++ b/lib/ansible/plugins/action/fetch.py -@@ -106,12 +106,6 @@ class ActionModule(ActionBase): +@@ -20,13 +20,13 @@ __metaclass__ = type + import os + import base64 + +-from ansible.errors import AnsibleError ++from ansible.errors import AnsibleActionFail, AnsibleActionSkip + from ansible.module_utils._text import to_bytes + from ansible.module_utils.six import string_types + from ansible.module_utils.parsing.convert_bool import boolean + from ansible.plugins.action import ActionBase + from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash +-from ansible.utils.path import makedirs_safe ++from ansible.utils.path import makedirs_safe, is_subpath + + try: + from __main__ import display +@@ -47,24 +47,23 @@ class ActionModule(ActionBase): + + try: + if self._play_context.check_mode: +- result['skipped'] = True +- result['msg'] = 'check mode not (yet) supported for this module' +- return result ++ raise AnsibleActionSkip('check mode not (yet) supported for this module') + + source = self._task.args.get('src', None) +- dest = self._task.args.get('dest', None) ++ original_dest = dest = self._task.args.get('dest', None) + flat = boolean(self._task.args.get('flat'), strict=False) + fail_on_missing = boolean(self._task.args.get('fail_on_missing', True), strict=False) + validate_checksum = boolean(self._task.args.get('validate_checksum', + self._task.args.get('validate_md5', True)), + strict=False) + ++ msg = '' + # validate source and dest are strings FIXME: use basic.py and module specs + if not isinstance(source, string_types): +- result['msg'] = "Invalid type supplied for source option, it must be a string" ++ msg = "Invalid type supplied for source option, it must be a string" + + if not isinstance(dest, string_types): +- result['msg'] = "Invalid type supplied for dest option, it must be a string" ++ msg = "Invalid type supplied for dest option, it must be a string" + + # validate_md5 is the deprecated way to specify validate_checksum + if 'validate_md5' in self._task.args and 'validate_checksum' in self._task.args: +@@ -74,11 +73,10 @@ class ActionModule(ActionBase): + display.deprecated('Use validate_checksum instead of validate_md5', version='2.8') + + if source is None or dest is None: +- result['msg'] = "src and dest are required" ++ msg = "src and dest are required" + +- if result.get('msg'): +- result['failed'] = True +- return result ++ if msg: ++ raise AnsibleActionFail(msg) + + source = self._connection._shell.join_path(source) + source = self._remote_expand_user(source) +@@ -106,12 +104,6 @@ class ActionModule(ActionBase): remote_data = base64.b64decode(slurpres['content']) if remote_data is not None: remote_checksum = checksum_s(remote_data) @@ -60,7 +107,7 @@ index fbaf992..471732c 100644 # calculate the destination name if os.path.sep not in self._connection._shell.join_path('a', ''): -@@ -120,13 +114,14 @@ class ActionModule(ActionBase): +@@ -120,13 +112,13 @@ class ActionModule(ActionBase): else: source_local = source @@ -68,8 +115,7 @@ index fbaf992..471732c 100644 + # ensure we only use file name, avoid relative paths + if not is_subpath(dest, original_dest): + # TODO: ? dest = os.path.expanduser(dest.replace(('../',''))) -+ raise AnsibleActionFail("Detected directory traversal, expected to be contained in '%s' but got '%s'" % (original_dest, dest)) -+ ++ raise AnsibleActionFail("Detected directory traversal, expected to be contained in '%s' but got '%s'" %(original_dest, dest)) if flat: if os.path.isdir(to_bytes(dest, errors='surrogate_or_strict')) and not dest.endswith(os.sep): - result['msg'] = "dest is an existing directory, use a trailing slash if you want to fetch src into that directory" @@ -80,7 +126,7 @@ index fbaf992..471732c 100644 if dest.endswith(os.sep): # if the path ends with "/", we'll use the source filename as the # destination filename -@@ -143,8 +138,6 @@ class ActionModule(ActionBase): +@@ -143,8 +135,6 @@ class ActionModule(ActionBase): target_name = self._play_context.remote_addr dest = "%s/%s/%s" % (self._loader.path_dwim(dest), target_name, source_local) @@ -89,16 +135,16 @@ index fbaf992..471732c 100644 if remote_checksum in ('0', '1', '2', '3', '4', '5'): result['changed'] = False result['file'] = source -@@ -172,6 +165,8 @@ class ActionModule(ActionBase): +@@ -172,6 +162,8 @@ class ActionModule(ActionBase): result['msg'] += ", not transferring, ignored" return result -+ dest = os.path.normpath(dest) ++ dest = os.path.normpath(dest) + # calculate checksum for the local file local_checksum = checksum(dest) -@@ -188,7 +183,7 @@ class ActionModule(ActionBase): +@@ -188,7 +180,7 @@ class ActionModule(ActionBase): f.write(remote_data) f.close() except (IOError, OSError) as e: @@ -107,9 +153,46 @@ index fbaf992..471732c 100644 new_checksum = secure_hash(dest) # For backwards compatibility. We'll return None on FIPS enabled systems try: +diff --git a/lib/ansible/utils/path.py b/lib/ansible/utils/path.py +index 717cef6..2ef1316 100644 +--- a/lib/ansible/utils/path.py ++++ b/lib/ansible/utils/path.py +@@ -97,3 +97,25 @@ def basedir(source): + dname = os.path.abspath(dname) + + return to_text(dname, errors='surrogate_or_strict') ++ ++def is_subpath(child, parent): ++ """ ++ Compares paths to check if one is contained in the other ++ :arg: child: Path to test ++ :arg parent; Path to test against ++ """ ++ test = False ++ ++ abs_child = unfrackpath(child, follow=False) ++ abs_parent = unfrackpath(parent, follow=False) ++ ++ c = abs_child.split(os.path.sep) ++ p = abs_parent.split(os.path.sep) ++ ++ try: ++ test = c[:len(p)] == p ++ except IndexError: ++ # child is shorter than parent so cannot be subpath ++ pass ++ ++ return test +diff --git a/test/integration/targets/fetch/aliases b/test/integration/targets/fetch/aliases +index 7af8b7f..197794b 100644 +--- a/test/integration/targets/fetch/aliases ++++ b/test/integration/targets/fetch/aliases +@@ -1 +1,2 @@ + posix/ci/group2 ++needs/target/setup_remote_tmp_dir diff --git a/test/integration/targets/fetch/injection/avoid_slurp_return.yml b/test/integration/targets/fetch/injection/avoid_slurp_return.yml new file mode 100644 -index 0000000..af62dcf +index 0000000..c44b85e --- /dev/null +++ b/test/integration/targets/fetch/injection/avoid_slurp_return.yml @@ -0,0 +1,26 @@ @@ -148,10 +231,10 @@ index 0000000..493021b +this is a test file diff --git a/test/integration/targets/fetch/injection/library/slurp.py b/test/integration/targets/fetch/injection/library/slurp.py new file mode 100644 -index 0000000..7b78ba1 +index 0000000..3916ae8 --- /dev/null +++ b/test/integration/targets/fetch/injection/library/slurp.py -@@ -0,0 +1,29 @@ +@@ -0,0 +1,28 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type @@ -171,7 +254,6 @@ index 0000000..7b78ba1 + +import json +import random -+ +bad_responses = ['../foo', '../../foo', '../../../foo', '/../../../foo', '/../foo', '//..//foo', '..//..//foo'] + + @@ -193,8 +275,8 @@ index 0000000..f2ff1df + roles: + - fetch_tests diff --git a/test/integration/targets/fetch/runme.sh b/test/integration/targets/fetch/runme.sh -new file mode 100755 -index 0000000..7e909dd +new file mode 100644 +index 0000000..3d5b75c --- /dev/null +++ b/test/integration/targets/fetch/runme.sh @@ -0,0 +1,12 @@ diff --git a/ansible.spec b/ansible.spec index 20344d3eb50a092a331e7d1735dc2e3929d4ddf0..aed62487d5e76da345e6162855491bc743b37110 100644 --- a/ansible.spec +++ b/ansible.spec @@ -3,7 +3,7 @@ Name: ansible Summary: SSH-based configuration management, deployment, and task execution system Version: 2.5.5 -Release: 4 +Release: 5 License: Python-2.0 and MIT and GPL+ Url: http://ansible.com Source0: https://releases.ansible.com/ansible/%{name}-%{version}.tar.gz @@ -12,7 +12,6 @@ Patch100: ansible-newer-jinja.patch Patch101: CVE-2019-14904.patch Patch102: CVE-2020-10684.patch Patch103: CVE-2020-10729.patch -Patch104: CVE-2020-1735.patch Patch106: CVE-2020-1737.patch Patch108: CVE-2020-1739.patch Patch109: CVE-2020-1740.patch @@ -20,6 +19,7 @@ Patch110: CVE-2020-1753.patch Patch111: CVE-2021-20191.patch Patch112: CVE-2019-10156-1.patch Patch113: CVE-2019-10156-2.patch +Patch114: CVE-2020-1735.patch BuildArch: noarch Provides: ansible-fireball = %{version}-%{release} Obsoletes: ansible-fireball < 1.2.4 @@ -77,7 +77,6 @@ This package installs extensive documentation for ansible %patch101 -p1 %patch102 -p1 %patch103 -p1 -%patch104 -p1 %patch106 -p1 %patch108 -p1 %patch109 -p1 @@ -85,7 +84,7 @@ This package installs extensive documentation for ansible %patch111 -p1 %patch112 -p1 %patch113 -p1 - +%patch114 -p1 %if 0%{?with_python3} rm -rf %{py3dir} cp -a . %{py3dir} @@ -146,6 +145,9 @@ cp -pr docs/docsite/rst . %endif %changelog +* Tue Nov 2 2021 caodongxia - 2.5.5-5 +- add defination of is_subpath and remove module_response_deepcopy + * Thu Oct 28 2021 liwu - 2.5.5-4 - The upstream community rolls back the patch