Additional fixes for security related to CVE-2016-9587

(cherry picked from commit d316068831)
pull/21095/head
James Cammarata 8 years ago
parent 51559b0a51
commit 35480106e5

@ -87,7 +87,7 @@ class Conditional:
if conditional is None or conditional == '': if conditional is None or conditional == '':
return True return True
if conditional in all_vars and '-' not in text_type(all_vars[conditional]): if conditional in all_vars and re.match("^[_A-Za-z][_a-zA-Z0-9]*$", conditional):
conditional = all_vars[conditional] conditional = all_vars[conditional]
# make sure the templar is using the variables specified with this method # make sure the templar is using the variables specified with this method
@ -99,12 +99,12 @@ class Conditional:
return conditional return conditional
# a Jinja2 evaluation that results in something Python can eval! # a Jinja2 evaluation that results in something Python can eval!
if hasattr(conditional, '__UNSAFE__') and LOOKUP_REGEX.match(conditional): disable_lookups = False
raise AnsibleError("The conditional '%s' contains variables which came from an unsafe " \ if hasattr(conditional, '__UNSAFE__'):
"source and also contains a lookup() call, failing conditional check" % conditional) disable_lookups = True
presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional
val = templar.template(presented).strip() val = templar.template(presented, disable_lookups=disable_lookups).strip()
if val == "True": if val == "True":
return True return True
elif val == "False": elif val == "False":

@ -30,10 +30,8 @@ from ansible.compat.six import string_types, text_type, binary_type
from jinja2 import Environment from jinja2 import Environment
from jinja2.loaders import FileSystemLoader from jinja2.loaders import FileSystemLoader
from jinja2.exceptions import TemplateSyntaxError, UndefinedError from jinja2.exceptions import TemplateSyntaxError, UndefinedError
from jinja2.nodes import EvalContext
from jinja2.utils import concat as j2_concat from jinja2.utils import concat as j2_concat
from jinja2.runtime import Context, StrictUndefined from jinja2.runtime import Context, StrictUndefined
from ansible import constants as C from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleUndefinedVariable from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleUndefinedVariable
from ansible.plugins import filter_loader, lookup_loader, test_loader from ansible.plugins import filter_loader, lookup_loader, test_loader
@ -126,13 +124,6 @@ def _count_newlines_from_end(in_str):
# Uncommon cases: zero length string and string containing only newlines # Uncommon cases: zero length string and string containing only newlines
return i return i
class AnsibleEvalContext(EvalContext):
'''
A custom jinja2 EvalContext, which is currently unused and saved
here for possible future use.
'''
pass
class AnsibleContext(Context): class AnsibleContext(Context):
''' '''
A custom context, which intercepts resolve() calls and sets a flag A custom context, which intercepts resolve() calls and sets a flag
@ -142,7 +133,6 @@ class AnsibleContext(Context):
''' '''
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AnsibleContext, self).__init__(*args, **kwargs) super(AnsibleContext, self).__init__(*args, **kwargs)
self.eval_ctx = AnsibleEvalContext(self.environment, self.name)
self.unsafe = False self.unsafe = False
def _is_unsafe(self, val): def _is_unsafe(self, val):
@ -334,7 +324,7 @@ class Templar:
self._available_variables = variables self._available_variables = variables
self._cached_result = {} self._cached_result = {}
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars = [''], cache = True, bare_deprecated=True): def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars = [''], cache = True, bare_deprecated=True, disable_lookups=False):
''' '''
Templates (possibly recursively) any given data as input. If convert_bare is Templates (possibly recursively) any given data as input. If convert_bare is
set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}')
@ -391,6 +381,7 @@ class Templar:
escape_backslashes=escape_backslashes, escape_backslashes=escape_backslashes,
fail_on_undefined=fail_on_undefined, fail_on_undefined=fail_on_undefined,
overrides=overrides, overrides=overrides,
disable_lookups=disable_lookups,
) )
unsafe = hasattr(result, '__UNSAFE__') unsafe = hasattr(result, '__UNSAFE__')
if convert_data and not self._no_type_regex.match(variable) and not unsafe: if convert_data and not self._no_type_regex.match(variable) and not unsafe:
@ -401,6 +392,7 @@ class Templar:
if eval_results[1] is None: if eval_results[1] is None:
result = eval_results[0] result = eval_results[0]
if unsafe: if unsafe:
from ansible.vars.unsafe_proxy import wrap_var
result = wrap_var(result) result = wrap_var(result)
else: else:
# FIXME: if the safe_eval raised an error, should we do something with it? # FIXME: if the safe_eval raised an error, should we do something with it?
@ -469,6 +461,9 @@ class Templar:
''' '''
return thing if thing is not None else '' return thing if thing is not None else ''
def _fail_lookup(self, name, *args, **kwargs):
raise AnsibleError("The lookup `%s` was found, however lookups were disabled from templating" % name)
def _lookup(self, name, *args, **kwargs): def _lookup(self, name, *args, **kwargs):
instance = self._lookup_loader.get(name.lower(), loader=self._loader, templar=self) instance = self._lookup_loader.get(name.lower(), loader=self._loader, templar=self)
@ -488,6 +483,7 @@ class Templar:
ran = None ran = None
if ran: if ran:
from ansible.vars.unsafe_proxy import UnsafeProxy, wrap_var
if wantlist: if wantlist:
ran = wrap_var(ran) ran = wrap_var(ran)
else: else:
@ -503,7 +499,7 @@ class Templar:
else: else:
raise AnsibleError("lookup plugin (%s) not found" % name) raise AnsibleError("lookup plugin (%s) not found" % name)
def _do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None): def _do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, disable_lookups=False):
# For preserving the number of input newlines in the output (used # For preserving the number of input newlines in the output (used
# later in this method) # later in this method)
@ -548,7 +544,11 @@ class Templar:
else: else:
return data return data
if disable_lookups:
t.globals['lookup'] = self._fail_lookup
else:
t.globals['lookup'] = self._lookup t.globals['lookup'] = self._lookup
t.globals['finalize'] = self._finalize t.globals['finalize'] = self._finalize
jvars = AnsibleJ2Vars(self, t.globals) jvars = AnsibleJ2Vars(self, t.globals)
@ -559,6 +559,7 @@ class Templar:
try: try:
res = j2_concat(rf) res = j2_concat(rf)
if new_context.unsafe: if new_context.unsafe:
from ansible.vars.unsafe_proxy import wrap_var
res = wrap_var(res) res = wrap_var(res)
except TypeError as te: except TypeError as te:
if 'StrictUndefined' in to_str(te): if 'StrictUndefined' in to_str(te):

Loading…
Cancel
Save