Lines 1-330
Link Here
|
1 |
From 1ccd26e24267ffa0c40b70c2c3282481fe4977c7 Mon Sep 17 00:00:00 2001 |
|
|
2 |
From: Jeremy Harris <jgh146exb@wizmail.org> |
3 |
Date: Thu, 16 Jan 2020 14:12:56 +0000 |
4 |
Subject: [PATCH 22/22] Taint: hybrid checking mode |
5 |
|
6 |
(cherry picked from commit 36eb5d3d77426d8cbf4243ea752f8d8cd1d5c682) |
7 |
--- |
8 |
doc/ChangeLog | 8 +++++ |
9 |
exim_monitor/em_version.c | 2 ++ |
10 |
src/functions.h | 58 +++++++++++++++++++++++++++++++- |
11 |
src/globals.c | 1 + |
12 |
src/globals.h | 1 + |
13 |
src/mytypes.h | 62 +++++------------------------------ |
14 |
src/store.c | 40 +++++++++++++++------- |
15 |
7 files changed, 107 insertions(+), 65 deletions(-) |
16 |
|
17 |
diff --git doc/ChangeLog doc/ChangeLog |
18 |
index f112fc9bf..508b8fa49 100644 |
19 |
--- doc/ChangeLog |
20 |
+++ doc/ChangeLog |
21 |
@@ -59,6 +59,14 @@ JH/21 Bug 2501: Fix init call in the heimdal authenticator. Previously it |
22 |
buffer was in use at the time. Change to a compile-time increase in the |
23 |
buffer size, when this authenticator is compiled into exim. |
24 |
|
25 |
+JH/22 Taint checking: move to a hybrid approach for checking. Previously, one |
26 |
+ of two ways was used, depending on a build-time flag. The fast method |
27 |
+ relied on assumptions about the OS and libc malloc, which were known to |
28 |
+ not hold for the BSD-derived platforms, and discovered to not hold for |
29 |
+ 32-bit Linux either. In fact the glibc documentation describes cases |
30 |
+ where these assumptions do not hold. The new implementation tests for |
31 |
+ the situation arising and actively switches over from fast to safe mode. |
32 |
+ |
33 |
|
34 |
Exim version 4.93 |
35 |
----------------- |
36 |
diff --git exim_monitor/em_version.c exim_monitor/em_version.c |
37 |
index 52c55a4a3..9b9c7d417 100644 |
38 |
--- exim_monitor/em_version.c |
39 |
+++ exim_monitor/em_version.c |
40 |
@@ -5,6 +5,8 @@ |
41 |
/* Copyright (c) University of Cambridge 1995 - 2018 */ |
42 |
/* See the file NOTICE for conditions of use and distribution. */ |
43 |
|
44 |
+#define EM_VERSION_C |
45 |
+ |
46 |
#include "mytypes.h" |
47 |
#include "store.h" |
48 |
#include "macros.h" |
49 |
diff --git src/functions.h src/functions.h |
50 |
index 87d1a04d8..0b5905562 100644 |
51 |
--- src/functions.h |
52 |
+++ src/functions.h |
53 |
@@ -187,6 +187,7 @@ extern void deliver_succeeded(address_item *); |
54 |
extern uschar *deliver_get_sender_address (uschar *id); |
55 |
extern void delivery_re_exec(int); |
56 |
|
57 |
+extern void die_tainted(const uschar *, const uschar *, int); |
58 |
extern BOOL directory_make(const uschar *, const uschar *, int, BOOL); |
59 |
#ifndef DISABLE_DKIM |
60 |
extern uschar *dkim_exim_query_dns_txt(const uschar *); |
61 |
@@ -602,6 +603,61 @@ extern BOOL write_chunk(transport_ctx *, uschar *, int); |
62 |
extern ssize_t write_to_fd_buf(int, const uschar *, size_t); |
63 |
|
64 |
|
65 |
+/******************************************************************************/ |
66 |
+/* Predicate: if an address is in a tainted pool. |
67 |
+By extension, a variable pointing to this address is tainted. |
68 |
+*/ |
69 |
+ |
70 |
+static inline BOOL |
71 |
+is_tainted(const void * p) |
72 |
+{ |
73 |
+#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) || defined(EM_VERSION_C) |
74 |
+return FALSE; |
75 |
+ |
76 |
+#else |
77 |
+extern BOOL is_tainted_fn(const void *); |
78 |
+extern void * tainted_base, * tainted_top; |
79 |
+ |
80 |
+return f.taint_check_slow |
81 |
+ ? is_tainted_fn(p) : p >= tainted_base && p < tainted_top; |
82 |
+#endif |
83 |
+} |
84 |
+ |
85 |
+/******************************************************************************/ |
86 |
+/* String functions */ |
87 |
+static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) |
88 |
+{ |
89 |
+#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
90 |
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); |
91 |
+#endif |
92 |
+return US strcat(CS dst, CCS src); |
93 |
+} |
94 |
+static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) |
95 |
+{ |
96 |
+#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
97 |
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); |
98 |
+#endif |
99 |
+return US strcpy(CS dst, CCS src); |
100 |
+} |
101 |
+static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) |
102 |
+{ |
103 |
+#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
104 |
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); |
105 |
+#endif |
106 |
+return US strncat(CS dst, CCS src, n); |
107 |
+} |
108 |
+static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) |
109 |
+{ |
110 |
+#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
111 |
+if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); |
112 |
+#endif |
113 |
+return US strncpy(CS dst, CCS src, n); |
114 |
+} |
115 |
+/*XXX will likely need unchecked copy also */ |
116 |
+ |
117 |
+ |
118 |
+/******************************************************************************/ |
119 |
+ |
120 |
#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) |
121 |
/* exim_chown - in some NFSv4 setups *seemes* to be an issue with |
122 |
chown(<exim-uid>, <exim-gid>). |
123 |
@@ -634,8 +690,8 @@ exim_chown(const uschar *name, uid_t owner, gid_t group) |
124 |
return chown(CCS name, owner, group) |
125 |
? exim_chown_failure(-1, name, owner, group) : 0; |
126 |
} |
127 |
- |
128 |
#endif /* !MACRO_PREDEF && !COMPILE_UTILITY */ |
129 |
+ |
130 |
/******************************************************************************/ |
131 |
/* String functions */ |
132 |
|
133 |
diff --git src/globals.c src/globals.c |
134 |
index 85a25a7f2..72449229e 100644 |
135 |
--- src/globals.c |
136 |
+++ src/globals.c |
137 |
@@ -311,6 +311,7 @@ struct global_flags f = |
138 |
.synchronous_delivery = FALSE, |
139 |
.system_filtering = FALSE, |
140 |
|
141 |
+ .taint_check_slow = FALSE, |
142 |
.tcp_fastopen_ok = FALSE, |
143 |
.tcp_in_fastopen = FALSE, |
144 |
.tcp_in_fastopen_data = FALSE, |
145 |
diff --git src/globals.h src/globals.h |
146 |
index ca342acc2..ac7bb8ef3 100644 |
147 |
--- src/globals.h |
148 |
+++ src/globals.h |
149 |
@@ -272,6 +272,7 @@ extern struct global_flags { |
150 |
BOOL synchronous_delivery :1; /* TRUE if -odi is set */ |
151 |
BOOL system_filtering :1; /* TRUE when running system filter */ |
152 |
|
153 |
+ BOOL taint_check_slow :1; /* malloc/mmap are not returning distinct ranges */ |
154 |
BOOL tcp_fastopen_ok :1; /* appears to be supported by kernel */ |
155 |
BOOL tcp_in_fastopen :1; /* conn usefully used fastopen */ |
156 |
BOOL tcp_in_fastopen_data :1; /* fastopen carried data */ |
157 |
diff --git src/mytypes.h src/mytypes.h |
158 |
index ceb9f1b55..e31ee8c1a 100644 |
159 |
--- src/mytypes.h |
160 |
+++ src/mytypes.h |
161 |
@@ -100,19 +100,15 @@ functions that are called quite often; for other calls to external libraries |
162 |
#define Uread(f,b,l) read(f,CS(b),l) |
163 |
#define Urename(s,t) rename(CCS(s),CCS(t)) |
164 |
#define Ustat(s,t) stat(CCS(s),t) |
165 |
-#define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) |
166 |
#define Ustrchr(s,n) US strchr(CCS(s),n) |
167 |
#define CUstrchr(s,n) CUS strchr(CCS(s),n) |
168 |
#define CUstrerror(n) CUS strerror(n) |
169 |
#define Ustrcmp(s,t) strcmp(CCS(s),CCS(t)) |
170 |
-#define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) |
171 |
#define Ustrcpy_nt(s,t) strcpy(CS s, CCS t) /* no taint check */ |
172 |
#define Ustrcspn(s,t) strcspn(CCS(s),CCS(t)) |
173 |
#define Ustrftime(s,m,f,t) strftime(CS(s),m,f,t) |
174 |
#define Ustrlen(s) (int)strlen(CCS(s)) |
175 |
-#define Ustrncat(s,t,n) __Ustrncat(s, CUS(t),n, __FUNCTION__, __LINE__) |
176 |
#define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n) |
177 |
-#define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t),n, __FUNCTION__, __LINE__) |
178 |
#define Ustrncpy_nt(s,t,n) strncpy(CS s, CCS t, n) /* no taint check */ |
179 |
#define Ustrpbrk(s,t) strpbrk(CCS(s),CCS(t)) |
180 |
#define Ustrrchr(s,n) US strrchr(CCS(s),n) |
181 |
@@ -125,57 +121,17 @@ functions that are called quite often; for other calls to external libraries |
182 |
#define Ustrtoul(s,t,b) strtoul(CCS(s),CSS(t),b) |
183 |
#define Uunlink(s) unlink(CCS(s)) |
184 |
|
185 |
-extern void die_tainted(const uschar *, const uschar *, int); |
186 |
- |
187 |
-/* Predicate: if an address is in a tainted pool. |
188 |
-By extension, a variable pointing to this address is tainted. |
189 |
-*/ |
190 |
- |
191 |
-static inline BOOL |
192 |
-is_tainted(const void * p) |
193 |
-{ |
194 |
-#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) |
195 |
-return FALSE; |
196 |
- |
197 |
-#elif defined(TAINT_CHECK_SLOW) |
198 |
-extern BOOL is_tainted_fn(const void *); |
199 |
-return is_tainted_fn(p); |
200 |
- |
201 |
+#ifdef EM_VERSION_C |
202 |
+# define Ustrcat(s,t) strcat(CS(s), CCS(t)) |
203 |
+# define Ustrcpy(s,t) strcpy(CS(s), CCS(t)) |
204 |
+# define Ustrncat(s,t,n) strncat(CS(s), CCS(t), n) |
205 |
+# define Ustrncpy(s,t,n) strncpy(CS(s), CCS(t), n) |
206 |
#else |
207 |
-extern void * tainted_base, * tainted_top; |
208 |
-return p >= tainted_base && p < tainted_top; |
209 |
-#endif |
210 |
-} |
211 |
- |
212 |
-static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) |
213 |
-{ |
214 |
-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
215 |
-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); |
216 |
-#endif |
217 |
-return US strcat(CS dst, CCS src); |
218 |
-} |
219 |
-static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) |
220 |
-{ |
221 |
-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
222 |
-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); |
223 |
-#endif |
224 |
-return US strcpy(CS dst, CCS src); |
225 |
-} |
226 |
-static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) |
227 |
-{ |
228 |
-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
229 |
-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); |
230 |
-#endif |
231 |
-return US strncat(CS dst, CCS src, n); |
232 |
-} |
233 |
-static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) |
234 |
-{ |
235 |
-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) |
236 |
-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); |
237 |
+# define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) |
238 |
+# define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) |
239 |
+# define Ustrncat(s,t,n) __Ustrncat(s, CUS(t), n, __FUNCTION__, __LINE__) |
240 |
+# define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t), n, __FUNCTION__, __LINE__) |
241 |
#endif |
242 |
-return US strncpy(CS dst, CCS src, n); |
243 |
-} |
244 |
-/*XXX will likely need unchecked copy also */ |
245 |
|
246 |
#endif |
247 |
/* End of mytypes.h */ |
248 |
diff --git src/store.c src/store.c |
249 |
index a06e1c19a..692a993e9 100644 |
250 |
--- src/store.c |
251 |
+++ src/store.c |
252 |
@@ -162,8 +162,14 @@ static void internal_tainted_free(storeblock *, const char *, int linenumber); |
253 |
|
254 |
/******************************************************************************/ |
255 |
|
256 |
-/* Slower version check, for use when platform intermixes malloc and mmap area |
257 |
-addresses. */ |
258 |
+/* Test if a pointer refers to tainted memory. |
259 |
+ |
260 |
+Slower version check, for use when platform intermixes malloc and mmap area |
261 |
+addresses. Test against the current-block of all tainted pools first, then all |
262 |
+blocks of all tainted pools. |
263 |
+ |
264 |
+Return: TRUE iff tainted |
265 |
+*/ |
266 |
|
267 |
BOOL |
268 |
is_tainted_fn(const void * p) |
269 |
@@ -171,23 +177,20 @@ is_tainted_fn(const void * p) |
270 |
storeblock * b; |
271 |
int pool; |
272 |
|
273 |
-for (pool = 0; pool < nelem(chainbase); pool++) |
274 |
+for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) |
275 |
if ((b = current_block[pool])) |
276 |
{ |
277 |
- char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; |
278 |
- if (CS p >= bc && CS p <= bc + b->length) goto hit; |
279 |
+ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; |
280 |
+ if (US p >= bc && US p <= bc + b->length) return TRUE; |
281 |
} |
282 |
|
283 |
-for (pool = 0; pool < nelem(chainbase); pool++) |
284 |
+for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) |
285 |
for (b = chainbase[pool]; b; b = b->next) |
286 |
{ |
287 |
- char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; |
288 |
- if (CS p >= bc && CS p <= bc + b->length) goto hit; |
289 |
+ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; |
290 |
+ if (US p >= bc && US p <= bc + b->length) return TRUE; |
291 |
} |
292 |
return FALSE; |
293 |
- |
294 |
-hit: |
295 |
-return pool >= POOL_TAINT_BASE; |
296 |
} |
297 |
|
298 |
|
299 |
@@ -198,6 +201,13 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", |
300 |
msg, func, line); |
301 |
} |
302 |
|
303 |
+static void |
304 |
+use_slow_taint_check(void) |
305 |
+{ |
306 |
+DEBUG(D_any) debug_printf("switching to slow-mode taint checking\n"); |
307 |
+f.taint_check_slow = TRUE; |
308 |
+} |
309 |
+ |
310 |
|
311 |
/************************************************* |
312 |
* Get a block from the current pool * |
313 |
@@ -820,6 +830,14 @@ if (!(yield = malloc((size_t)size))) |
314 |
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of memory: " |
315 |
"called from line %d in %s", size, linenumber, func); |
316 |
|
317 |
+/* If malloc ever returns apparently tainted memory, which glibc |
318 |
+malloc will as it uses mmap for larger requests, we must switch to |
319 |
+the slower checking for tainting (checking an address against all |
320 |
+the tainted pool block spans, rather than just the mmap span) */ |
321 |
+ |
322 |
+if (!f.taint_check_slow && is_tainted(yield)) |
323 |
+ use_slow_taint_check(); |
324 |
+ |
325 |
return store_alloc_tail(yield, size, func, linenumber, US"Malloc"); |
326 |
} |
327 |
|
328 |
-- |
329 |
2.24.1 |
330 |
|