Import Debian changes 4.89-2+deb9u5
[hcoop/debian/exim4.git] / debian / patches / 84_Avoid-re-expansion-in-sort-CVE-2019-13917-OVE-201907.patch
CommitLineData
b032efff
AM
1From cf84d126bc1f04746eb7c8e8b3468f7e70add3ec Mon Sep 17 00:00:00 2001
2From: Jeremy Harris <jgh146exb@wizmail.org>
3Date: Fri, 5 Jul 2019 15:38:15 +0100
4Subject: [PATCH] Avoid re-expansion in ${sort } CVE-2019-13917
5 OVE-20190718-0006
6
7(cherry picked from commit 5c887f836e4d8e3f79da1c15565b56b40d9bd0dd)
8---
9 doc/ChangeLog | 6 ++
10 doc/doc-txt/cve-2019-13917 | 46 ++++++++
11 src/expand.c | 214 +++++++++++++++++++++++++------------
12 3 files changed, 199 insertions(+), 67 deletions(-)
13 create mode 100644 doc/doc-txt/cve-2019-13917
14
15--- a/src/expand.c
16+++ b/src/expand.c
17@@ -2115,6 +2115,55 @@ return ret;
18
19
20
21+/************************************************/
22+/* Return offset in ops table, or -1 if not found.
23+Repoint to just after the operator in the string.
24+
25+Argument:
26+ ss string representation of operator
27+ opname split-out operator name
28+*/
29+
30+static int
31+identify_operator(const uschar ** ss, uschar ** opname)
32+{
33+const uschar * s = *ss;
34+uschar name[256];
35+
36+/* Numeric comparisons are symbolic */
37+
38+if (*s == '=' || *s == '>' || *s == '<')
39+ {
40+ int p = 0;
41+ name[p++] = *s++;
42+ if (*s == '=')
43+ {
44+ name[p++] = '=';
45+ s++;
46+ }
47+ name[p] = 0;
48+ }
49+
50+/* All other conditions are named */
51+
52+else
53+ s = read_name(name, sizeof(name), s, US"_");
54+*ss = s;
55+
56+/* If we haven't read a name, it means some non-alpha character is first. */
57+
58+if (!name[0])
59+ {
60+ expand_string_message = string_sprintf("condition name expected, "
61+ "but found \"%.16s\"", s);
62+ return -1;
63+ }
64+if (opname)
65+ *opname = string_copy(name);
66+
67+return chop_match(name, cond_table, nelem(cond_table));
68+}
69+
70
71 /*************************************************
72 * Read and evaluate a condition *
73@@ -2145,6 +2194,7 @@ BOOL sub2_honour_dollar = TRUE;
74 int i, rc, cond_type, roffset;
75 int_eximarith_t num[2];
76 struct stat statbuf;
77+uschar * opname;
78 uschar name[256];
79 const uschar *sub[10];
80
81@@ -2157,37 +2207,7 @@ for (;;)
82 if (*s == '!') { testfor = !testfor; s++; } else break;
83 }
84
85-/* Numeric comparisons are symbolic */
86-
87-if (*s == '=' || *s == '>' || *s == '<')
88- {
89- int p = 0;
90- name[p++] = *s++;
91- if (*s == '=')
92- {
93- name[p++] = '=';
94- s++;
95- }
96- name[p] = 0;
97- }
98-
99-/* All other conditions are named */
100-
101-else s = read_name(name, 256, s, US"_");
102-
103-/* If we haven't read a name, it means some non-alpha character is first. */
104-
105-if (name[0] == 0)
106- {
107- expand_string_message = string_sprintf("condition name expected, "
108- "but found \"%.16s\"", s);
109- return NULL;
110- }
111-
112-/* Find which condition we are dealing with, and switch on it */
113-
114-cond_type = chop_match(name, cond_table, nelem(cond_table));
115-switch(cond_type)
116+switch(cond_type = identify_operator(&s, &opname))
117 {
118 /* def: tests for a non-empty variable, or for the existence of a header. If
119 yield == NULL we are in a skipping state, and don't care about the answer. */
120@@ -2506,7 +2526,7 @@ switch(cond_type)
121 {
122 if (i == 0) goto COND_FAILED_CURLY_START;
123 expand_string_message = string_sprintf("missing 2nd string in {} "
124- "after \"%s\"", name);
125+ "after \"%s\"", opname);
126 return NULL;
127 }
128 sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL,
129@@ -2518,7 +2538,7 @@ switch(cond_type)
130 conditions that compare numbers do not start with a letter. This just saves
131 checking for them individually. */
132
133- if (!isalpha(name[0]) && yield != NULL)
134+ if (!isalpha(opname[0]) && yield != NULL)
135 if (sub[i][0] == 0)
136 {
137 num[i] = 0;
138@@ -2832,7 +2852,7 @@ switch(cond_type)
139 uschar *save_iterate_item = iterate_item;
140 int (*compare)(const uschar *, const uschar *);
141
142- DEBUG(D_expand) debug_printf_indent("condition: %s\n", name);
143+ DEBUG(D_expand) debug_printf_indent("condition: %s\n", opname);
144
145 tempcond = FALSE;
146 compare = cond_type == ECOND_INLISTI
147@@ -2871,14 +2891,14 @@ switch(cond_type)
148 if (*s != '{') /* }-for-text-editors */
149 {
150 expand_string_message = string_sprintf("each subcondition "
151- "inside an \"%s{...}\" condition must be in its own {}", name);
152+ "inside an \"%s{...}\" condition must be in its own {}", opname);
153 return NULL;
154 }
155
156 if (!(s = eval_condition(s+1, resetok, subcondptr)))
157 {
158 expand_string_message = string_sprintf("%s inside \"%s{...}\" condition",
159- expand_string_message, name);
160+ expand_string_message, opname);
161 return NULL;
162 }
163 while (isspace(*s)) s++;
164@@ -2888,7 +2908,7 @@ switch(cond_type)
165 {
166 /* {-for-text-editors */
167 expand_string_message = string_sprintf("missing } at end of condition "
168- "inside \"%s\" group", name);
169+ "inside \"%s\" group", opname);
170 return NULL;
171 }
172
173@@ -2920,7 +2940,7 @@ switch(cond_type)
174 int sep = 0;
175 uschar *save_iterate_item = iterate_item;
176
177- DEBUG(D_expand) debug_printf_indent("condition: %s\n", name);
178+ DEBUG(D_expand) debug_printf_indent("condition: %s\n", opname);
179
180 while (isspace(*s)) s++;
181 if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
182@@ -2941,7 +2961,7 @@ switch(cond_type)
183 if (!(s = eval_condition(sub[1], resetok, NULL)))
184 {
185 expand_string_message = string_sprintf("%s inside \"%s\" condition",
186- expand_string_message, name);
187+ expand_string_message, opname);
188 return NULL;
189 }
190 while (isspace(*s)) s++;
191@@ -2951,7 +2971,7 @@ switch(cond_type)
192 {
193 /* {-for-text-editors */
194 expand_string_message = string_sprintf("missing } at end of condition "
195- "inside \"%s\"", name);
196+ "inside \"%s\"", opname);
197 return NULL;
198 }
199
200@@ -2963,11 +2983,11 @@ switch(cond_type)
201 if (!eval_condition(sub[1], resetok, &tempcond))
202 {
203 expand_string_message = string_sprintf("%s inside \"%s\" condition",
204- expand_string_message, name);
205+ expand_string_message, opname);
206 iterate_item = save_iterate_item;
207 return NULL;
208 }
209- DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name,
210+ DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", opname,
211 tempcond? "true":"false");
212
213 if (yield != NULL) *yield = (tempcond == testfor);
214@@ -3060,19 +3080,20 @@ switch(cond_type)
215 /* Unknown condition */
216
217 default:
218- expand_string_message = string_sprintf("unknown condition \"%s\"", name);
219- return NULL;
220+ if (!expand_string_message || !*expand_string_message)
221+ expand_string_message = string_sprintf("unknown condition \"%s\"", opname);
222+ return NULL;
223 } /* End switch on condition type */
224
225 /* Missing braces at start and end of data */
226
227 COND_FAILED_CURLY_START:
228-expand_string_message = string_sprintf("missing { after \"%s\"", name);
229+expand_string_message = string_sprintf("missing { after \"%s\"", opname);
230 return NULL;
231
232 COND_FAILED_CURLY_END:
233 expand_string_message = string_sprintf("missing } at end of \"%s\" condition",
234- name);
235+ opname);
236 return NULL;
237
238 /* A condition requires code that is not compiled */
239@@ -3082,7 +3103,7 @@ return NULL;
240 !defined(SUPPORT_CRYPTEQ) || !defined(CYRUS_SASLAUTHD_SOCKET)
241 COND_FAILED_NOT_COMPILED:
242 expand_string_message = string_sprintf("support for \"%s\" not compiled",
243- name);
244+ opname);
245 return NULL;
246 #endif
247 }
248@@ -3793,6 +3814,58 @@ return x;
249 }
250
251
252+/************************************************/
253+/* Comparison operation for sort expansion. We need to avoid
254+re-expanding the fields being compared, so need a custom routine.
255+
256+Arguments:
257+ cond_type Comparison operator code
258+ leftarg, rightarg Arguments for comparison
259+
260+Return true iff (leftarg compare rightarg)
261+*/
262+
263+static BOOL
264+sortsbefore(int cond_type, BOOL alpha_cond,
265+ const uschar * leftarg, const uschar * rightarg)
266+{
267+int_eximarith_t l_num, r_num;
268+
269+if (!alpha_cond)
270+ {
271+ l_num = expanded_string_integer(leftarg, FALSE);
272+ if (expand_string_message) return FALSE;
273+ r_num = expanded_string_integer(rightarg, FALSE);
274+ if (expand_string_message) return FALSE;
275+
276+ switch (cond_type)
277+ {
278+ case ECOND_NUM_G: return l_num > r_num;
279+ case ECOND_NUM_GE: return l_num >= r_num;
280+ case ECOND_NUM_L: return l_num < r_num;
281+ case ECOND_NUM_LE: return l_num <= r_num;
282+ default: break;
283+ }
284+ }
285+else
286+ switch (cond_type)
287+ {
288+ case ECOND_STR_LT: return Ustrcmp (leftarg, rightarg) < 0;
289+ case ECOND_STR_LTI: return strcmpic(leftarg, rightarg) < 0;
290+ case ECOND_STR_LE: return Ustrcmp (leftarg, rightarg) <= 0;
291+ case ECOND_STR_LEI: return strcmpic(leftarg, rightarg) <= 0;
292+ case ECOND_STR_GT: return Ustrcmp (leftarg, rightarg) > 0;
293+ case ECOND_STR_GTI: return strcmpic(leftarg, rightarg) > 0;
294+ case ECOND_STR_GE: return Ustrcmp (leftarg, rightarg) >= 0;
295+ case ECOND_STR_GEI: return strcmpic(leftarg, rightarg) >= 0;
296+ default: break;
297+ }
298+return FALSE; /* should not happen */
299+}
300+
301+
302+
303+
304
305 /*************************************************
306 * Expand string *
307@@ -5904,9 +5977,10 @@ while (*s != 0)
308
309 case EITEM_SORT:
310 {
311+ int cond_type;
312 int sep = 0;
313 const uschar *srclist, *cmp, *xtract;
314- uschar *srcitem;
315+ uschar * opname, * srcitem;
316 const uschar *dstlist = NULL, *dstkeylist = NULL;
317 uschar * tmp;
318 uschar *save_iterate_item = iterate_item;
319@@ -5941,6 +6015,25 @@ while (*s != 0)
320 goto EXPAND_FAILED_CURLY;
321 }
322
323+ if ((cond_type = identify_operator(&cmp, &opname)) == -1)
324+ {
325+ if (!expand_string_message)
326+ expand_string_message = string_sprintf("unknown condition \"%s\"", s);
327+ goto EXPAND_FAILED;
328+ }
329+ switch(cond_type)
330+ {
331+ case ECOND_NUM_L: case ECOND_NUM_LE:
332+ case ECOND_NUM_G: case ECOND_NUM_GE:
333+ case ECOND_STR_GE: case ECOND_STR_GEI: case ECOND_STR_GT: case ECOND_STR_GTI:
334+ case ECOND_STR_LE: case ECOND_STR_LEI: case ECOND_STR_LT: case ECOND_STR_LTI:
335+ break;
336+
337+ default:
338+ expand_string_message = US"comparator not handled for sort";
339+ goto EXPAND_FAILED;
340+ }
341+
342 while (isspace(*s)) s++;
343 if (*s++ != '{')
344 {
345@@ -5969,10 +6062,9 @@ while (*s != 0)
346
347 while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0)))
348 {
349- uschar * dstitem;
350+ uschar * srcfield, * dstitem;
351 uschar * newlist = NULL;
352 uschar * newkeylist = NULL;
353- uschar * srcfield;
354
355 DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, srcitem);
356
357@@ -5993,25 +6085,15 @@ while (*s != 0)
358 while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0)))
359 {
360 uschar * dstfield;
361- uschar * expr;
362- BOOL before;
363
364 /* field for comparison */
365 if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0)))
366 goto sort_mismatch;
367
368- /* build and run condition string */
369- expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield);
370-
371- DEBUG(D_expand) debug_printf_indent("%s: cond = \"%s\"\n", name, expr);
372- if (!eval_condition(expr, &resetok, &before))
373- {
374- expand_string_message = string_sprintf("comparison in sort: %s",
375- expr);
376- goto EXPAND_FAILED;
377- }
378+ /* String-comparator names start with a letter; numeric names do not */
379
380- if (before)
381+ if (sortsbefore(cond_type, isalpha(opname[0]),
382+ srcfield, dstfield))
383 {
384 /* New-item sorts before this dst-item. Append new-item,
385 then dst-item, then remainder of dst list. */