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
1 From cf84d126bc1f04746eb7c8e8b3468f7e70add3ec Mon Sep 17 00:00:00 2001
2 From: Jeremy Harris <jgh146exb@wizmail.org>
3 Date: Fri, 5 Jul 2019 15:38:15 +0100
4 Subject: [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. */