Commit | Line | Data |
---|---|---|
0c0c20aa AM |
1 | From 99ae249e9857e80ff4d65b2388bc68c624dcb739 Mon Sep 17 00:00:00 2001 |
2 | From: Qualys Security Advisory <qsa@qualys.com> | |
3 | Date: Tue, 23 Feb 2021 08:33:03 -0800 | |
4 | Subject: [PATCH 23/29] CVE-2020-28007: Link attack in Exim's log directory | |
5 | ||
6 | We patch this vulnerability by opening (instead of just creating) the | |
7 | log file in an unprivileged (exim) child process, and by passing this | |
8 | file descriptor back to the privileged (root) parent process. The two | |
9 | functions log_send_fd() and log_recv_fd() are inspired by OpenSSH's | |
10 | functions mm_send_fd() and mm_receive_fd(); thanks! | |
11 | ||
12 | This patch also fixes: | |
13 | ||
14 | - a NULL-pointer dereference in usr1_handler() (this signal handler is | |
15 | installed before process_log_path is initialized); | |
16 | ||
17 | - a file-descriptor leak in dmarc_write_history_file() (two return paths | |
18 | did not close history_file_fd). | |
19 | ||
20 | Note: the use of log_open_as_exim() in dmarc_write_history_file() should | |
21 | be fine because the documentation explicitly states "Make sure the | |
22 | directory of this file is writable by the user exim runs as." | |
23 | ||
24 | (cherry picked from commit 2502cc41d1d92c1413eca6a4ba035c21162662bd) | |
25 | (cherry picked from commit 93e9a18fbf09deb59bd133986f4c89aeb2d2d86a) | |
26 | --- | |
27 | src/dmarc.c | 179 ++++++++++++++++++------------------ | |
28 | src/exim.c | 14 +-- | |
29 | src/functions.h | 3 +- | |
30 | src/log.c | 214 ++++++++++++++++++++++++++++---------------- | |
31 | test/stderr/0397 | 6 +- | |
32 | 5 files changed, 234 insertions(+), 182 deletions(-) | |
33 | ||
34 | diff --git a/src/dmarc.c b/src/dmarc.c | |
35 | index f29f7eba6..c5e01c7ee 100644 | |
36 | --- a/src/dmarc.c | |
37 | +++ b/src/dmarc.c | |
38 | @@ -204,6 +204,97 @@ if ( dmarc_policy == DMARC_POLICY_REJECT && action == DMARC_RESULT_REJECT | |
39 | } | |
40 | } | |
41 | ||
42 | + | |
43 | +static int | |
44 | +dmarc_write_history_file() | |
45 | +{ | |
46 | +int tmp_ans; | |
47 | +u_char **rua; /* aggregate report addressees */ | |
48 | +uschar *history_buffer = NULL; | |
49 | + | |
50 | +if (!dmarc_history_file) | |
51 | + { | |
52 | + DEBUG(D_receive) debug_printf("DMARC history file not set\n"); | |
53 | + return DMARC_HIST_DISABLED; | |
54 | + } | |
55 | + | |
56 | +/* Generate the contents of the history file */ | |
57 | +history_buffer = string_sprintf( | |
58 | + "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", | |
59 | + message_id, primary_hostname, time(NULL), sender_host_address, | |
60 | + header_from_sender, expand_string(US"$sender_address_domain")); | |
61 | + | |
62 | +if (spf_response) | |
63 | + history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result); | |
64 | + /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */ | |
65 | + | |
66 | +history_buffer = string_sprintf( | |
67 | + "%s%spdomain %s\npolicy %d\n", | |
68 | + history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy); | |
69 | + | |
70 | +if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) | |
71 | + for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) | |
72 | + history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]); | |
73 | +else | |
74 | + history_buffer = string_sprintf("%srua -\n", history_buffer); | |
75 | + | |
76 | +opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); | |
77 | +history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans); | |
78 | + | |
79 | +opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); | |
80 | +history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans); | |
81 | + | |
82 | +opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); | |
83 | +history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans); | |
84 | + | |
85 | +opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); | |
86 | +history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans); | |
87 | + | |
88 | +opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); | |
89 | +history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans); | |
90 | + | |
91 | +history_buffer = string_sprintf( | |
92 | + "%salign_dkim %d\nalign_spf %d\naction %d\n", | |
93 | + history_buffer, da, sa, action); | |
94 | + | |
95 | +/* Write the contents to the history file */ | |
96 | +DEBUG(D_receive) | |
97 | + debug_printf("DMARC logging history data for opendmarc reporting%s\n", | |
98 | + (host_checking || f.running_in_test_harness) ? " (not really)" : ""); | |
99 | +if (host_checking || f.running_in_test_harness) | |
100 | + { | |
101 | + DEBUG(D_receive) | |
102 | + debug_printf("DMARC history data for debugging:\n%s", history_buffer); | |
103 | + } | |
104 | +else | |
105 | + { | |
106 | + ssize_t written_len; | |
107 | + const int history_file_fd = log_open_as_exim(dmarc_history_file); | |
108 | + | |
109 | + if (history_file_fd < 0) | |
110 | + { | |
111 | + log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", | |
112 | + dmarc_history_file); | |
113 | + return DMARC_HIST_FILE_ERR; | |
114 | + } | |
115 | + | |
116 | + written_len = write_to_fd_buf(history_file_fd, | |
117 | + history_buffer, | |
118 | + Ustrlen(history_buffer)); | |
119 | + | |
120 | + (void)close(history_file_fd); | |
121 | + | |
122 | + if (written_len <= 0) | |
123 | + { | |
124 | + log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s", | |
125 | + dmarc_history_file); | |
126 | + return DMARC_HIST_WRITE_ERR; | |
127 | + } | |
128 | + } | |
129 | +return DMARC_HIST_OK; | |
130 | +} | |
131 | + | |
132 | + | |
133 | /* dmarc_process adds the envelope sender address to the existing | |
134 | context (if any), retrieves the result, sets up expansion | |
135 | strings and evaluates the condition outcome. */ | |
136 | @@ -486,94 +577,6 @@ if (!f.dmarc_disable_verify) | |
137 | return OK; | |
138 | } | |
139 | ||
140 | -static int | |
141 | -dmarc_write_history_file() | |
142 | -{ | |
143 | -int history_file_fd; | |
144 | -ssize_t written_len; | |
145 | -int tmp_ans; | |
146 | -u_char **rua; /* aggregate report addressees */ | |
147 | -uschar *history_buffer = NULL; | |
148 | - | |
149 | -if (!dmarc_history_file) | |
150 | - { | |
151 | - DEBUG(D_receive) debug_printf("DMARC history file not set\n"); | |
152 | - return DMARC_HIST_DISABLED; | |
153 | - } | |
154 | -history_file_fd = log_create(dmarc_history_file); | |
155 | - | |
156 | -if (history_file_fd < 0) | |
157 | - { | |
158 | - log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", | |
159 | - dmarc_history_file); | |
160 | - return DMARC_HIST_FILE_ERR; | |
161 | - } | |
162 | - | |
163 | -/* Generate the contents of the history file */ | |
164 | -history_buffer = string_sprintf( | |
165 | - "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", | |
166 | - message_id, primary_hostname, time(NULL), sender_host_address, | |
167 | - header_from_sender, expand_string(US"$sender_address_domain")); | |
168 | - | |
169 | -if (spf_response) | |
170 | - history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result); | |
171 | - /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */ | |
172 | - | |
173 | -history_buffer = string_sprintf( | |
174 | - "%s%spdomain %s\npolicy %d\n", | |
175 | - history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy); | |
176 | - | |
177 | -if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) | |
178 | - for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) | |
179 | - history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]); | |
180 | -else | |
181 | - history_buffer = string_sprintf("%srua -\n", history_buffer); | |
182 | - | |
183 | -opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); | |
184 | -history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans); | |
185 | - | |
186 | -opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); | |
187 | -history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans); | |
188 | - | |
189 | -opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); | |
190 | -history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans); | |
191 | - | |
192 | -opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); | |
193 | -history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans); | |
194 | - | |
195 | -opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); | |
196 | -history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans); | |
197 | - | |
198 | -history_buffer = string_sprintf( | |
199 | - "%salign_dkim %d\nalign_spf %d\naction %d\n", | |
200 | - history_buffer, da, sa, action); | |
201 | - | |
202 | -/* Write the contents to the history file */ | |
203 | -DEBUG(D_receive) | |
204 | - debug_printf("DMARC logging history data for opendmarc reporting%s\n", | |
205 | - (host_checking || f.running_in_test_harness) ? " (not really)" : ""); | |
206 | -if (host_checking || f.running_in_test_harness) | |
207 | - { | |
208 | - DEBUG(D_receive) | |
209 | - debug_printf("DMARC history data for debugging:\n%s", history_buffer); | |
210 | - } | |
211 | -else | |
212 | - { | |
213 | - written_len = write_to_fd_buf(history_file_fd, | |
214 | - history_buffer, | |
215 | - Ustrlen(history_buffer)); | |
216 | - if (written_len == 0) | |
217 | - { | |
218 | - log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s", | |
219 | - dmarc_history_file); | |
220 | - return DMARC_HIST_WRITE_ERR; | |
221 | - } | |
222 | - (void)close(history_file_fd); | |
223 | - } | |
224 | -return DMARC_HIST_OK; | |
225 | -} | |
226 | - | |
227 | - | |
228 | uschar * | |
229 | dmarc_exim_expand_query(int what) | |
230 | { | |
231 | diff --git a/src/exim.c b/src/exim.c | |
232 | index a7dc48c4e..f0a168983 100644 | |
233 | --- a/src/exim.c | |
234 | +++ b/src/exim.c | |
235 | @@ -227,18 +227,8 @@ int fd; | |
236 | ||
237 | os_restarting_signal(sig, usr1_handler); | |
238 | ||
239 | -if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0) | |
240 | - { | |
241 | - /* If we are already running as the Exim user, try to create it in the | |
242 | - current process (assuming spool_directory exists). Otherwise, if we are | |
243 | - root, do the creation in an exim:exim subprocess. */ | |
244 | - | |
245 | - int euid = geteuid(); | |
246 | - if (euid == exim_uid) | |
247 | - fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
248 | - else if (euid == root_uid) | |
249 | - fd = log_create_as_exim(process_log_path); | |
250 | - } | |
251 | +if (!process_log_path) return; | |
252 | +fd = log_open_as_exim(process_log_path); | |
253 | ||
254 | /* If we are neither exim nor root, or if we failed to create the log file, | |
255 | give up. There is not much useful we can do with errors, since we don't want | |
256 | diff --git a/src/functions.h b/src/functions.h | |
257 | index cab7a7363..366cb2f26 100644 | |
258 | --- a/src/functions.h | |
259 | +++ b/src/functions.h | |
260 | @@ -281,8 +281,7 @@ extern int ip_streamsocket(const uschar *, uschar **, int); | |
261 | extern int ipv6_nmtoa(int *, uschar *); | |
262 | ||
263 | extern uschar *local_part_quote(uschar *); | |
264 | -extern int log_create(uschar *); | |
265 | -extern int log_create_as_exim(uschar *); | |
266 | +extern int log_open_as_exim(uschar *); | |
267 | extern void log_close_all(void); | |
268 | ||
269 | extern macro_item * macro_create(const uschar *, const uschar *, BOOL); | |
270 | diff --git a/src/log.c b/src/log.c | |
271 | index c8313890e..15c88c13e 100644 | |
272 | --- a/src/log.c | |
273 | +++ b/src/log.c | |
274 | @@ -264,14 +264,19 @@ overwrite it temporarily if it is necessary to create the directory. | |
275 | Returns: a file descriptor, or < 0 on failure (errno set) | |
276 | */ | |
277 | ||
278 | -int | |
279 | -log_create(uschar *name) | |
280 | +static int | |
281 | +log_open_already_exim(uschar * const name) | |
282 | { | |
283 | -int fd = Uopen(name, | |
284 | -#ifdef O_CLOEXEC | |
285 | - O_CLOEXEC | | |
286 | -#endif | |
287 | - O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
288 | +int fd = -1; | |
289 | +const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK; | |
290 | + | |
291 | +if (geteuid() != exim_uid) | |
292 | + { | |
293 | + errno = EACCES; | |
294 | + return -1; | |
295 | + } | |
296 | + | |
297 | +fd = Uopen(name, flags, LOG_MODE); | |
298 | ||
299 | /* If creation failed, attempt to build a log directory in case that is the | |
300 | problem. */ | |
301 | @@ -285,11 +290,7 @@ if (fd < 0 && errno == ENOENT) | |
302 | DEBUG(D_any) debug_printf("%s log directory %s\n", | |
303 | created ? "created" : "failed to create", name); | |
304 | *lastslash = '/'; | |
305 | - if (created) fd = Uopen(name, | |
306 | -#ifdef O_CLOEXEC | |
307 | - O_CLOEXEC | | |
308 | -#endif | |
309 | - O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); | |
310 | + if (created) fd = Uopen(name, flags, LOG_MODE); | |
311 | } | |
312 | ||
313 | return fd; | |
314 | @@ -297,6 +298,81 @@ return fd; | |
315 | ||
316 | ||
317 | ||
318 | +/* Inspired by OpenSSH's mm_send_fd(). Thanks! */ | |
319 | + | |
320 | +static int | |
321 | +log_send_fd(const int sock, const int fd) | |
322 | +{ | |
323 | +struct msghdr msg; | |
324 | +union { | |
325 | + struct cmsghdr hdr; | |
326 | + char buf[CMSG_SPACE(sizeof(int))]; | |
327 | +} cmsgbuf; | |
328 | +struct cmsghdr *cmsg; | |
329 | +struct iovec vec; | |
330 | +char ch = 'A'; | |
331 | +ssize_t n; | |
332 | + | |
333 | +memset(&msg, 0, sizeof(msg)); | |
334 | +memset(&cmsgbuf, 0, sizeof(cmsgbuf)); | |
335 | +msg.msg_control = &cmsgbuf.buf; | |
336 | +msg.msg_controllen = sizeof(cmsgbuf.buf); | |
337 | + | |
338 | +cmsg = CMSG_FIRSTHDR(&msg); | |
339 | +cmsg->cmsg_len = CMSG_LEN(sizeof(int)); | |
340 | +cmsg->cmsg_level = SOL_SOCKET; | |
341 | +cmsg->cmsg_type = SCM_RIGHTS; | |
342 | +*(int *)CMSG_DATA(cmsg) = fd; | |
343 | + | |
344 | +vec.iov_base = &ch; | |
345 | +vec.iov_len = 1; | |
346 | +msg.msg_iov = &vec; | |
347 | +msg.msg_iovlen = 1; | |
348 | + | |
349 | +while ((n = sendmsg(sock, &msg, 0)) == -1 && errno == EINTR); | |
350 | +if (n != 1) return -1; | |
351 | +return 0; | |
352 | +} | |
353 | + | |
354 | +/* Inspired by OpenSSH's mm_receive_fd(). Thanks! */ | |
355 | + | |
356 | +static int | |
357 | +log_recv_fd(const int sock) | |
358 | +{ | |
359 | +struct msghdr msg; | |
360 | +union { | |
361 | + struct cmsghdr hdr; | |
362 | + char buf[CMSG_SPACE(sizeof(int))]; | |
363 | +} cmsgbuf; | |
364 | +struct cmsghdr *cmsg; | |
365 | +struct iovec vec; | |
366 | +ssize_t n; | |
367 | +char ch = '\0'; | |
368 | +int fd = -1; | |
369 | + | |
370 | +memset(&msg, 0, sizeof(msg)); | |
371 | +vec.iov_base = &ch; | |
372 | +vec.iov_len = 1; | |
373 | +msg.msg_iov = &vec; | |
374 | +msg.msg_iovlen = 1; | |
375 | + | |
376 | +memset(&cmsgbuf, 0, sizeof(cmsgbuf)); | |
377 | +msg.msg_control = &cmsgbuf.buf; | |
378 | +msg.msg_controllen = sizeof(cmsgbuf.buf); | |
379 | + | |
380 | +while ((n = recvmsg(sock, &msg, 0)) == -1 && errno == EINTR); | |
381 | +if (n != 1 || ch != 'A') return -1; | |
382 | + | |
383 | +cmsg = CMSG_FIRSTHDR(&msg); | |
384 | +if (cmsg == NULL) return -1; | |
385 | +if (cmsg->cmsg_type != SCM_RIGHTS) return -1; | |
386 | +fd = *(const int *)CMSG_DATA(cmsg); | |
387 | +if (fd < 0) return -1; | |
388 | +return fd; | |
389 | +} | |
390 | + | |
391 | + | |
392 | + | |
393 | /************************************************* | |
394 | * Create a log file as the exim user * | |
395 | *************************************************/ | |
396 | @@ -312,41 +388,60 @@ Returns: a file descriptor, or < 0 on failure (errno set) | |
397 | */ | |
398 | ||
399 | int | |
400 | -log_create_as_exim(uschar *name) | |
401 | +log_open_as_exim(uschar * const name) | |
402 | { | |
403 | -pid_t pid = fork(); | |
404 | -int status = 1; | |
405 | int fd = -1; | |
406 | +const uid_t euid = geteuid(); | |
407 | ||
408 | -/* In the subprocess, change uid/gid and do the creation. Return 0 from the | |
409 | -subprocess on success. If we don't check for setuid failures, then the file | |
410 | -can be created as root, so vulnerabilities which cause setuid to fail mean | |
411 | -that the Exim user can use symlinks to cause a file to be opened/created as | |
412 | -root. We always open for append, so can't nuke existing content but it would | |
413 | -still be Rather Bad. */ | |
414 | - | |
415 | -if (pid == 0) | |
416 | +if (euid == exim_uid) | |
417 | { | |
418 | - if (setgid(exim_gid) < 0) | |
419 | - die(US"exim: setgid for log-file creation failed, aborting", | |
420 | - US"Unexpected log failure, please try later"); | |
421 | - if (setuid(exim_uid) < 0) | |
422 | - die(US"exim: setuid for log-file creation failed, aborting", | |
423 | - US"Unexpected log failure, please try later"); | |
424 | - _exit((log_create(name) < 0)? 1 : 0); | |
425 | + fd = log_open_already_exim(name); | |
426 | } | |
427 | +else if (euid == root_uid) | |
428 | + { | |
429 | + int sock[2]; | |
430 | + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == 0) | |
431 | + { | |
432 | + const pid_t pid = fork(); | |
433 | + if (pid == 0) | |
434 | + { | |
435 | + (void)close(sock[0]); | |
436 | + if (setgroups(1, &exim_gid) != 0) _exit(EXIT_FAILURE); | |
437 | + if (setgid(exim_gid) != 0) _exit(EXIT_FAILURE); | |
438 | + if (setuid(exim_uid) != 0) _exit(EXIT_FAILURE); | |
439 | + | |
440 | + if (getuid() != exim_uid || geteuid() != exim_uid) _exit(EXIT_FAILURE); | |
441 | + if (getgid() != exim_gid || getegid() != exim_gid) _exit(EXIT_FAILURE); | |
442 | + | |
443 | + fd = log_open_already_exim(name); | |
444 | + if (fd < 0) _exit(EXIT_FAILURE); | |
445 | + if (log_send_fd(sock[1], fd) != 0) _exit(EXIT_FAILURE); | |
446 | + (void)close(sock[1]); | |
447 | + _exit(EXIT_SUCCESS); | |
448 | + } | |
449 | ||
450 | -/* If we created a subprocess, wait for it. If it succeeded, try the open. */ | |
451 | - | |
452 | -while (pid > 0 && waitpid(pid, &status, 0) != pid); | |
453 | -if (status == 0) fd = Uopen(name, | |
454 | -#ifdef O_CLOEXEC | |
455 | - O_CLOEXEC | | |
456 | -#endif | |
457 | - O_APPEND|O_WRONLY, LOG_MODE); | |
458 | + (void)close(sock[1]); | |
459 | + if (pid > 0) | |
460 | + { | |
461 | + fd = log_recv_fd(sock[0]); | |
462 | + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR); | |
463 | + } | |
464 | + (void)close(sock[0]); | |
465 | + } | |
466 | + } | |
467 | ||
468 | -/* If we failed to create a subprocess, we are in a bad way. We return | |
469 | -with fd still < 0, and errno set, letting the caller handle the error. */ | |
470 | +if (fd >= 0) | |
471 | + { | |
472 | + int flags; | |
473 | + flags = fcntl(fd, F_GETFD); | |
474 | + if (flags != -1) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC); | |
475 | + flags = fcntl(fd, F_GETFL); | |
476 | + if (flags != -1) (void)fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); | |
477 | + } | |
478 | +else | |
479 | + { | |
480 | + errno = EACCES; | |
481 | + } | |
482 | ||
483 | return fd; | |
484 | } | |
485 | @@ -459,52 +554,17 @@ if (!ok) | |
486 | die(US"exim: log file path too long: aborting", | |
487 | US"Logging failure; please try later"); | |
488 | ||
489 | -/* We now have the file name. Try to open an existing file. After a successful | |
490 | -open, arrange for automatic closure on exec(), and then return. */ | |
491 | +/* We now have the file name. After a successful open, return. */ | |
492 | ||
493 | -*fd = Uopen(buffer, | |
494 | -#ifdef O_CLOEXEC | |
495 | - O_CLOEXEC | | |
496 | -#endif | |
497 | - O_APPEND|O_WRONLY, LOG_MODE); | |
498 | +*fd = log_open_as_exim(buffer); | |
499 | ||
500 | if (*fd >= 0) | |
501 | { | |
502 | -#ifndef O_CLOEXEC | |
503 | - (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC); | |
504 | -#endif | |
505 | return; | |
506 | } | |
507 | ||
508 | -/* Open was not successful: try creating the file. If this is a root process, | |
509 | -we must do the creating in a subprocess set to exim:exim in order to ensure | |
510 | -that the file is created with the right ownership. Otherwise, there can be a | |
511 | -race if another Exim process is trying to write to the log at the same time. | |
512 | -The use of SIGUSR1 by the exiwhat utility can provoke a lot of simultaneous | |
513 | -writing. */ | |
514 | - | |
515 | euid = geteuid(); | |
516 | ||
517 | -/* If we are already running as the Exim user (even if that user is root), | |
518 | -we can go ahead and create in the current process. */ | |
519 | - | |
520 | -if (euid == exim_uid) *fd = log_create(buffer); | |
521 | - | |
522 | -/* Otherwise, if we are root, do the creation in an exim:exim subprocess. If we | |
523 | -are neither exim nor root, creation is not attempted. */ | |
524 | - | |
525 | -else if (euid == root_uid) *fd = log_create_as_exim(buffer); | |
526 | - | |
527 | -/* If we now have an open file, set the close-on-exec flag and return. */ | |
528 | - | |
529 | -if (*fd >= 0) | |
530 | - { | |
531 | -#ifndef O_CLOEXEC | |
532 | - (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC); | |
533 | -#endif | |
534 | - return; | |
535 | - } | |
536 | - | |
537 | /* Creation failed. There are some circumstances in which we get here when | |
538 | the effective uid is not root or exim, which is the problem. (For example, a | |
539 | non-setuid binary with log_arguments set, called in certain ways.) Rather than | |
540 | -- | |
541 | 2.30.2 | |
542 |