0
|
1 /* Distributed Checksum Clearinghouse |
|
2 * |
|
3 * threaded version of client library |
|
4 * |
|
5 * Copyright (c) 2008 by Rhyolite Software, LLC |
|
6 * |
|
7 * This agreement is not applicable to any entity which sells anti-spam |
|
8 * solutions to others or provides an anti-spam solution as part of a |
|
9 * security solution sold to other entities, or to a private network |
|
10 * which employs the DCC or uses data provided by operation of the DCC |
|
11 * but does not provide corresponding data to other users. |
|
12 * |
|
13 * Permission to use, copy, modify, and distribute this software without |
|
14 * changes for any purpose with or without fee is hereby granted, provided |
|
15 * that the above copyright notice and this permission notice appear in all |
|
16 * copies and any distributed versions or copies are either unchanged |
|
17 * or not called anything similar to "DCC" or "Distributed Checksum |
|
18 * Clearinghouse". |
|
19 * |
|
20 * Parties not eligible to receive a license under this agreement can |
|
21 * obtain a commercial license to use DCC by contacting Rhyolite Software |
|
22 * at sales@rhyolite.com. |
|
23 * |
|
24 * A commercial license would be for Distributed Checksum and Reputation |
|
25 * Clearinghouse software. That software includes additional features. This |
|
26 * free license for Distributed ChecksumClearinghouse Software does not in any |
|
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse |
|
28 * software |
|
29 * |
|
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL |
|
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES |
|
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC |
|
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES |
|
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
|
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
|
37 * SOFTWARE. |
|
38 * |
|
39 * Rhyolite Software DCC 1.3.103-1.148 $Revision$ |
|
40 */ |
|
41 |
|
42 |
|
43 #include "cmn_defs.h" |
|
44 #include "dcc_paths.h" |
|
45 |
|
46 CMN_ACTION action = CMN_REJECT; |
|
47 |
|
48 CHGHDR chghdr = SETHDR; |
|
49 |
|
50 const char *userdirs; |
|
51 static DCC_PATH userdirs_path; |
|
52 static int userdirs_len; |
|
53 |
|
54 u_char dcc_query_only; |
|
55 u_char try_extra_hard; /* 0 or DCC_CLNT_FG_NO_FAIL */ |
|
56 u_char to_white_only; |
|
57 |
|
58 u_int dcc_ctxt_sn = 1; /* change X-DCC header server name */ |
|
59 |
|
60 const char *max_max_work_src = "FD_SETSIZE limit"; |
|
61 int max_work; |
|
62 int init_work; |
|
63 int total_work; |
|
64 |
|
65 static int total_rcpt_sts; |
|
66 RCPT_ST *rcpt_st_free; |
|
67 |
|
68 /* cwf_mutex protects all CWF structures as well as the cmn_wf structure */ |
|
69 typedef struct cwf { /* private whitelist state */ |
|
70 struct cwf *older, *newer; |
|
71 DCC_WF wf; |
|
72 } CWF; |
|
73 static CWF *cur_cwf, cwfs[NUM_CWFS]; |
|
74 |
|
75 /* protected by user_log_mutex */ |
|
76 static int user_log_fd = -1; |
|
77 static int log_fd2 = -1; |
|
78 |
|
79 |
|
80 /* the work lock must be held or not yet exist */ |
|
81 static void |
|
82 add_rcpt_sts(int i) |
|
83 { |
|
84 RCPT_ST *rcpt_st; |
|
85 |
|
86 total_rcpt_sts += i; |
|
87 |
|
88 rcpt_st = dcc_malloc(sizeof(*rcpt_st)*i); |
|
89 memset(rcpt_st, 0, sizeof(*rcpt_st)*i); |
|
90 |
|
91 while (i-- != 0) { |
|
92 rcpt_st->fwd = rcpt_st_free; |
|
93 rcpt_st_free = rcpt_st; |
|
94 ++rcpt_st; |
|
95 } |
|
96 } |
|
97 |
|
98 |
|
99 |
|
100 void |
|
101 cmn_init(void) |
|
102 { |
|
103 init_work = 50; |
|
104 if (init_work > max_max_work) |
|
105 init_work = max_max_work; |
|
106 add_rcpt_sts(init_work); |
|
107 |
|
108 finish_replies(); |
|
109 |
|
110 /* start the client library threads and locks */ |
|
111 dcc_clnt_thread_init(); |
|
112 for (cur_cwf = cwfs; cur_cwf <= LAST(cwfs); ++cur_cwf) { |
|
113 cur_cwf->newer = cur_cwf-1; |
|
114 cur_cwf->older = cur_cwf+1; |
|
115 dcc_wf_init(&cur_cwf->wf, DCC_WF_PER_USER); |
|
116 } |
|
117 cur_cwf = cwfs; |
|
118 LAST(cwfs)->older = cur_cwf; |
|
119 cur_cwf->newer = LAST(cwfs); |
|
120 |
|
121 totals_init(); |
|
122 } |
|
123 |
|
124 |
|
125 |
|
126 void |
|
127 cmn_create(CMN_WORK *cwp) |
|
128 { |
|
129 cwp->tmp_fd = -1; |
|
130 cwp->log_fd = -1; |
|
131 } |
|
132 |
|
133 |
|
134 |
|
135 u_char |
|
136 cmn_open_tmp(CMN_WORK *cwp) |
|
137 { |
|
138 cwp->tmp_fd = dcc_mkstemp(cwp->emsg, |
|
139 cwp->tmp_nm, sizeof(cwp->tmp_nm), |
|
140 cwp->id, sizeof(cwp->id), |
|
141 0, 0, DCC_TMP_LOG_PREFIX, 1, 0); |
|
142 |
|
143 return cwp->tmp_fd >= 0; |
|
144 } |
|
145 |
|
146 |
|
147 |
|
148 void |
|
149 cmn_close_tmp(CMN_WORK *cwp) |
|
150 { |
|
151 if (cwp->tmp_fd >= 0) { |
|
152 if (0 > close(cwp->tmp_fd)) |
|
153 thr_error_msg(cwp, "close(%s): %s", |
|
154 cwp->tmp_nm, ERROR_STR()); |
|
155 cwp->tmp_fd = -1; |
|
156 } |
|
157 cwp->tmp_nm[0] = '\0'; |
|
158 } |
|
159 |
|
160 |
|
161 |
|
162 u_char |
|
163 cmn_write_tmp(CMN_WORK *cwp, const void *buf, int len) |
|
164 { |
|
165 int i; |
|
166 |
|
167 if (cwp->tmp_fd < 0) |
|
168 return 1; |
|
169 |
|
170 i = write(cwp->tmp_fd, buf, len); |
|
171 if (i == len) |
|
172 return 1; |
|
173 |
|
174 if (i < 0) |
|
175 thr_error_msg(cwp, "write(%s,%d): %s", |
|
176 cwp->tmp_nm, len, ERROR_STR()); |
|
177 else |
|
178 thr_error_msg(cwp, "write(%s,%d)=%d", |
|
179 cwp->tmp_nm, len, i); |
|
180 cmn_close_tmp(cwp); |
|
181 return 0; |
|
182 } |
|
183 |
|
184 |
|
185 |
|
186 /* If the immediate SMTP client because it is a listed MX server, |
|
187 * then we must ignore its IP address and keep looking for the |
|
188 * real SMTP client. */ |
|
189 u_char /* 1=listed MX server */ |
|
190 check_mx_listing(CMN_WORK *cwp) |
|
191 { |
|
192 DCC_TGTS tgts; |
|
193 u_char result; |
|
194 |
|
195 lock_wf(); |
|
196 result = dcc_white_mx(cwp->emsg, &tgts, &cwp->cks); |
|
197 unlock_wf(); |
|
198 if (!result) { |
|
199 thr_error_msg(cwp, "%s", cwp->emsg); |
|
200 return 0; |
|
201 } |
|
202 |
|
203 if (tgts == DCC_TGTS_OK) { |
|
204 return 0; |
|
205 } |
|
206 |
|
207 if (tgts == DCC_TGTS_SUBMIT_CLIENT) { |
|
208 /* Common SMTP submission clients are too dumb to do the |
|
209 * right thing with 4yz rejections of individual Rcpt_To |
|
210 * commands. So reject the message for all or no recipients. */ |
|
211 cwp->cmn_fgs |= CMN_FG_FROM_SUBMIT; |
|
212 thr_log_print(cwp, 1, |
|
213 "%s is a listed 'submit' client\n", |
|
214 dcc_trim_ffff(cwp->sender_str)); |
|
215 return 0; |
|
216 } |
|
217 |
|
218 if (tgts == DCC_TGTS_OK_MXDCC) { |
|
219 thr_log_print(cwp, 1, |
|
220 "%s is a whitelisted MX server with DCC client\n", |
|
221 dcc_trim_ffff(cwp->sender_str)); |
|
222 cwp->ask_st |= ASK_ST_QUERY; |
|
223 } else if (tgts == DCC_TGTS_OK_MX) { |
|
224 thr_log_print(cwp, 1, "%s is a whitelisted MX server\n", |
|
225 dcc_trim_ffff(cwp->sender_str)); |
|
226 } else { |
|
227 return 0; |
|
228 } |
|
229 |
|
230 /* we cannot greylist or reject through our MX servers */ |
|
231 cwp->cmn_fgs |= CMN_FG_FROM_MX; |
|
232 if (cwp->action == CMN_REJECT) |
|
233 cwp->action = CMN_DISCARD; |
|
234 |
|
235 cwp->sender_name[0] = '\0'; |
|
236 cwp->sender_str[0] = '\0'; |
|
237 dcc_unget_ip_ck(&cwp->cks); |
|
238 |
|
239 /* tell caller to look at the next Received: header */ |
|
240 return 1; |
|
241 } |
|
242 |
|
243 |
|
244 |
|
245 /* clear a common work area for a message, possibly not the first |
|
246 * in the session */ |
|
247 void |
|
248 cmn_clear(CMN_WORK *cwp, struct work *wp, |
|
249 u_char new_conn) /* 1=first message for the connection */ |
|
250 { |
|
251 log_stop(cwp); |
|
252 |
|
253 cmn_close_tmp(cwp); |
|
254 |
|
255 if (cwp->num_rcpts) |
|
256 free_rcpt_sts(cwp, 1); |
|
257 |
|
258 memset(&cwp->CMN_WORK_ZERO, 0, |
|
259 sizeof(*cwp) - ((char*)&cwp->CMN_WORK_ZERO - (char*)cwp)); |
|
260 cwp->cmn_fgs = 0; |
|
261 cwp->mail_host[0] = '\0'; |
|
262 cwp->env_from[0] = '\0'; |
|
263 cwp->early_log.len = 0; |
|
264 cwp->emsg[0] = '\0'; |
|
265 cwp->id[0] = '\0'; |
|
266 cwp->header.used = 0; |
|
267 cwp->cmn_fgs = CMN_FG_LOG_EARLY; |
|
268 if (dcc_query_only) |
|
269 cwp->ask_st |= ASK_ST_QUERY_GREY; |
|
270 cwp->action = action; |
|
271 |
|
272 if (new_conn) { |
|
273 cwp->wp = wp; |
|
274 cwp->helo[0] = '\0'; |
|
275 cwp->clnt_name[0] = '\0'; |
|
276 cwp->clnt_str[0] = '\0'; |
|
277 } else { |
|
278 /* assume for now that the sender is the current SMTP client */ |
|
279 strcpy(cwp->sender_name, cwp->clnt_name); |
|
280 strcpy(cwp->sender_str, cwp->clnt_str); |
|
281 } |
|
282 } |
|
283 |
|
284 |
|
285 |
|
286 /* free all of the per-recipient state for a message */ |
|
287 void |
|
288 free_rcpt_sts(CMN_WORK *cwp, u_char need_lock) |
|
289 { |
|
290 RCPT_ST *rcpt_st, *next_rcpt_st; |
|
291 |
|
292 rcpt_st = cwp->rcpt_st_first; |
|
293 if (!rcpt_st) |
|
294 return; |
|
295 |
|
296 if (need_lock) |
|
297 lock_work(); |
|
298 cwp->rcpt_st_first = 0; |
|
299 do { |
|
300 next_rcpt_st = rcpt_st->fwd; |
|
301 rcpt_st->fwd = rcpt_st_free; |
|
302 rcpt_st_free = rcpt_st; |
|
303 } while ((rcpt_st = next_rcpt_st) != 0); |
|
304 cwp->num_rcpts = 0; |
|
305 |
|
306 if (need_lock) |
|
307 unlock_work(); |
|
308 } |
|
309 |
|
310 |
|
311 |
|
312 RCPT_ST * |
|
313 alloc_rcpt_st(CMN_WORK *cwp, |
|
314 u_char unlocked) /* 1=unlocked on entry & exit */ |
|
315 { |
|
316 RCPT_ST *rcpt_st; |
|
317 |
|
318 if (cwp->num_rcpts >= MAX_RCPTS) { |
|
319 thr_error_msg(cwp, "too many recipients"); |
|
320 return 0; |
|
321 } |
|
322 |
|
323 if (unlocked) |
|
324 lock_work(); |
|
325 rcpt_st = rcpt_st_free; |
|
326 if (!rcpt_st) { |
|
327 if (dcc_clnt_debug > 1) |
|
328 thr_trace_msg(cwp, |
|
329 "add %d recipient blocks to %d", |
|
330 init_work, total_rcpt_sts); |
|
331 add_rcpt_sts(init_work); |
|
332 rcpt_st = rcpt_st_free; |
|
333 } |
|
334 rcpt_st_free = rcpt_st->fwd; |
|
335 if (unlocked) |
|
336 unlock_work(); |
|
337 |
|
338 rcpt_st->fwd = 0; |
|
339 rcpt_st->log_pos_white = -1; |
|
340 rcpt_st->log_pos_to = -1; |
|
341 memset(rcpt_st->wtgts, 0, sizeof(rcpt_st->wtgts)); |
|
342 rcpt_st->env_to_tgts = 0; |
|
343 rcpt_st->user_tgts = 0; |
|
344 rcpt_st->grey_result = ASK_GREY_OFF; |
|
345 rcpt_st->embargo_num = 0; |
|
346 rcpt_st->fgs = 0; |
|
347 rcpt_st->sws = 0; |
|
348 rcpt_st->user[0] = '\0'; |
|
349 rcpt_st->rej_msg[0] = '\0'; |
|
350 rcpt_st->dir[0] = '\0'; |
|
351 rcpt_st->user_log_nm[0] = '\0'; |
|
352 |
|
353 rcpt_st->cwp = cwp; |
|
354 if (!cwp->rcpt_st_first) { |
|
355 cwp->rcpt_st_first = rcpt_st; |
|
356 } else { |
|
357 cwp->rcpt_st_last->fwd = rcpt_st; |
|
358 } |
|
359 cwp->rcpt_st_last = rcpt_st; |
|
360 ++cwp->num_rcpts; |
|
361 |
|
362 return rcpt_st; |
|
363 } |
|
364 |
|
365 |
|
366 |
|
367 void |
|
368 parse_userdirs(const char *arg) |
|
369 { |
|
370 /* add '/' to end of the path without converting it to "/" */ |
|
371 if (*arg == '\0') { |
|
372 userdirs_path[0] = '\0'; |
|
373 userdirs_len = 0; |
|
374 } else { |
|
375 strncpy(userdirs_path, arg, |
|
376 sizeof(userdirs_path)); |
|
377 userdirs_len = strlen(userdirs_path)-1; |
|
378 while (userdirs_len > 1 && userdirs_path[userdirs_len] == '/') |
|
379 --userdirs_len; |
|
380 userdirs_path[++userdirs_len] = '/'; |
|
381 userdirs_path[++userdirs_len] = '\0'; |
|
382 |
|
383 } |
|
384 userdirs = userdirs_path; |
|
385 } |
|
386 |
|
387 |
|
388 |
|
389 /* sanitize recipient mailbox and per-user log and whitelist directory */ |
|
390 u_char /* 0=complain about something */ |
|
391 get_user_dir(RCPT_ST *rcpt_st, |
|
392 const char *str1, int str1_len, const char *str2, int str2_len) |
|
393 { |
|
394 char *p; |
|
395 char c; |
|
396 u_char seen_slash; |
|
397 int dots; |
|
398 int i; |
|
399 |
|
400 if (!userdirs) { |
|
401 rcpt_st->dir[0] = '\0'; |
|
402 return 1; |
|
403 } |
|
404 |
|
405 memcpy(rcpt_st->dir, userdirs, userdirs_len); |
|
406 i = userdirs_len; |
|
407 if (i+str1_len < ISZ(rcpt_st->dir)) |
|
408 memcpy(&rcpt_st->dir[i], str1, str1_len); |
|
409 i += str1_len; |
|
410 if (str2) { |
|
411 if (i+str2_len+1 < ISZ(rcpt_st->dir)) { |
|
412 rcpt_st->dir[i++] = '/'; |
|
413 memcpy(&rcpt_st->dir[i], str2, str2_len); |
|
414 } |
|
415 i += str2_len; |
|
416 } |
|
417 if (i >= ISZ(rcpt_st->dir)) { |
|
418 dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg, |
|
419 "recipient \"%s\" is too long", rcpt_st->dir); |
|
420 rcpt_st->dir[0] = '\0'; |
|
421 return 0; |
|
422 } |
|
423 rcpt_st->dir[i] = '\0'; |
|
424 |
|
425 /* To get a consistent directory name, |
|
426 * convert ASCII upper to lower case. |
|
427 * Be simplistic about international character sets and |
|
428 * avoid locale and portability complications. |
|
429 * Refuse insecure paths. */ |
|
430 seen_slash = 1; /* userdirs ends with '/' */ |
|
431 dots = 0; |
|
432 p = &rcpt_st->dir[userdirs_len]; |
|
433 for (;;) { |
|
434 c = *p; |
|
435 if (c == '/' || c == '\\' || c == '\0') { |
|
436 if (dots == 2) { |
|
437 dcc_pemsg(EX_DATAERR, rcpt_st->cwp->emsg, |
|
438 "path \"%s\" is insecure", |
|
439 rcpt_st->dir); |
|
440 rcpt_st->dir[0] = '\0'; |
|
441 return 0; |
|
442 } |
|
443 if (c == '\0') |
|
444 break; |
|
445 seen_slash = 1; |
|
446 dots = 0; |
|
447 } else if (c == '.' && seen_slash && dots <= 1) { |
|
448 ++dots; |
|
449 } else { |
|
450 *p = DCC_TO_LOWER(c); |
|
451 seen_slash = 0; |
|
452 dots = 0; |
|
453 } |
|
454 ++p; |
|
455 } |
|
456 |
|
457 return 1; |
|
458 } |
|
459 |
|
460 |
|
461 |
|
462 /* start main log file */ |
|
463 void |
|
464 log_start(CMN_WORK *cwp) |
|
465 { |
|
466 char date_buf[40]; |
|
467 |
|
468 /* don't even whine if there is no log directory */ |
|
469 if (dcc_main_logdir[0] == '\0') |
|
470 return; |
|
471 |
|
472 /* nothing to do if we already have a log file */ |
|
473 if (cwp->log_fd >= 0) |
|
474 return; |
|
475 |
|
476 cwp->log_size = 0; |
|
477 cwp->log_fd = dcc_main_log_open(cwp->emsg, cwp->log_nm, |
|
478 cwp->id, sizeof(cwp->id)); |
|
479 if (cwp->log_fd < 0) { |
|
480 static time_t whined; |
|
481 time_t now; |
|
482 |
|
483 /* complain about not being able to open log files |
|
484 * only occassionally */ |
|
485 now = time(0); |
|
486 if (now < whined || now > whined+5*60 || dcc_clnt_debug) |
|
487 dcc_error_msg("%s", cwp->emsg); |
|
488 whined = now; |
|
489 cwp->emsg[0] = '\0'; |
|
490 return; |
|
491 } |
|
492 |
|
493 gettimeofday(&cwp->ldate, 0); |
|
494 |
|
495 thr_log_print(cwp, 0, DCC_LOG_DATE_PAT"\n", |
|
496 dcc_time2str(date_buf, sizeof(date_buf), DCC_LOG_DATE_FMT, |
|
497 cwp->ldate.tv_sec)); |
|
498 } |
|
499 |
|
500 |
|
501 |
|
502 /* get an independent FD for the main log file that can be |
|
503 * repositioned without affecting additional output to the main log. */ |
|
504 static u_char |
|
505 log2_start(CMN_WORK *cwp) |
|
506 { |
|
507 DCC_PATH abs_nm; |
|
508 |
|
509 #ifdef DCC_DEBUG_CLNT_LOCK |
|
510 if (user_log_owner != pthread_self()) |
|
511 dcc_logbad(EX_SOFTWARE, "don't have user_log lock"); |
|
512 #endif |
|
513 |
|
514 if (log_fd2 >= 0) |
|
515 return 1; |
|
516 |
|
517 /* give up if things are already broken */ |
|
518 if (log_fd2 != -1 |
|
519 || cwp->log_fd < 0) |
|
520 return 0; |
|
521 |
|
522 /* Some systems don't synchronize the meta data among FDs for |
|
523 * a file, causing the second FD to appear to be truncated. */ |
|
524 if (fsync(cwp->log_fd) < 0) |
|
525 thr_error_msg(cwp, "log_fd fsync(%s): %s", |
|
526 fnm2abs_err(abs_nm, cwp->log_nm), |
|
527 ERROR_STR()); |
|
528 |
|
529 log_fd2 = open(cwp->log_nm, O_RDWR, 0); |
|
530 if (log_fd2 < 0) { |
|
531 thr_error_msg(cwp, "log_fd2 open(%s): %s", |
|
532 fnm2abs_err(abs_nm, cwp->log_nm), |
|
533 ERROR_STR()); |
|
534 log_fd2 = -2; |
|
535 return 0; |
|
536 } |
|
537 |
|
538 return 1; |
|
539 } |
|
540 |
|
541 |
|
542 |
|
543 static void |
|
544 log_fd2_close(int flag) |
|
545 { |
|
546 if (user_log_owner == pthread_self()) { |
|
547 if (log_fd2 >= 0) |
|
548 close(log_fd2); |
|
549 log_fd2 = flag; |
|
550 } |
|
551 } |
|
552 |
|
553 |
|
554 |
|
555 void |
|
556 log_stop(CMN_WORK *cwp) |
|
557 { |
|
558 thr_log_late(cwp); |
|
559 log_fd2_close(-1); |
|
560 |
|
561 if (cwp->log_fd < 0) |
|
562 return; |
|
563 |
|
564 /* Close before renaming to accomodate WIN32 foolishness. |
|
565 * Assuming dcc_mkstemp() works properly, there is no race */ |
|
566 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); |
|
567 if (!(cwp->ask_st & ASK_ST_LOGIT)) { |
|
568 /* Delete the log file if it is not interesting */ |
|
569 unlink(cwp->log_nm); |
|
570 } else { |
|
571 /* give it a permanent name if it is interesting */ |
|
572 dcc_log_keep(0, cwp->log_nm); |
|
573 } |
|
574 cwp->log_nm[0] = '\0'; |
|
575 cwp->log_fd = -1; |
|
576 } |
|
577 |
|
578 |
|
579 |
|
580 void |
|
581 log_write(CMN_WORK *cwp, const void *buf, u_int buflen) |
|
582 { |
|
583 int result; |
|
584 |
|
585 if (cwp->log_fd < 0) |
|
586 return; |
|
587 |
|
588 if (!buflen) |
|
589 buflen = strlen(buf); |
|
590 cwp->log_size += buflen; |
|
591 |
|
592 result = write(cwp->log_fd, buf, buflen); |
|
593 if (buflen == (u_int)result) |
|
594 return; |
|
595 |
|
596 if (result < 0) |
|
597 dcc_error_msg("write(%s): %s", cwp->log_nm, ERROR_STR()); |
|
598 else |
|
599 dcc_error_msg("write(%s,%d)=%d", cwp->log_nm, buflen, result); |
|
600 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); |
|
601 cwp->log_fd = -1; |
|
602 } |
|
603 |
|
604 |
|
605 |
|
606 void |
|
607 log_body_write(CMN_WORK *cwp, const char *buf, u_int buflen) |
|
608 { |
|
609 int trimlen; |
|
610 const char *p, *lim; |
|
611 |
|
612 if (cwp->log_fd < 0) |
|
613 return; |
|
614 |
|
615 /* just write if there is room */ |
|
616 trimlen = MAX_LOG_KBYTE*1024 - cwp->log_size; |
|
617 if (trimlen >= (int)buflen) { |
|
618 log_write(cwp, buf, buflen); |
|
619 return; |
|
620 } |
|
621 |
|
622 /* do nothing if too much already written */ |
|
623 if (trimlen < 0) |
|
624 return; |
|
625 |
|
626 /* look for and end-of-line near the end of the buffer |
|
627 * so that we can make the truncation pretty */ |
|
628 lim = buf; |
|
629 p = lim+trimlen; |
|
630 if (trimlen > 90) |
|
631 lim += trimlen-90; |
|
632 while (--p > lim) { |
|
633 if (*p == '\n') { |
|
634 trimlen = p-buf+1; |
|
635 break; |
|
636 } |
|
637 } |
|
638 log_write(cwp, buf, trimlen); |
|
639 if (buf[trimlen-1] != '\n') |
|
640 LOG_CMN_EOL(cwp); |
|
641 LOG_CMN_CAPTION(cwp, DCC_LOG_TRN_MSG_CR); |
|
642 cwp->log_size = MAX_LOG_KBYTE*1024+1; |
|
643 } |
|
644 |
|
645 |
|
646 |
|
647 off_t |
|
648 log_lseek_get(CMN_WORK *cwp) |
|
649 { |
|
650 off_t result; |
|
651 |
|
652 if (cwp->log_fd < 0) |
|
653 return 0; |
|
654 result = lseek(cwp->log_fd, 0, SEEK_END); |
|
655 if (result == -1) { |
|
656 thr_error_msg(cwp, "lseek(%s, 0, SEEK_END): %s", |
|
657 cwp->log_nm, ERROR_STR()); |
|
658 dcc_log_close(0, cwp->log_nm, cwp->log_fd, &cwp->ldate); |
|
659 cwp->log_fd = -1; |
|
660 return 0; |
|
661 } |
|
662 return result; |
|
663 } |
|
664 |
|
665 |
|
666 |
|
667 static u_char |
|
668 log_lseek_set(CMN_WORK *cwp, off_t pos) |
|
669 { |
|
670 #ifdef DCC_DEBUG_CLNT_LOCK |
|
671 if (user_log_owner != pthread_self()) |
|
672 dcc_logbad(EX_SOFTWARE, "don't have user_log lock"); |
|
673 #endif |
|
674 |
|
675 if (log_fd2 < 0) |
|
676 return 0; |
|
677 |
|
678 if (-1 == lseek(log_fd2, pos, SEEK_SET)) { |
|
679 thr_error_msg(cwp, "lseek(%s,%d,SEEK_SET): %s", |
|
680 cwp->log_nm, (int)pos, ERROR_STR()); |
|
681 log_fd2_close(-2); |
|
682 return 0; |
|
683 } |
|
684 |
|
685 return 1; |
|
686 } |
|
687 |
|
688 |
|
689 |
|
690 /* put something into a log file |
|
691 * does not append '\n' */ |
|
692 static int /* bytes written */ |
|
693 vthr_log_print(CMN_WORK *cwp, |
|
694 u_char error, /* 1=important enough to buffer */ |
|
695 const char *p, va_list args) |
|
696 { |
|
697 char logbuf[LOGBUF_SIZE*2]; |
|
698 int i; |
|
699 |
|
700 if (cwp->log_fd < 0 |
|
701 || (error && (cwp->cmn_fgs & CMN_FG_LOG_EARLY))) { |
|
702 return dcc_vearly_log(&cwp->early_log, p, args); |
|
703 } |
|
704 |
|
705 i = vsnprintf(logbuf, sizeof(logbuf), p, args); |
|
706 if (i < ISZ(logbuf)) { |
|
707 log_write(cwp, logbuf, i); |
|
708 return i; |
|
709 } |
|
710 log_write(cwp, logbuf, sizeof(logbuf)); |
|
711 log_write(cwp, "...", 3); |
|
712 return i+3; |
|
713 } |
|
714 |
|
715 |
|
716 |
|
717 /* does not append '\n' */ |
|
718 int PATTRIB(3,4) /* bytes written */ |
|
719 thr_log_print(void *cwp, u_char error, const char *pat, ...) |
|
720 { |
|
721 va_list args; |
|
722 int i; |
|
723 |
|
724 va_start(args, pat); |
|
725 i = vthr_log_print(cwp, error, pat, args); |
|
726 va_end(args); |
|
727 return i; |
|
728 } |
|
729 |
|
730 |
|
731 |
|
732 int /* bytes written */ |
|
733 thr_error_msg(void *cwp0, const char *pat, ...) |
|
734 { |
|
735 CMN_WORK *cwp = cwp0; |
|
736 va_list args; |
|
737 int i; |
|
738 |
|
739 va_start(args, pat); |
|
740 dcc_verror_msg(pat, args); |
|
741 va_end(args); |
|
742 |
|
743 va_start(args, pat); |
|
744 i = vthr_log_print(cwp, 1, pat, args); |
|
745 va_end(args); |
|
746 thr_log_print(cwp, 1, "\n"); |
|
747 |
|
748 cwp->ask_st |= ASK_ST_LOGIT; |
|
749 |
|
750 return i+1; |
|
751 } |
|
752 |
|
753 |
|
754 |
|
755 void |
|
756 thr_trace_msg(void *cwp0, const char *p, ...) |
|
757 { |
|
758 va_list args; |
|
759 |
|
760 va_start(args, p); |
|
761 dcc_vtrace_msg(p, args); |
|
762 va_end(args); |
|
763 |
|
764 if (cwp0) { |
|
765 CMN_WORK *cwp = cwp0; |
|
766 va_start(args, p); |
|
767 vthr_log_print(cwp, 1, p, args); |
|
768 va_end(args); |
|
769 thr_log_print(cwp, 1, "\n"); |
|
770 |
|
771 cwp->ask_st |= ASK_ST_LOGIT; |
|
772 } |
|
773 } |
|
774 |
|
775 |
|
776 |
|
777 void |
|
778 thr_log_late(CMN_WORK *cwp) |
|
779 { |
|
780 cwp->cmn_fgs &= ~CMN_FG_LOG_EARLY; |
|
781 if (cwp->early_log.len) { |
|
782 log_write(cwp, cwp->early_log.buf, cwp->early_log.len); |
|
783 cwp->early_log.len = 0; |
|
784 } |
|
785 } |
|
786 |
|
787 |
|
788 |
|
789 void |
|
790 thr_log_envelope(CMN_WORK *cwp, u_char ip_placekeeper) |
|
791 { |
|
792 RCPT_ST *rcpt_st; |
|
793 off_t cur_pos; |
|
794 int i; |
|
795 |
|
796 cwp->cmn_fgs |= CMN_FG_ENV_LOGGED; |
|
797 |
|
798 /* install the sender in blank area in the log file that we skipped */ |
|
799 cwp->log_ip_pos = log_lseek_get(cwp) + LITZ(DCC_XHDR_TYPE_IP": "); |
|
800 if (cwp->sender_str[0] != '\0') { |
|
801 /* Dccm will not have computed the checksum |
|
802 * if there were no envelope Mail_From commands |
|
803 * On a second message in a session, dccm will not have |
|
804 * looked at cwp->clnt_addr */ |
|
805 if (cwp->cks.sums[DCC_CK_IP].type == DCC_CK_INVALID) |
|
806 dcc_get_ipv6_ck(&cwp->cks, &cwp->clnt_addr); |
|
807 i = thr_log_print(cwp, 0, |
|
808 DCC_XHDR_TYPE_IP": %s %s\n", |
|
809 cwp->sender_name, cwp->sender_str); |
|
810 |
|
811 } else if (ip_placekeeper) { |
|
812 /* log a blank, place keeping string for the IP address |
|
813 * so that it can be inserted later */ |
|
814 i = thr_log_print(cwp, 0, |
|
815 DCC_XHDR_TYPE_IP": %*s\n", |
|
816 1+1+1+INET6_ADDRSTRLEN, ""); |
|
817 } else { |
|
818 i = 0; |
|
819 } |
|
820 i -= LITZ(DCC_XHDR_TYPE_IP": \n"); |
|
821 cwp->log_ip_len = i > 0 ? i : 0; |
|
822 |
|
823 /* log HELO value if we have it |
|
824 * make checksum of it or of null string if we don't */ |
|
825 if (cwp->helo[0] != '\0') |
|
826 thr_log_print(cwp, 0, "HELO: %s\n", cwp->helo); |
|
827 dcc_ck_get_sub(&cwp->cks, "helo", cwp->helo); |
|
828 |
|
829 if (cwp->env_from[0] != '\0') { |
|
830 LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_FROM": "); |
|
831 log_write(cwp, cwp->env_from, 0); |
|
832 dcc_get_cks(&cwp->cks, DCC_CK_ENV_FROM, cwp->env_from, 1); |
|
833 |
|
834 if (cwp->mail_host[0] == '\0') |
|
835 parse_mail_host(cwp->env_from, cwp->mail_host, |
|
836 sizeof(cwp->mail_host)); |
|
837 |
|
838 LOG_CMN_CAPTION(cwp, " mail_host="); |
|
839 log_write(cwp, cwp->mail_host, 0); |
|
840 if (cwp->mail_host[0] != '\0') |
|
841 dcc_ck_get_sub(&cwp->cks, "mail_host", cwp->mail_host); |
|
842 LOG_CMN_EOL(cwp); |
|
843 } |
|
844 |
|
845 cwp->log_pos_to_first = cur_pos = log_lseek_get(cwp); |
|
846 for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) { |
|
847 rcpt_st->log_pos_to = cur_pos; |
|
848 LOG_CMN_CAPTION(cwp, DCC_XHDR_TYPE_ENV_TO": "); |
|
849 log_write(cwp, rcpt_st->env_to, 0); |
|
850 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) { |
|
851 LOG_CMN_CAPTION(cwp, " "DCC_XHDR_MTA_REJECTION"\n"); |
|
852 } else { |
|
853 LOG_CMN_CAPTION(cwp, " addr="); |
|
854 log_write(cwp, rcpt_st->user, 0); |
|
855 LOG_CMN_CAPTION(cwp, " dir="); |
|
856 log_write(cwp, rcpt_st->dir, 0); |
|
857 LOG_CMN_EOL(cwp); |
|
858 } |
|
859 cur_pos = log_lseek_get(cwp); |
|
860 } |
|
861 cwp->log_pos_to_end = cur_pos; |
|
862 |
|
863 /* log the blank line between the log file header and mail message */ |
|
864 LOG_CMN_EOL(cwp); |
|
865 } |
|
866 |
|
867 |
|
868 |
|
869 /* open the connection to the nearest DCC server */ |
|
870 u_char |
|
871 ck_dcc_ctxt(CMN_WORK *cwp) |
|
872 { |
|
873 if (cwp->dcc_ctxt_sn != dcc_ctxt_sn) { |
|
874 cwp->dcc_ctxt_sn = dcc_ctxt_sn; |
|
875 cwp->dcc_ctxt = dcc_clnt_init(cwp->emsg, cwp->dcc_ctxt, 0, |
|
876 DCC_CLNT_FG_BAD_SRVR_OK |
|
877 | DCC_CLNT_FG_NO_PICK_SRVR |
|
878 | DCC_CLNT_FG_NO_FAIL); |
|
879 if (!cwp->dcc_ctxt) { |
|
880 /* failed to create context */ |
|
881 thr_error_msg(cwp, "%s", cwp->emsg); |
|
882 cwp->dcc_ctxt_sn = 0; |
|
883 return 0; |
|
884 } |
|
885 cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname, |
|
886 sizeof(cwp->xhdr_fname), |
|
887 dcc_clnt_info); |
|
888 } |
|
889 return 1; |
|
890 } |
|
891 |
|
892 |
|
893 |
|
894 /* find and lock a per-user DCC_WF |
|
895 * it is locked by grabbing the mutex for the main whiteclnt file */ |
|
896 static CWF * |
|
897 find_cwf(RCPT_ST *rcpt_st) |
|
898 { |
|
899 CWF *cwf; |
|
900 DCC_PATH white_nm_buf; |
|
901 const char *white_nm_ptr; |
|
902 |
|
903 if (rcpt_st->dir[0] == '\0') { |
|
904 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; |
|
905 return 0; |
|
906 } |
|
907 |
|
908 /* canonicalize the key */ |
|
909 if (!fnm2rel(white_nm_buf, rcpt_st->dir, "/whiteclnt")) { |
|
910 thr_error_msg(rcpt_st->cwp, |
|
911 "long user whiteclnt name \"%s/whiteclnt\"", |
|
912 rcpt_st->dir); |
|
913 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; |
|
914 return 0; |
|
915 } |
|
916 white_nm_ptr = path2fnm(white_nm_buf); |
|
917 |
|
918 lock_wf(); |
|
919 cwf = cur_cwf; |
|
920 for (;;) { |
|
921 if (!strcmp(white_nm_ptr, cwf->wf.ascii_nm)) |
|
922 break; /* found old DCC_WF for target file */ |
|
923 |
|
924 if (cwf->older == cur_cwf) { |
|
925 /* We do not know this file. |
|
926 * Recycle the oldest DCC_WF */ |
|
927 if (!dcc_new_white_nm(rcpt_st->cwp->emsg, &cwf->wf, |
|
928 white_nm_ptr)) { |
|
929 thr_error_msg(rcpt_st->cwp, "%s", |
|
930 rcpt_st->cwp->emsg); |
|
931 unlock_wf(); |
|
932 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; |
|
933 return 0; |
|
934 } |
|
935 break; |
|
936 } |
|
937 |
|
938 cwf = cwf->older; |
|
939 } |
|
940 |
|
941 /* move to front */ |
|
942 if (cwf != cur_cwf) { |
|
943 cwf->older->newer = cwf->newer; |
|
944 cwf->newer->older = cwf->older; |
|
945 cwf->older = cur_cwf; |
|
946 cwf->newer = cur_cwf->newer; |
|
947 cwf->newer->older = cwf; |
|
948 cwf->older->newer = cwf; |
|
949 cur_cwf = cwf; |
|
950 } |
|
951 |
|
952 switch (dcc_rdy_white(rcpt_st->cwp->emsg, &cwf->wf, &cmn_tmp_wf)) { |
|
953 case DCC_WHITE_CONTINUE: |
|
954 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); |
|
955 /* fall through */ |
|
956 case DCC_WHITE_OK: |
|
957 /* notice if the file contains no checksums or CIDR blocks |
|
958 * or flag bits that make the file differ from an empty |
|
959 * or non-existent file */ |
|
960 if (cwf->wf.wtbl->hdr.entries == 0 |
|
961 && cwf->wf.wtbl->hdr.cidr.len == 0 |
|
962 && !(cwf->wf.wtbl_flags & DCC_WHITE_FG_TRAPS)) { |
|
963 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; |
|
964 } else { |
|
965 rcpt_st->fgs &= ~RCPT_FG_NULL_WHITECLNT; |
|
966 memcpy(rcpt_st->wf_sum, cwf->wf.wtbl->hdr.ck_sum, |
|
967 sizeof(rcpt_st->wf_sum)); |
|
968 } |
|
969 return cwf; |
|
970 case DCC_WHITE_NOFILE: |
|
971 break; |
|
972 case DCC_WHITE_SILENT: |
|
973 if (dcc_clnt_debug) |
|
974 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); |
|
975 break; |
|
976 case DCC_WHITE_COMPLAIN: |
|
977 thr_error_msg(rcpt_st->cwp, "%s", rcpt_st->cwp->emsg); |
|
978 break; |
|
979 } |
|
980 |
|
981 unlock_wf(); |
|
982 rcpt_st->fgs |= RCPT_FG_NULL_WHITECLNT; |
|
983 return 0; |
|
984 } |
|
985 |
|
986 |
|
987 |
|
988 /* digest the results of one recipient's whitelist */ |
|
989 static void |
|
990 white_results(RCPT_ST *rcpt_st, |
|
991 CMN_WORK *cwp, |
|
992 RCPT_FGS *fgsp, |
|
993 DCC_WHITE_RESULT result, |
|
994 const DCC_WHITE_LISTING *listingp) |
|
995 { |
|
996 |
|
997 DCC_WHITE_LISTING listing; |
|
998 |
|
999 /* call-by-reference parameter to resolve order of evaluation |
|
1000 * in callers */ |
|
1001 listing = *listingp; |
|
1002 |
|
1003 /* override if the result of the whitelist lookup was bad */ |
|
1004 switch (result) { |
|
1005 case DCC_WHITE_OK: |
|
1006 break; |
|
1007 case DCC_WHITE_SILENT: |
|
1008 case DCC_WHITE_NOFILE: |
|
1009 listing = DCC_WHITE_UNLISTED; |
|
1010 break; |
|
1011 case DCC_WHITE_COMPLAIN: |
|
1012 case DCC_WHITE_CONTINUE: |
|
1013 thr_error_msg(cwp, "%s", cwp->emsg); |
|
1014 return; |
|
1015 } |
|
1016 |
|
1017 switch (listing) { |
|
1018 case DCC_WHITE_USE_DCC: |
|
1019 /* "OK2" for the env_to checksum in the local whitelist |
|
1020 * does not mean the address is half whitelisted, |
|
1021 * but that it is ok to reject or discard spam for it based |
|
1022 * on DCC results. |
|
1023 * It is the original, deprecated mechanism for turn DCC checks |
|
1024 * on and off for individual targets |
|
1025 * We get this value only from dcc_white_sum() */ |
|
1026 if (rcpt_st) |
|
1027 rcpt_st->sws &= ~FLTR_SW_DCC_OFF; |
|
1028 /* fall through */ |
|
1029 case DCC_WHITE_UNLISTED: |
|
1030 /* a spam trap rejects everything |
|
1031 * or accepts but marks everything as spam */ |
|
1032 if (rcpt_st && (rcpt_st->sws & FLTR_SW_TRAPS)) { |
|
1033 if (!(*fgsp & RCPT_FG_WHITE)) |
|
1034 *fgsp |= RCPT_FG_BLACK; |
|
1035 /* remember this hit for the log */ |
|
1036 *fgsp |= RCPT_FG_WLIST_ISSPAM; |
|
1037 cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM; |
|
1038 } |
|
1039 break; |
|
1040 |
|
1041 case DCC_WHITE_LISTED: |
|
1042 *fgsp |= RCPT_FG_WHITE; |
|
1043 *fgsp &= ~RCPT_FG_BLACK; |
|
1044 /* remember this hit for the log */ |
|
1045 *fgsp |= RCPT_FG_WLIST_NOTSPAM; |
|
1046 cwp->rcpt_fgs |= RCPT_FG_WLIST_NOTSPAM; |
|
1047 break; |
|
1048 |
|
1049 case DCC_WHITE_BLACK: |
|
1050 if (!(*fgsp & RCPT_FG_WHITE)) |
|
1051 *fgsp |= RCPT_FG_BLACK; |
|
1052 /* remember this hit for the log */ |
|
1053 *fgsp |= RCPT_FG_WLIST_ISSPAM; |
|
1054 cwp->rcpt_fgs |= RCPT_FG_WLIST_ISSPAM; |
|
1055 break; |
|
1056 } |
|
1057 } |
|
1058 |
|
1059 |
|
1060 |
|
1061 static void |
|
1062 rcpt_fgs2ask_st(CMN_WORK *cwp, FLTR_SWS sws, RCPT_FGS fgs) |
|
1063 { |
|
1064 /* We need to know if all targets are whitelisted for the DCC |
|
1065 * before we ask the DCC server. Mail sent only to whitelisted |
|
1066 * targets should not be reported to the DCC server. |
|
1067 * For that we need a count of whitelisted targets */ |
|
1068 if (fgs & RCPT_FG_WHITE) { |
|
1069 ++cwp->white_tgts; |
|
1070 return; |
|
1071 } |
|
1072 |
|
1073 /* it is spam if it is blacklisted by any target */ |
|
1074 if (fgs & RCPT_FG_BLACK) { |
|
1075 cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT); |
|
1076 return; |
|
1077 } |
|
1078 |
|
1079 /* If we had a DNS blacklist hit |
|
1080 * and if this recipient believes the DNS blacklist, |
|
1081 * then it is spam to report to DCC server. |
|
1082 * We need to know if it is spam for at least one target before |
|
1083 * deciding what to do for each target. */ |
|
1084 if (0 != (FLTR_SW_DNSBL_BITS(sws) & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st))) |
|
1085 cwp->ask_st |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT); |
|
1086 } |
|
1087 |
|
1088 |
|
1089 |
|
1090 /* set the global defaults for the switches or options */ |
|
1091 static void |
|
1092 rcpt_sws_def(CMN_WORK *cwp, u_char locked) |
|
1093 { |
|
1094 if (!locked) |
|
1095 lock_wf(); |
|
1096 |
|
1097 if (to_white_only) |
|
1098 cwp->init_sws |= FLTR_SW_DCC_OFF; |
|
1099 cwp->init_sws |= FLTR_SW_NO_DISCARD; |
|
1100 cwp->init_sws = wf2sws(cwp->init_sws, &cmn_wf); |
|
1101 if (cannot_discard) /* enforce the default if necessary */ |
|
1102 cwp->init_sws |= FLTR_SW_NO_DISCARD; |
|
1103 cwp->rcpts_sws = cwp->init_sws; |
|
1104 |
|
1105 if (!locked) |
|
1106 unlock_wf(); |
|
1107 } |
|
1108 |
|
1109 |
|
1110 |
|
1111 /* merge global and per-user thresholds */ |
|
1112 static void |
|
1113 make_tholds(DCC_CKSUM_THOLDS out, CWF *cwf) |
|
1114 { |
|
1115 dcc_merge_tholds(out, dcc_tholds_rej, cmn_wf.wtbl); |
|
1116 |
|
1117 if (cwf && cwf->wf.wtbl) |
|
1118 dcc_merge_tholds(out, out, cwf->wf.wtbl); |
|
1119 } |
|
1120 |
|
1121 |
|
1122 |
|
1123 /* set per-user switches and compute env_to whitelisting |
|
1124 * If we return a pointer, then we grabbed and have kept the lock */ |
|
1125 static CWF * |
|
1126 rcpt_sws_env_to(CMN_WORK *cwp, RCPT_ST *rcpt_st) |
|
1127 { |
|
1128 CWF *cwf; |
|
1129 DCC_WHITE_LISTING listing; |
|
1130 |
|
1131 /* We are finished after finding the recipient's whitelist if we |
|
1132 * have already checked its settings. |
|
1133 * |
|
1134 * In this case, we have been here before for this recipient |
|
1135 * and fetched the global as well as this recipient's switch settings |
|
1136 * and action. |
|
1137 * |
|
1138 * If we found that the file was empty of checksums or non-existent |
|
1139 * before, then assume it is still is and so repeat the previous 0 |
|
1140 * answer without wasting time looking. This is an extremely |
|
1141 * common case that deserves optimizing. */ |
|
1142 if (rcpt_st->fgs & RCPT_FG_NULL_WHITECLNT) |
|
1143 return 0; |
|
1144 |
|
1145 /* after mapping the per-recipient whiteclnt file, |
|
1146 * do not repeat the work of examining it if we have already done it */ |
|
1147 cwf = find_cwf(rcpt_st); |
|
1148 if (rcpt_st->sws & FLTR_SW_SET) |
|
1149 return cwf; |
|
1150 |
|
1151 /* The first time for the first recipient, |
|
1152 * we must get the global switch settings. |
|
1153 * If we have a per-user whitelist, then we have locked cmn_wf |
|
1154 * to protect the per-user whitelist data structures */ |
|
1155 if (!(cwp->init_sws & FLTR_SW_SET)) |
|
1156 rcpt_sws_def(cwp, cwf != 0); |
|
1157 |
|
1158 /* get flags and filter settings for recipient, |
|
1159 * including setting FLTR_SW_SET so that we won't do this again */ |
|
1160 if (cwf) |
|
1161 rcpt_st->sws = wf2sws(cwp->init_sws, &cwf->wf); |
|
1162 else |
|
1163 rcpt_st->sws = cwp->init_sws; |
|
1164 if (cannot_discard) /* dccifd cannot discard */ |
|
1165 rcpt_st->sws |= FLTR_SW_NO_DISCARD; |
|
1166 |
|
1167 /* if we have a per-user whitelist, then we have already locked cmn_wf |
|
1168 * to protect the per-user whitelist data structures */ |
|
1169 if (!cwf) |
|
1170 lock_wf(); |
|
1171 |
|
1172 /* check the env_to address in the global whitelist */ |
|
1173 dcc_str2ck(rcpt_st->env_to_sum, 0, 0, rcpt_st->env_to); |
|
1174 rcpt_st->global_env_to_fgs = 0; |
|
1175 white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs, |
|
1176 dcc_white_sum(cwp->emsg, &cmn_wf, |
|
1177 DCC_CK_ENV_TO, rcpt_st->env_to_sum, |
|
1178 &rcpt_st->env_to_tgts, &listing), |
|
1179 &listing); |
|
1180 if (listing != DCC_WHITE_UNLISTED) |
|
1181 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; |
|
1182 |
|
1183 /* check the mailbox name (after aliases etc.) in the global whitelist |
|
1184 * if we did not just check it as the envelope Rcpt_To value */ |
|
1185 if (rcpt_st->user[0] != '\0' |
|
1186 && strcmp(rcpt_st->env_to, rcpt_st->user)) { |
|
1187 dcc_str2ck(rcpt_st->user_sum, 0, 0, rcpt_st->user); |
|
1188 white_results(rcpt_st, cwp, &rcpt_st->global_env_to_fgs, |
|
1189 dcc_white_sum(cwp->emsg, &cmn_wf, |
|
1190 DCC_CK_ENV_TO, rcpt_st->user_sum, |
|
1191 &rcpt_st->user_tgts, &listing), |
|
1192 &listing); |
|
1193 if (listing != DCC_WHITE_UNLISTED) |
|
1194 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; |
|
1195 } |
|
1196 if (!cwf) { |
|
1197 unlock_wf(); |
|
1198 |
|
1199 } else { |
|
1200 /* save per-user envelope Rcpt_To value and mailbox name |
|
1201 * (after aliases etc.) white- or blacklisting |
|
1202 * and the DCC_WHITE_USE_DCC setting */ |
|
1203 rcpt_st->env_to_fgs = 0; |
|
1204 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, |
|
1205 dcc_white_sum(cwp->emsg, &cwf->wf, |
|
1206 DCC_CK_ENV_TO, rcpt_st->env_to_sum, |
|
1207 &rcpt_st->env_to_tgts, &listing), |
|
1208 &listing); |
|
1209 if (rcpt_st->user[0] != '\0' |
|
1210 && strcmp(rcpt_st->env_to, rcpt_st->user)) { |
|
1211 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, |
|
1212 dcc_white_sum(cwp->emsg, &cwf->wf, |
|
1213 DCC_CK_ENV_TO, |
|
1214 rcpt_st->user_sum, |
|
1215 &rcpt_st->user_tgts, |
|
1216 &listing), |
|
1217 &listing); |
|
1218 } |
|
1219 } |
|
1220 |
|
1221 /* remove any reset _ON bits from the consensus */ |
|
1222 cwp->rcpts_sws &= (rcpt_st->sws | ~FLTR_SWS_SETTINGS_ON); |
|
1223 /* add any set _OFF bits to the consensus */ |
|
1224 cwp->rcpts_sws |= (rcpt_st->sws & FLTR_SWS_SETTINGS_OFF); |
|
1225 |
|
1226 return cwf; |
|
1227 } |
|
1228 |
|
1229 |
|
1230 |
|
1231 /* see if a recipient's whitelist decision is certain to be the same |
|
1232 * as all preceding recipients */ |
|
1233 u_char /* 0=must reject this recipient */ |
|
1234 cmn_compat_whitelist(CMN_WORK *cwp, |
|
1235 RCPT_ST *rcpt_st_new) |
|
1236 { |
|
1237 RCPT_ST *rcpt_st2; |
|
1238 CWF *cwf; |
|
1239 FLTR_SWS save_rcpts_sws; |
|
1240 DCC_CKSUM_THOLDS tholds_rej; |
|
1241 |
|
1242 /* everything is compatible if we won't reject |
|
1243 * or if we are forced to reject for all if we reject for any */ |
|
1244 if ((cwp->action != CMN_REJECT) |
|
1245 || cannot_reject |
|
1246 || (cwp->cmn_fgs & CMN_FG_FROM_SUBMIT)) |
|
1247 return 1; |
|
1248 |
|
1249 /* postpone poking at whitelists on the first not-rejected recipient */ |
|
1250 rcpt_st2 = cwp->rcpt_st_first; |
|
1251 for (;;) { |
|
1252 /* wait until later if this is the first recipient */ |
|
1253 if (rcpt_st2 == rcpt_st_new) |
|
1254 return 1; |
|
1255 |
|
1256 if (!(rcpt_st2->fgs & (RCPT_FG_REJ_FILTER |
|
1257 | RCPT_FG_BAD_USERNAME))) |
|
1258 break; |
|
1259 rcpt_st2 = rcpt_st2->fwd; |
|
1260 if (!rcpt_st2) |
|
1261 return 1; |
|
1262 } |
|
1263 |
|
1264 /* we are dealing with a second recipient |
|
1265 * get the settings that we postponed when we saw the first recipient */ |
|
1266 if (!(rcpt_st2->sws & FLTR_SW_SET)) { |
|
1267 cwf = rcpt_sws_env_to(cwp, rcpt_st2); |
|
1268 make_tholds(cwp->cks.tholds_rej, cwf); |
|
1269 cwp->cmn_fgs |= CMN_FG_THOLDS_SET; |
|
1270 if (cwf) |
|
1271 unlock_wf(); |
|
1272 } |
|
1273 |
|
1274 /* See if this message might be accepted for this recipient |
|
1275 * but rejected for some other recipient that does not want |
|
1276 * forced discarding or if this recipient does not want forced |
|
1277 * discarding and has weaker filtering than other recipients. |
|
1278 * If so, reject this recipient. */ |
|
1279 |
|
1280 save_rcpts_sws = cwp->rcpts_sws; |
|
1281 cwf = rcpt_sws_env_to(cwp, rcpt_st_new); |
|
1282 make_tholds(tholds_rej, cwf); |
|
1283 if (cwf) |
|
1284 unlock_wf(); |
|
1285 |
|
1286 /* Differing DCC thresholds conflict */ |
|
1287 if (memcmp(tholds_rej, cwp->cks.tholds_rej, sizeof(tholds_rej))) { |
|
1288 cwp->rcpts_sws = save_rcpts_sws; |
|
1289 cwp->ask_st |= ASK_ST_LOGIT; |
|
1290 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; |
|
1291 return 0; |
|
1292 } |
|
1293 |
|
1294 /* Accept-traps prefer to avoid all rejections including Mail_From |
|
1295 * commands and implicitly discard but tolerate rejections. |
|
1296 * Reject-traps require rejections. */ |
|
1297 if (rcpt_st_new->sws & FLTR_SW_TRAP_ACC) |
|
1298 return 1; |
|
1299 |
|
1300 /* everything other than differing thesholds is compatible |
|
1301 * if discarding is ok for all recipients so far */ |
|
1302 if (!(cwp->rcpts_sws & FLTR_SW_NO_DISCARD)) |
|
1303 return 1; |
|
1304 |
|
1305 do { |
|
1306 /* ignore already rejected recipients */ |
|
1307 if (rcpt_st2->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME)) |
|
1308 continue; |
|
1309 |
|
1310 /* Traps are fexible. */ |
|
1311 if (rcpt_st2->sws & FLTR_SW_TRAP_ACC) |
|
1312 continue; |
|
1313 |
|
1314 /* Differing whitelists make it possible that the message |
|
1315 * could need to be rejected for one recipient but accepted |
|
1316 * for the other */ |
|
1317 if (((rcpt_st2->sws & FLTR_SW_NO_DISCARD) |
|
1318 || (rcpt_st_new->sws & FLTR_SW_NO_DISCARD)) |
|
1319 && (((rcpt_st2->fgs ^ rcpt_st_new->fgs) |
|
1320 & RCPT_FG_NULL_WHITECLNT) != 0 |
|
1321 || (!(rcpt_st2->fgs & RCPT_FG_NULL_WHITECLNT) |
|
1322 && memcmp(rcpt_st2->wf_sum, rcpt_st_new->wf_sum, |
|
1323 sizeof(rcpt_st2->wf_sum))))) { |
|
1324 cwp->rcpts_sws = save_rcpts_sws; |
|
1325 cwp->ask_st |= ASK_ST_LOGIT; |
|
1326 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; |
|
1327 return 0; |
|
1328 } |
|
1329 |
|
1330 /* Stronger filter choices for a preceding recipient are |
|
1331 * potential reasons to reject the message not shared by the |
|
1332 * new recipient and that could force the message to be |
|
1333 * discarded for the preceding recipient */ |
|
1334 if ((rcpt_st2->sws & FLTR_SW_NO_DISCARD) |
|
1335 && ((FLTR_SWS_ON(rcpt_st2->sws) |
|
1336 & ~FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) { |
|
1337 cwp->rcpts_sws = save_rcpts_sws; |
|
1338 cwp->ask_st |= ASK_ST_LOGIT; |
|
1339 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; |
|
1340 return 0; |
|
1341 } |
|
1342 |
|
1343 /* weaker filter choices for a preceding recipient imply |
|
1344 * potential reasons to reject the message not shared by the |
|
1345 * preceding recipient and that could force the message to |
|
1346 * be discarded for the new recipient */ |
|
1347 if ((rcpt_st_new->sws & FLTR_SW_NO_DISCARD) |
|
1348 && ((~FLTR_SWS_ON(rcpt_st2->sws) |
|
1349 & FLTR_SWS_ON(rcpt_st_new->sws)) != 0)) { |
|
1350 cwp->rcpts_sws = save_rcpts_sws; |
|
1351 cwp->ask_st |= ASK_ST_LOGIT; |
|
1352 rcpt_st_new->fgs |= RCPT_FG_INCOMPAT_REJ; |
|
1353 return 0; |
|
1354 } |
|
1355 } while ((rcpt_st2 = rcpt_st2->fwd) != rcpt_st_new); |
|
1356 |
|
1357 return 1; |
|
1358 } |
|
1359 |
|
1360 |
|
1361 |
|
1362 /* check the whitelists for a single user or target */ |
|
1363 static void |
|
1364 ask_white_rcpt(CMN_WORK *cwp, |
|
1365 RCPT_ST *rcpt_st, |
|
1366 RCPT_FGS global_fgs) |
|
1367 { |
|
1368 DCC_WHITE_LISTING listing; |
|
1369 CWF *cwf; |
|
1370 DCC_PATH abs_nm; |
|
1371 |
|
1372 rcpt_st->log_pos_white = log_lseek_get(cwp); |
|
1373 |
|
1374 /* Quit after capturing the log position if the recipient has |
|
1375 * been rejected. |
|
1376 * We cannot do more because dccm does not have the mailer and so |
|
1377 * cannot find the likely userdirs/local/user/whiteclnt file */ |
|
1378 if (rcpt_st->fgs & (RCPT_FG_REJ_FILTER | RCPT_FG_BAD_USERNAME)) |
|
1379 return; |
|
1380 |
|
1381 /* Get the switch settings and the env_to whitelist results. */ |
|
1382 cwf = rcpt_sws_env_to(cwp, rcpt_st); |
|
1383 if (!(cwp->cmn_fgs & CMN_FG_THOLDS_SET)) { |
|
1384 cwp->cmn_fgs |= CMN_FG_THOLDS_SET; |
|
1385 make_tholds(cwp->cks.tholds_rej, cwf); |
|
1386 } |
|
1387 |
|
1388 /* Compute white- or blacklisting. |
|
1389 * The per-user whiteclnt file overrides the global file. |
|
1390 * The whiteclnt files override the MTA with "option MTA-first". |
|
1391 * Without, the MTA controls. |
|
1392 * Within each category, whitelisting overrides blacklisting. |
|
1393 * |
|
1394 * The global whitelist's answer for message's checksums other |
|
1395 * than the env_to checksums have already been computed in |
|
1396 * global_fgs. */ |
|
1397 |
|
1398 if (global_fgs != 0) { |
|
1399 rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); |
|
1400 rcpt_st->fgs |= global_fgs; |
|
1401 } |
|
1402 |
|
1403 if (rcpt_st->sws & FLTR_SW_MTA_FIRST) { |
|
1404 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { |
|
1405 rcpt_st->fgs |= RCPT_FG_WHITE; |
|
1406 rcpt_st->fgs &= ~RCPT_FG_BLACK; |
|
1407 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { |
|
1408 rcpt_st->fgs |= RCPT_FG_BLACK; |
|
1409 rcpt_st->fgs &= ~RCPT_FG_WHITE; |
|
1410 } |
|
1411 } |
|
1412 |
|
1413 /* apply the previously computed env_to global whitelist results |
|
1414 * as well as the other global whitelist results */ |
|
1415 if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_WHITE) { |
|
1416 rcpt_st->fgs &= ~RCPT_FG_BLACK; |
|
1417 rcpt_st->fgs |= RCPT_FG_WHITE; |
|
1418 } else if ((rcpt_st->global_env_to_fgs | global_fgs) & RCPT_FG_BLACK) { |
|
1419 rcpt_st->fgs &= ~RCPT_FG_WHITE; |
|
1420 rcpt_st->fgs |= RCPT_FG_BLACK; |
|
1421 } |
|
1422 |
|
1423 if (!cwf) { |
|
1424 /* Without a per-user whitelist or without any entries in |
|
1425 * the per-user whitelist, we will be using the |
|
1426 * global whitelist for the other messages checksums. |
|
1427 * Arrange to include those results in the per-user log file */ |
|
1428 memcpy(rcpt_st->wtgts, cwp->wtgts, sizeof(rcpt_st->wtgts)); |
|
1429 |
|
1430 } else { |
|
1431 /* Check other message checksums in per-user whitelist */ |
|
1432 white_results(rcpt_st, cwp, &rcpt_st->env_to_fgs, |
|
1433 dcc_white_cks(cwp->emsg, &cwf->wf, &cwp->cks, |
|
1434 rcpt_st->wtgts, &listing), |
|
1435 &listing); |
|
1436 |
|
1437 if (rcpt_st->env_to_fgs == 0) { |
|
1438 /* Without an answer from the per-user whitelist, |
|
1439 * we will be using the global whitelist. |
|
1440 * So arrange to include those results in the per-user |
|
1441 * log file */ |
|
1442 memcpy(rcpt_st->wtgts, cwp->wtgts, |
|
1443 sizeof(rcpt_st->wtgts)); |
|
1444 |
|
1445 } else { |
|
1446 thr_log_print(cwp, 0, "%s%s\n", |
|
1447 fnm2abs_err(abs_nm, cwf->wf.ascii_nm), |
|
1448 (rcpt_st->env_to_fgs & RCPT_FG_WHITE) |
|
1449 ? DCC_XHDR_ISOK |
|
1450 : (rcpt_st->sws & FLTR_SW_TRAP_ACC) |
|
1451 ? "-->"DCC_XHDR_TRAP_ACC |
|
1452 : (rcpt_st->sws & FLTR_SW_TRAP_REJ) |
|
1453 ? "-->"DCC_XHDR_TRAP_REJ |
|
1454 : DCC_XHDR_ISSPAM); |
|
1455 rcpt_st->fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); |
|
1456 rcpt_st->fgs |= rcpt_st->env_to_fgs; |
|
1457 } |
|
1458 |
|
1459 /* release common lock that protected the per-user whitelist |
|
1460 * because we are finished with the per-user whitelist */ |
|
1461 unlock_wf(); |
|
1462 } |
|
1463 |
|
1464 if (rcpt_st->env_to_tgts != 0 |
|
1465 || rcpt_st->user_tgts != 0) |
|
1466 cwp->cmn_fgs |= CMN_FG_LOG_ENV_TO; |
|
1467 |
|
1468 if (!(rcpt_st->sws & FLTR_SW_MTA_FIRST)) { |
|
1469 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { |
|
1470 rcpt_st->fgs |= RCPT_FG_WHITE; |
|
1471 rcpt_st->fgs &= ~RCPT_FG_BLACK; |
|
1472 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { |
|
1473 rcpt_st->fgs |= RCPT_FG_BLACK; |
|
1474 rcpt_st->fgs &= ~RCPT_FG_WHITE; |
|
1475 } |
|
1476 } |
|
1477 } |
|
1478 |
|
1479 |
|
1480 |
|
1481 /* check the whitelists for all targets */ |
|
1482 void |
|
1483 cmn_ask_white(CMN_WORK *cwp) |
|
1484 { |
|
1485 RCPT_ST *rcpt_st; |
|
1486 RCPT_FGS global_fgs; |
|
1487 DCC_OPS grey_op; |
|
1488 DCC_WHITE_LISTING listing; |
|
1489 |
|
1490 /* log sendmail access_db spam */ |
|
1491 if (cwp->ask_st & ASK_ST_MTA_ISSPAM) |
|
1492 cwp->ask_st |= ASK_ST_LOGIT; |
|
1493 |
|
1494 dcc_dnsbl_result(&cwp->ask_st, cwp->cks.dnsbl); |
|
1495 |
|
1496 cwp->log_pos_white_first = log_lseek_get(cwp); |
|
1497 |
|
1498 /* Use the main whitelist only for recipients whose individual |
|
1499 * whitelists don't give a black or white answer. |
|
1500 * Check the main whitelist first (and so even if not necessary) |
|
1501 * so that problems with it are in all of the logs and to simplify |
|
1502 * merging the global and per-user whitelist results. */ |
|
1503 lock_wf(); |
|
1504 global_fgs = 0; |
|
1505 white_results(0, cwp, &global_fgs, |
|
1506 dcc_white_cks(cwp->emsg, &cmn_wf, &cwp->cks, |
|
1507 cwp->wtgts, &listing), |
|
1508 &listing); |
|
1509 |
|
1510 /* get the defaults for the options */ |
|
1511 if (!(cwp->init_sws & FLTR_SW_SET)) |
|
1512 rcpt_sws_def(cwp, 1); |
|
1513 unlock_wf(); |
|
1514 |
|
1515 /* kludge similar to ask_white_rcpt() for no recipients for dccifd with |
|
1516 * the ASCII protocol */ |
|
1517 if ((rcpt_st = cwp->rcpt_st_first) == 0) { |
|
1518 if (cwp->init_sws & FLTR_SW_MTA_FIRST) { |
|
1519 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { |
|
1520 cwp->rcpt_fgs |= RCPT_FG_WHITE; |
|
1521 cwp->rcpt_fgs &= ~RCPT_FG_BLACK; |
|
1522 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { |
|
1523 cwp->rcpt_fgs |= RCPT_FG_BLACK; |
|
1524 cwp->rcpt_fgs &= ~RCPT_FG_WHITE; |
|
1525 } |
|
1526 } |
|
1527 if (global_fgs != 0) { |
|
1528 cwp->rcpt_fgs &= ~(RCPT_FG_WHITE | RCPT_FG_BLACK); |
|
1529 cwp->rcpt_fgs |= global_fgs; |
|
1530 } |
|
1531 if (!(cwp->init_sws & FLTR_SW_MTA_FIRST)) { |
|
1532 if (cwp->ask_st & ASK_ST_MTA_NOTSPAM) { |
|
1533 cwp->rcpt_fgs |= RCPT_FG_WHITE; |
|
1534 cwp->rcpt_fgs &= ~RCPT_FG_BLACK; |
|
1535 } else if (cwp->ask_st & ASK_ST_MTA_ISSPAM) { |
|
1536 cwp->rcpt_fgs |= RCPT_FG_BLACK; |
|
1537 cwp->rcpt_fgs &= ~RCPT_FG_WHITE; |
|
1538 } |
|
1539 } |
|
1540 |
|
1541 rcpt_fgs2ask_st(cwp, cwp->init_sws, cwp->rcpt_fgs); |
|
1542 } |
|
1543 |
|
1544 for (; rcpt_st; rcpt_st = rcpt_st->fwd) { |
|
1545 /* maybe this recipient is whitelisted or a spam trap |
|
1546 * or has per-user option settings */ |
|
1547 ask_white_rcpt(cwp, rcpt_st, global_fgs); |
|
1548 |
|
1549 rcpt_fgs2ask_st(cwp, rcpt_st->sws, rcpt_st->fgs); |
|
1550 |
|
1551 /* no greylist check if it is off or should not be done */ |
|
1552 if ((rcpt_st->sws & (FLTR_SW_GREY_OFF | FLTR_SW_TRAPS)) |
|
1553 || (cwp->cmn_fgs & (CMN_FG_FROM_MX | CMN_FG_FROM_SUBMIT)) |
|
1554 || (rcpt_st->fgs & (RCPT_FG_REJ_FILTER |
|
1555 | RCPT_FG_BAD_USERNAME)) |
|
1556 || (cwp->ask_st & ASK_ST_INVALID_MSG)) |
|
1557 continue; |
|
1558 |
|
1559 if (rcpt_st->fgs & RCPT_FG_BLACK) { |
|
1560 if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP |
|
1561 || (cwp->cks.sums[DCC_CK_ENV_FROM].type |
|
1562 != DCC_CK_ENV_FROM)) |
|
1563 continue; |
|
1564 grey_op = DCC_OP_GREY_QUERY; |
|
1565 } else if (cwp->ask_st & ASK_ST_QUERY_GREY) { |
|
1566 grey_op = DCC_OP_GREY_QUERY; |
|
1567 } else if (rcpt_st->fgs & RCPT_FG_WHITE) { |
|
1568 grey_op = DCC_OP_GREY_WHITE; |
|
1569 } else { |
|
1570 grey_op = DCC_OP_GREY_REPORT; |
|
1571 } |
|
1572 rcpt_st->grey_result = ask_grey(cwp->emsg, cwp->dcc_ctxt, |
|
1573 grey_op, |
|
1574 rcpt_st->msg_sum, |
|
1575 rcpt_st->triple_sum, |
|
1576 &cwp->cks, |
|
1577 rcpt_st->env_to_sum, |
|
1578 &rcpt_st->embargo_num, |
|
1579 &cwp->early_grey_tgts, |
|
1580 &cwp->late_grey_tgts); |
|
1581 |
|
1582 switch (rcpt_st->grey_result) { |
|
1583 case ASK_GREY_OFF: |
|
1584 case ASK_GREY_SPAM: |
|
1585 dcc_logbad(EX_SOFTWARE, |
|
1586 "cmn_ask_white grey_result=%d", |
|
1587 rcpt_st->grey_result); |
|
1588 break; |
|
1589 case ASK_GREY_FAIL: |
|
1590 thr_error_msg(cwp, "%s", cwp->emsg); |
|
1591 /* If we are trying hard, assume the |
|
1592 * message would have been embargoed */ |
|
1593 if (try_extra_hard) |
|
1594 cwp->ask_st |= (ASK_ST_GREY_EMBARGO |
|
1595 | ASK_ST_GREY_LOGIT |
|
1596 | ASK_ST_LOGIT); |
|
1597 break; |
|
1598 case ASK_GREY_EMBARGO: |
|
1599 if (rcpt_st->embargo_num == 1 |
|
1600 && (rcpt_st->fgs & RCPT_FG_BLACK)) { |
|
1601 /* don't bother revoking non-existent entry */ |
|
1602 rcpt_st->grey_result = ASK_GREY_OFF; |
|
1603 } else { |
|
1604 cwp->ask_st |= (ASK_ST_GREY_EMBARGO |
|
1605 | ASK_ST_GREY_LOGIT |
|
1606 | ASK_ST_LOGIT); |
|
1607 if (cwp->max_embargo_num < rcpt_st->embargo_num) |
|
1608 cwp->max_embargo_num = rcpt_st->embargo_num; |
|
1609 } |
|
1610 break; |
|
1611 case ASK_GREY_EMBARGO_END: |
|
1612 cwp->ask_st |= (ASK_ST_GREY_LOGIT | ASK_ST_LOGIT); |
|
1613 cwp->rcpt_fgs |= RCPT_FG_GREY_END; |
|
1614 break; |
|
1615 case ASK_GREY_PASS: |
|
1616 break; |
|
1617 case ASK_GREY_WHITE: |
|
1618 rcpt_st->fgs |= RCPT_FG_GREY_WHITE; |
|
1619 break; |
|
1620 } |
|
1621 } |
|
1622 |
|
1623 cwp->log_pos_white_last = log_lseek_get(cwp); |
|
1624 cwp->log_pos_ask_error = cwp->log_pos_white_last; |
|
1625 } |
|
1626 |
|
1627 |
|
1628 |
|
1629 /* ask a DCC server */ |
|
1630 int /* <0=big problem, 0=retryable, 1=ok */ |
|
1631 cmn_ask_dcc(CMN_WORK *cwp) |
|
1632 { |
|
1633 int i; |
|
1634 |
|
1635 /* Talk to the DCC server and make the X-DCC header. |
|
1636 * If we have blacklist entries for it, then we'll tell the DCC |
|
1637 * server it is spam and say so in the X-DCC header. |
|
1638 * Note that a target count of 0 is a query. */ |
|
1639 if (cwp->ask_st & ASK_ST_QUERY) { |
|
1640 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; |
|
1641 cwp->local_tgts = 0; |
|
1642 } else if (cwp->ask_st & ASK_ST_CLNT_ISSPAM) { |
|
1643 cwp->cmn_fgs |= CMN_FG_LOCAL_SPAM; |
|
1644 cwp->local_tgts = cwp->tgts + cwp->mta_rej_tgts; |
|
1645 } else if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { |
|
1646 /* if the message is under a greylist embargo, |
|
1647 * then report to the DCC only the targets for |
|
1648 * which it is an initial transmission or embargo #1 */ |
|
1649 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; |
|
1650 cwp->local_tgts = cwp->early_grey_tgts + cwp->mta_rej_tgts; |
|
1651 } else { |
|
1652 /* if this is the end of a greylist embargo |
|
1653 * then do not tell the DCC about targets that were |
|
1654 * counted with previous transmissions. Those targets |
|
1655 * are counted in late_grey_tgts this time, but were |
|
1656 * counted in early_grey_tgts for previous transmissions */ |
|
1657 cwp->cmn_fgs &= ~CMN_FG_LOCAL_SPAM; |
|
1658 cwp->local_tgts = (cwp->tgts - cwp->late_grey_tgts |
|
1659 + cwp->mta_rej_tgts); |
|
1660 } |
|
1661 |
|
1662 /* talk to the DCC server */ |
|
1663 i = ask_dcc(cwp->emsg, cwp->dcc_ctxt, try_extra_hard, |
|
1664 &cwp->header, &cwp->cks, &cwp->ask_st, |
|
1665 (cwp->cmn_fgs & CMN_FG_LOCAL_SPAM) != 0, |
|
1666 cwp->local_tgts); |
|
1667 if (i <= 0) { |
|
1668 cwp->log_pos_ask_error += thr_error_msg(cwp, "%s", cwp->emsg); |
|
1669 return i; |
|
1670 } |
|
1671 |
|
1672 /* if we are talking to a new server, |
|
1673 * remember to fix the X-DCC headers of the other contexts */ |
|
1674 if (cwp->xhdr_fname_len != cwp->header.start_len |
|
1675 || strncmp(cwp->header.buf, cwp->xhdr_fname, cwp->xhdr_fname_len)) { |
|
1676 if (dcc_clnt_debug > 1) |
|
1677 thr_trace_msg(cwp, DCC_XHDR_START |
|
1678 "header changed from %s to %.*s", |
|
1679 cwp->xhdr_fname, |
|
1680 (int)cwp->header.start_len, |
|
1681 cwp->header.buf); |
|
1682 cwp->xhdr_fname_len = get_xhdr_fname(cwp->xhdr_fname, |
|
1683 sizeof(cwp->xhdr_fname), |
|
1684 dcc_clnt_info); |
|
1685 if (++dcc_ctxt_sn == 0) |
|
1686 dcc_ctxt_sn = 1; |
|
1687 cwp->dcc_ctxt_sn = dcc_ctxt_sn; |
|
1688 } |
|
1689 |
|
1690 return 1; |
|
1691 } |
|
1692 |
|
1693 |
|
1694 #define USER_LOG_CAPTION(rcpt_st, s) user_log_write((rcpt_st), (s), LITZ(s)) |
|
1695 #define USER_LOG_EOL(rcpt_st) USER_LOG_CAPTION((rcpt_st), "\n") |
|
1696 |
|
1697 static u_char |
|
1698 user_log_write(RCPT_ST *rcpt_st, const void *buf, u_int len) |
|
1699 { |
|
1700 DCC_PATH abs_nm; |
|
1701 int result; |
|
1702 |
|
1703 if (user_log_fd < 0) |
|
1704 return 0; |
|
1705 |
|
1706 if (!len) |
|
1707 len = strlen(buf); |
|
1708 result = write(user_log_fd, buf, len); |
|
1709 if (result == (int)len) |
|
1710 return 1; |
|
1711 |
|
1712 if (result < 0) |
|
1713 thr_error_msg(rcpt_st->cwp, "write(%s): %s", |
|
1714 fnm2abs_err(abs_nm, rcpt_st->user_log_nm), |
|
1715 ERROR_STR()); |
|
1716 else |
|
1717 thr_error_msg(rcpt_st->cwp, |
|
1718 "write(%s)=%d instead of %d", |
|
1719 fnm2abs_err(abs_nm, rcpt_st->user_log_nm), |
|
1720 result, (int)len); |
|
1721 dcc_log_close(0, rcpt_st->user_log_nm, user_log_fd, |
|
1722 &rcpt_st->cwp->ldate); |
|
1723 user_log_fd = -1; |
|
1724 return 0; |
|
1725 } |
|
1726 |
|
1727 |
|
1728 |
|
1729 static void PATTRIB(2,3) |
|
1730 user_log_print(RCPT_ST *rcpt_st, const char *p, ...) |
|
1731 { |
|
1732 char logbuf[LOGBUF_SIZE*2]; |
|
1733 int i; |
|
1734 va_list args; |
|
1735 |
|
1736 if (user_log_fd < 0) |
|
1737 return; |
|
1738 |
|
1739 va_start(args, p); |
|
1740 i = vsnprintf(logbuf, sizeof(logbuf), p, args); |
|
1741 va_end(args); |
|
1742 if (i < ISZ(logbuf)) { |
|
1743 user_log_write(rcpt_st, logbuf, i); |
|
1744 return; |
|
1745 } |
|
1746 user_log_write(rcpt_st, logbuf, sizeof(logbuf)); |
|
1747 user_log_write(rcpt_st, "...", 3); |
|
1748 } |
|
1749 |
|
1750 |
|
1751 |
|
1752 static void |
|
1753 user_log_block(RCPT_ST *rcpt_st, /* copy from main log file to this */ |
|
1754 off_t start, /* starting here */ |
|
1755 off_t stop) /* and ending here */ |
|
1756 { |
|
1757 CMN_WORK *cwp; |
|
1758 char buf[4096]; |
|
1759 int len; |
|
1760 int result; |
|
1761 |
|
1762 if (user_log_fd < 0) |
|
1763 return; |
|
1764 |
|
1765 if (start == stop) |
|
1766 return; |
|
1767 |
|
1768 cwp = rcpt_st->cwp; |
|
1769 |
|
1770 if (start == -1 || stop == -1 || start > stop) { |
|
1771 thr_error_msg(cwp, "bogus user_log_block position %d to %d", |
|
1772 (int)start, (int)stop); |
|
1773 return; |
|
1774 } |
|
1775 |
|
1776 if (!log_lseek_set(cwp, start)) |
|
1777 return; |
|
1778 |
|
1779 while ((len = stop - start) != 0) { |
|
1780 if (len > ISZ(buf)) |
|
1781 len = ISZ(buf); |
|
1782 result = read(log_fd2, buf, len); |
|
1783 if (result != len) { |
|
1784 if (result < 0) |
|
1785 thr_error_msg(cwp, |
|
1786 "user_log_block read(%s,%d): %s", |
|
1787 cwp->log_nm, len, ERROR_STR()); |
|
1788 else |
|
1789 thr_error_msg(cwp, "user_log_block" |
|
1790 " read(%s,%d)=%d", |
|
1791 cwp->log_nm, len, result); |
|
1792 log_fd2_close(-2); |
|
1793 return; |
|
1794 } |
|
1795 if (!user_log_write(rcpt_st, buf, len)) |
|
1796 return; |
|
1797 start += len; |
|
1798 } |
|
1799 } |
|
1800 |
|
1801 |
|
1802 /* print |
|
1803 * env_To: user@example.com ok |
|
1804 * user@example.com: f7a48ff4 70d29d39 4ed1e36f 104e4fa0 |
|
1805 * 86e0517b b455b130 4cfccc8c f1a1ff37 Pass |
|
1806 */ |
|
1807 static void |
|
1808 print_addr_sum(LOG_WRITE_FNC out, void *arg, |
|
1809 const char *addr, |
|
1810 int addr_len, /* trim trailing '>' from grey addr */ |
|
1811 const char *sum, |
|
1812 int sum_len, /* trim trailing '>' from env_To */ |
|
1813 const char *tgts, |
|
1814 int tgts_width) /* to right-justify env_To tgts */ |
|
1815 { |
|
1816 char buf[100]; |
|
1817 int i; |
|
1818 |
|
1819 i = snprintf(buf, sizeof(buf), PRINT_CK_PAT_LIM_CK" %*s\n", |
|
1820 addr_len, addr, |
|
1821 addr_len > 0 ? ':' : ' ', |
|
1822 sum_len, sum, |
|
1823 tgts_width, tgts); |
|
1824 if (i >= ISZ(buf)) { |
|
1825 i = sizeof(buf); |
|
1826 buf[i-1] = '\n'; |
|
1827 } |
|
1828 out(arg, buf, i); |
|
1829 } |
|
1830 |
|
1831 |
|
1832 |
|
1833 static void |
|
1834 print_env_to(LOG_WRITE_FNC out, void *arg, const RCPT_ST *rcpt_st) |
|
1835 { |
|
1836 const char *addr; |
|
1837 int addr_len; |
|
1838 char tgts_buf[16]; |
|
1839 |
|
1840 if (rcpt_st->env_to_tgts != 0) { |
|
1841 addr = rcpt_st->env_to; |
|
1842 addr_len = strlen(addr); |
|
1843 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { |
|
1844 ++addr; |
|
1845 addr_len -= 2; |
|
1846 } |
|
1847 print_addr_sum(out, arg, |
|
1848 DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO), |
|
1849 addr, addr_len, |
|
1850 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), |
|
1851 rcpt_st->env_to_tgts, 0), |
|
1852 PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN); |
|
1853 } |
|
1854 |
|
1855 if (rcpt_st->user_tgts != 0) { |
|
1856 addr = rcpt_st->user; |
|
1857 addr_len = strlen(addr); |
|
1858 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { |
|
1859 ++addr; |
|
1860 addr_len -= 2; |
|
1861 } |
|
1862 print_addr_sum(out, arg, |
|
1863 DCC_XHDR_TYPE_ENV_TO, LITZ(DCC_XHDR_TYPE_ENV_TO), |
|
1864 addr, addr_len, |
|
1865 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), |
|
1866 rcpt_st->user_tgts, 0), |
|
1867 PRINT_CK_PAT_SRVR_LEN+PRINT_CK_PAT_WLIST_LEN); |
|
1868 } |
|
1869 } |
|
1870 |
|
1871 |
|
1872 |
|
1873 static void |
|
1874 print_grey(LOG_WRITE_FNC out, void *arg, |
|
1875 const RCPT_ST *rcpt_st, u_char *headed) |
|
1876 { |
|
1877 #define CK_HEADING " "DCC_XHDR_GREY_RECIP"\n" |
|
1878 #define CK_HEADING_QUERY " "DCC_XHDR_GREY_RECIP" query\n" |
|
1879 char cbuf[DCC_CK2STR_LEN]; |
|
1880 char embargo_buf[20]; |
|
1881 const char *addr; |
|
1882 int addr_len; |
|
1883 const char *embargo; |
|
1884 |
|
1885 embargo = 0; |
|
1886 switch (rcpt_st->grey_result) { |
|
1887 case ASK_GREY_FAIL: |
|
1888 embargo = DCC_XHDR_EMBARGO_FAIL; |
|
1889 break; |
|
1890 case ASK_GREY_OFF: |
|
1891 return; |
|
1892 case ASK_GREY_EMBARGO: |
|
1893 if (rcpt_st->embargo_num > 0) { |
|
1894 snprintf(embargo_buf, sizeof(embargo_buf), |
|
1895 DCC_XHDR_EMBARGO_NUM, rcpt_st->embargo_num); |
|
1896 embargo = embargo_buf; |
|
1897 } else { |
|
1898 embargo = DCC_XHDR_EMBARGO; |
|
1899 } |
|
1900 break; |
|
1901 case ASK_GREY_EMBARGO_END: |
|
1902 embargo = DCC_XHDR_EMBARGO_ENDED; |
|
1903 break; |
|
1904 case ASK_GREY_PASS: |
|
1905 embargo = DCC_XHDR_EMBARGO_PASS; |
|
1906 break; |
|
1907 case ASK_GREY_WHITE: |
|
1908 embargo = DCC_XHDR_EMBARGO_OK; |
|
1909 break; |
|
1910 case ASK_GREY_SPAM: |
|
1911 snprintf(embargo_buf, sizeof(embargo_buf), |
|
1912 DCC_XHDR_EMBARGO_RESET, rcpt_st->embargo_num); |
|
1913 embargo = embargo_buf; |
|
1914 break; |
|
1915 } |
|
1916 |
|
1917 if (!headed || !*headed) { |
|
1918 if (headed) |
|
1919 *headed = 1; |
|
1920 if (rcpt_st->cwp->ask_st & ASK_ST_QUERY) |
|
1921 out(arg, CK_HEADING_QUERY, LITZ(CK_HEADING_QUERY)); |
|
1922 else |
|
1923 out(arg, CK_HEADING, LITZ(CK_HEADING)); |
|
1924 } |
|
1925 |
|
1926 addr = rcpt_st->env_to; |
|
1927 addr_len = strlen(addr); |
|
1928 if (addr_len > 1 && addr[0] == '<' && addr[addr_len-1] == '>') { |
|
1929 ++addr; |
|
1930 addr_len -= 2; |
|
1931 } |
|
1932 print_addr_sum(out, arg, addr, addr_len, |
|
1933 dcc_ck2str(cbuf, sizeof(cbuf), |
|
1934 DCC_CK_GREY_MSG, rcpt_st->msg_sum, 0), |
|
1935 PRINT_CK_SUM_LEN, |
|
1936 "", 0); |
|
1937 print_addr_sum(out, arg, "", 0, |
|
1938 dcc_ck2str(cbuf, sizeof(cbuf), |
|
1939 DCC_CK_GREY3, rcpt_st->triple_sum, 0), |
|
1940 PRINT_CK_SUM_LEN, |
|
1941 embargo, 0); |
|
1942 |
|
1943 if (!headed) |
|
1944 out(arg, "\n", 1); |
|
1945 |
|
1946 #undef CK_HEADING |
|
1947 } |
|
1948 |
|
1949 |
|
1950 |
|
1951 /* log external header, X-DCC header, and DCC results */ |
|
1952 static void |
|
1953 log_isspam(LOG_WRITE_FNC fnc, void *cp, CMN_WORK *cwp, |
|
1954 u_char log_type, /* 0="" 1="per-user" 2="global" */ |
|
1955 FLTR_SWS sws, RCPT_FGS rcpt_fgs) |
|
1956 { |
|
1957 ASK_ST ask_st; |
|
1958 |
|
1959 ask_st = cwp->ask_st; |
|
1960 if (rcpt_fgs & RCPT_FG_WLIST_NOTSPAM) |
|
1961 ask_st |= ASK_ST_WLIST_NOTSPAM; |
|
1962 if (rcpt_fgs & RCPT_FG_WLIST_ISSPAM) |
|
1963 ask_st |= ASK_ST_WLIST_ISSPAM; |
|
1964 log_ask_st(fnc, cp, ask_st, sws, log_type, &cwp->header); |
|
1965 } |
|
1966 |
|
1967 |
|
1968 |
|
1969 static void |
|
1970 user_log_msg(CMN_WORK *cwp, RCPT_ST *rcpt_st) |
|
1971 { |
|
1972 DCC_PATH rcpt_logdir; |
|
1973 const char *old_log; |
|
1974 |
|
1975 /* since the user wants a log file, make one for the system */ |
|
1976 cwp->ask_st |= ASK_ST_LOGIT; |
|
1977 |
|
1978 /* we need the main log file to create per-user log files */ |
|
1979 if (rcpt_st->dir[0] == '\0' |
|
1980 || dcc_main_logdir[0] == '\0') |
|
1981 return; |
|
1982 |
|
1983 if (!log2_start(cwp)) |
|
1984 return; |
|
1985 |
|
1986 /* create the user's log file */ |
|
1987 snprintf(rcpt_logdir, sizeof(rcpt_logdir), "%s/log", rcpt_st->dir); |
|
1988 /* try to use the same name as the main log file */ |
|
1989 old_log = rindex(cwp->log_nm, '.'); |
|
1990 if (old_log) { |
|
1991 if (strlen(++old_log) != DCC_MKSTEMP_LEN) |
|
1992 old_log = 0; |
|
1993 } |
|
1994 user_log_fd = dcc_log_open(cwp->emsg, rcpt_st->user_log_nm, |
|
1995 cwp->id, sizeof(cwp->id), old_log, |
|
1996 rcpt_logdir, DCC_FIN_LOG_PREFIX, |
|
1997 (rcpt_st->sws & FLTR_SW_LOG_M) |
|
1998 ? LOG_MODE_MINUTE |
|
1999 : (rcpt_st->sws & FLTR_SW_LOG_H) |
|
2000 ? LOG_MODE_HOUR |
|
2001 : (rcpt_st->sws & FLTR_SW_LOG_D) |
|
2002 ? LOG_MODE_DAY |
|
2003 : LOG_MODE_FLAT); |
|
2004 if (user_log_fd < 0) { |
|
2005 if (user_log_fd == -1) |
|
2006 thr_error_msg(cwp, "%s", cwp->emsg); |
|
2007 return; |
|
2008 } |
|
2009 |
|
2010 user_log_block(rcpt_st, /* copy envelope before env_To line */ |
|
2011 0, cwp->log_pos_to_first); |
|
2012 user_log_block(rcpt_st, /* copy this env_To line */ |
|
2013 rcpt_st->log_pos_to, |
|
2014 rcpt_st->fwd |
|
2015 ? rcpt_st->fwd->log_pos_to |
|
2016 : cwp->log_pos_to_end); |
|
2017 user_log_block(rcpt_st, /* copy the body of the message */ |
|
2018 cwp->log_pos_to_end, |
|
2019 cwp->log_pos_white_first); |
|
2020 user_log_block(rcpt_st, /* copy whitelist error messages */ |
|
2021 rcpt_st->log_pos_white, |
|
2022 rcpt_st->fwd |
|
2023 ? rcpt_st->fwd->log_pos_white |
|
2024 : cwp->log_pos_white_last); |
|
2025 user_log_block(rcpt_st, /* copy DCC error messages */ |
|
2026 cwp->log_pos_white_last, |
|
2027 cwp->log_pos_ask_error); |
|
2028 |
|
2029 /* log external header, X-DCC header, and DCC results */ |
|
2030 log_isspam((LOG_WRITE_FNC)user_log_write, rcpt_st, cwp, 1, |
|
2031 rcpt_st->sws, rcpt_st->fgs); |
|
2032 |
|
2033 /* log the checksums and their counts */ |
|
2034 dcc_print_cks((LOG_WRITE_FNC)user_log_write, rcpt_st, |
|
2035 cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts, |
|
2036 &cwp->cks, rcpt_st->wtgts, |
|
2037 (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0); |
|
2038 print_env_to((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st); |
|
2039 USER_LOG_EOL(rcpt_st); |
|
2040 print_grey((LOG_WRITE_FNC)user_log_write, rcpt_st, rcpt_st, 0); |
|
2041 } |
|
2042 |
|
2043 |
|
2044 |
|
2045 void |
|
2046 log_smtp_reply(CMN_WORK *cwp) |
|
2047 { |
|
2048 thr_log_print(cwp, 1, DCC_XHDR_REJ_DATA_MSG); |
|
2049 log_write(cwp, cwp->reply.rcode, 0); |
|
2050 LOG_CMN_CAPTION(cwp, " "); |
|
2051 log_write(cwp, cwp->reply.xcode, 0); |
|
2052 LOG_CMN_CAPTION(cwp, " "); |
|
2053 log_write(cwp, cwp->reply.str, 0); |
|
2054 LOG_CMN_EOL(cwp); |
|
2055 } |
|
2056 |
|
2057 |
|
2058 |
|
2059 static void |
|
2060 user_log_smtp_reply(CMN_WORK *cwp, RCPT_ST *rcpt_st) |
|
2061 { |
|
2062 user_log_print(rcpt_st, DCC_XHDR_REJ_DATA_MSG"%s %s %s\n", |
|
2063 cwp->reply.rcode, cwp->reply.xcode, cwp->reply.str); |
|
2064 } |
|
2065 |
|
2066 |
|
2067 |
|
2068 /* tell the grey list to restore the embargo on a triple */ |
|
2069 static void |
|
2070 grey_spam(CMN_WORK *cwp, RCPT_ST *rcpt_st) |
|
2071 { |
|
2072 DCC_GREY_SPAM gs; |
|
2073 DCC_OP_RESP resp; |
|
2074 |
|
2075 switch (rcpt_st->grey_result) { |
|
2076 case ASK_GREY_FAIL: |
|
2077 case ASK_GREY_OFF: |
|
2078 case ASK_GREY_WHITE: |
|
2079 return; |
|
2080 case ASK_GREY_EMBARGO: |
|
2081 if (rcpt_st->embargo_num == 0) { |
|
2082 rcpt_st->grey_result = ASK_GREY_OFF; |
|
2083 return; |
|
2084 } |
|
2085 break; |
|
2086 case ASK_GREY_EMBARGO_END: |
|
2087 case ASK_GREY_PASS: |
|
2088 break; |
|
2089 case ASK_GREY_SPAM: |
|
2090 dcc_logbad(EX_SOFTWARE, "cmn_ask_white grey_result=%d", |
|
2091 rcpt_st->grey_result); |
|
2092 return; |
|
2093 } |
|
2094 |
|
2095 memset(&gs, 0, sizeof(gs)); |
|
2096 |
|
2097 if (cwp->cks.sums[DCC_CK_IP].type != DCC_CK_IP) { |
|
2098 thr_error_msg(cwp, "missing IP checksum for dcc_grey_spam()"); |
|
2099 return; |
|
2100 } |
|
2101 |
|
2102 gs.ip.type = DCC_CK_IP; |
|
2103 gs.ip.len = sizeof(gs.ip); |
|
2104 memcpy(&gs.ip.sum, &cwp->cks.sums[DCC_CK_IP].sum, sizeof(DCC_SUM)); |
|
2105 |
|
2106 gs.triple.type = DCC_CK_GREY3; |
|
2107 gs.triple.len = sizeof(gs.triple); |
|
2108 memcpy(&gs.triple.sum, rcpt_st->triple_sum, sizeof(DCC_SUM)); |
|
2109 |
|
2110 gs.msg.type = DCC_CK_GREY_MSG; |
|
2111 gs.msg.len = sizeof(gs.msg); |
|
2112 memcpy(&gs.msg.sum, rcpt_st->msg_sum, sizeof(DCC_SUM)); |
|
2113 |
|
2114 if (!dcc_clnt_op(cwp->emsg, cwp->dcc_ctxt, DCC_CLNT_FG_GREY, |
|
2115 0, 0, 0, &gs.hdr, sizeof(gs), |
|
2116 DCC_OP_GREY_SPAM, &resp, sizeof(resp))) { |
|
2117 thr_error_msg(cwp, "%s", cwp->emsg); |
|
2118 } |
|
2119 |
|
2120 rcpt_st->grey_result = ASK_GREY_SPAM; |
|
2121 } |
|
2122 |
|
2123 |
|
2124 |
|
2125 /* process the message for each user to decide what to do with it */ |
|
2126 void |
|
2127 users_process(CMN_WORK *cwp) |
|
2128 { |
|
2129 RCPT_ST *rcpt_st; |
|
2130 u_char need_eol; |
|
2131 int trap_acc_tgts; |
|
2132 DNSBL_GBITS dnsbl_sws, dnsbl_hits, common_dnsbl_hits; |
|
2133 int dnsbl_delay_tgts; |
|
2134 u_char dnsbl_timeo; |
|
2135 const DNSBL_GROUP *blg; |
|
2136 const REPLY_TPLT *reply; |
|
2137 int i; |
|
2138 |
|
2139 /* log the DCC results and headers in the common log file */ |
|
2140 log_isspam((LOG_WRITE_FNC)log_write, cwp, cwp, 2, |
|
2141 cwp->rcpts_sws, cwp->rcpt_fgs); |
|
2142 |
|
2143 /* log the checksums, DCC server counts and global whitelist values */ |
|
2144 dcc_print_cks((LOG_WRITE_FNC)log_write, cwp, |
|
2145 cwp->cmn_fgs & CMN_FG_LOCAL_SPAM, cwp->local_tgts, |
|
2146 &cwp->cks, cwp->wtgts, |
|
2147 (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) != 0); |
|
2148 if (cwp->cmn_fgs & CMN_FG_LOG_ENV_TO) { |
|
2149 for (rcpt_st = cwp->rcpt_st_first; |
|
2150 rcpt_st; |
|
2151 rcpt_st = rcpt_st->fwd) { |
|
2152 print_env_to((LOG_WRITE_FNC)log_write, cwp, rcpt_st); |
|
2153 } |
|
2154 } |
|
2155 LOG_CMN_EOL(cwp); |
|
2156 |
|
2157 /* mark recipients who won't receive it */ |
|
2158 need_eol = 0; |
|
2159 trap_acc_tgts = 0; |
|
2160 common_dnsbl_hits = -1; |
|
2161 dnsbl_delay_tgts = 0; |
|
2162 for (rcpt_st = cwp->rcpt_st_first; |
|
2163 rcpt_st; |
|
2164 rcpt_st = rcpt_st->fwd) { |
|
2165 /* Ignore recipients whose RCPT_TO commands were rejected */ |
|
2166 if (rcpt_st->fgs & RCPT_FG_REJ_FILTER) |
|
2167 continue; |
|
2168 |
|
2169 /* We cannot decide whether the message might be spam |
|
2170 * for targets rejected by the MTA because dccm does not |
|
2171 * have the mailer and so cannot find the likely |
|
2172 * userdirs/local/user/whiteclnt file */ |
|
2173 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) |
|
2174 continue; |
|
2175 |
|
2176 if (rcpt_st->fgs & RCPT_FG_WHITE) { |
|
2177 common_dnsbl_hits = 0; |
|
2178 dnsbl_timeo = 0; |
|
2179 } else { |
|
2180 /* decide whether it is spam for this target */ |
|
2181 if ((rcpt_st->fgs & RCPT_FG_BLACK) |
|
2182 || ((cwp->ask_st & ASK_ST_SRVR_ISSPAM) |
|
2183 && !(rcpt_st->sws & FLTR_SW_DCC_OFF))) |
|
2184 rcpt_st->fgs |= RCPT_FG_ISSPAM; |
|
2185 |
|
2186 dnsbl_sws = FLTR_SW_DNSBL_BITS(rcpt_st->sws); |
|
2187 dnsbl_hits = (dnsbl_sws |
|
2188 & ASK_ST_DNSBL_HIT_BITS(cwp->ask_st)); |
|
2189 common_dnsbl_hits &= dnsbl_hits; |
|
2190 if (dnsbl_hits != 0) { |
|
2191 dnsbl_timeo = 0; |
|
2192 rcpt_st->fgs |= RCPT_FG_ISSPAM; |
|
2193 } else if (0 != (ASK_ST_DNSBL_TFAIL_BITS(cwp->ask_st) |
|
2194 & dnsbl_sws)) { |
|
2195 dnsbl_timeo = 1; |
|
2196 } else { |
|
2197 dnsbl_timeo = 0; |
|
2198 } |
|
2199 } |
|
2200 |
|
2201 if (rcpt_st->fgs & RCPT_FG_ISSPAM) { |
|
2202 if (rcpt_st->sws & FLTR_SW_TRAP_ACC) |
|
2203 ++trap_acc_tgts; |
|
2204 else |
|
2205 ++cwp->reject_tgts; |
|
2206 } else if (dnsbl_timeo) { |
|
2207 ++dnsbl_delay_tgts; |
|
2208 } else { |
|
2209 ++cwp->deliver_tgts; |
|
2210 } |
|
2211 |
|
2212 /* Tell greylist to restore the embargo for targets that believe |
|
2213 * the message was spam and did not white- or blacklist it */ |
|
2214 if ((rcpt_st->fgs & RCPT_FG_ISSPAM) |
|
2215 && !(rcpt_st->sws & FLTR_SW_TRAPS)) |
|
2216 grey_spam(cwp, rcpt_st); |
|
2217 |
|
2218 print_grey((LOG_WRITE_FNC)log_write, cwp, rcpt_st, &need_eol); |
|
2219 } |
|
2220 if (need_eol) |
|
2221 LOG_CMN_EOL(cwp); |
|
2222 |
|
2223 /* If real targets or rejection-traps need to reject the message, |
|
2224 * then treat any accept-traps as reject-traps to avoid telling the |
|
2225 * SMTP client that the message was accepted. */ |
|
2226 if (cwp->reject_tgts != 0 && cwp->deliver_tgts == 0) { |
|
2227 cwp->reject_tgts += trap_acc_tgts; |
|
2228 } else { |
|
2229 cwp->deliver_tgts += trap_acc_tgts; |
|
2230 } |
|
2231 |
|
2232 /* temp-fail ambiguous mail if DNSBL checks timed out */ |
|
2233 if (dnsbl_delay_tgts != 0) { |
|
2234 if (cwp->deliver_tgts != 0) { |
|
2235 /* It is not ambiguous if it must be delivered to |
|
2236 * at least one recipient. */ |
|
2237 cwp->deliver_tgts += dnsbl_delay_tgts; |
|
2238 dnsbl_delay_tgts = 0; |
|
2239 } else { |
|
2240 cwp->reject_tgts += dnsbl_delay_tgts; |
|
2241 } |
|
2242 } |
|
2243 |
|
2244 if (cwp->reject_tgts != 0 && cwp->deliver_tgts != 0) { |
|
2245 /* It is spam for some targets and not for others. |
|
2246 * If we cannot discard, then reject it for all targets */ |
|
2247 if ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT) |
|
2248 || cannot_discard) { |
|
2249 thr_log_print(cwp, 0, |
|
2250 "rejection forced for %d targets\n", |
|
2251 cwp->deliver_tgts); |
|
2252 cwp->reject_tgts += cwp->deliver_tgts; |
|
2253 cwp->deliver_tgts = 0; |
|
2254 } else { |
|
2255 thr_log_print(cwp, 0, |
|
2256 "discard forced for %d targets\n", |
|
2257 cwp->reject_tgts); |
|
2258 } |
|
2259 } |
|
2260 |
|
2261 /* do not embargo the message if no target wants it */ |
|
2262 if (cwp->deliver_tgts == 0) |
|
2263 cwp->ask_st &= ~ASK_ST_GREY_EMBARGO; |
|
2264 |
|
2265 if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { |
|
2266 make_reply(&cwp->reply, &grey_reply, cwp, 0); |
|
2267 return; |
|
2268 } |
|
2269 |
|
2270 /* finished if it is not spam or we won't do anything about it */ |
|
2271 if (cwp->reject_tgts == 0 || cwp->action == CMN_IGNORE) |
|
2272 return; |
|
2273 |
|
2274 /* make an SMTP rejection message unless we have already have one */ |
|
2275 if (cwp->reply.log_result) |
|
2276 return; |
|
2277 |
|
2278 /* if possible, use a DNSBL rejection message that applies |
|
2279 * to all recipients */ |
|
2280 if (common_dnsbl_hits != 0) { |
|
2281 for (i = 0; i < MAX_DNSBL_GROUPS; ++i) { |
|
2282 if ((common_dnsbl_hits & DNSBL_G2B(i)) != 0 |
|
2283 && 0 != (blg = &cwp->cks.dnsbl->groups[i]) |
|
2284 && 0 != (reply = blg->dnsbl->reply)) { |
|
2285 make_reply(&cwp->reply, reply, cwp, blg); |
|
2286 return; |
|
2287 } |
|
2288 } |
|
2289 } |
|
2290 |
|
2291 if (dnsbl_delay_tgts != 0) { |
|
2292 make_reply(&cwp->reply, &dnsbl_timeo_reply, cwp, 0); |
|
2293 return; |
|
2294 } |
|
2295 |
|
2296 /* use the generic DCC rejection message */ |
|
2297 make_reply(&cwp->reply, &reject_reply, cwp, 0); |
|
2298 } |
|
2299 |
|
2300 |
|
2301 |
|
2302 /* after having checked each user or recipient, |
|
2303 * dispose of the message for each */ |
|
2304 static void |
|
2305 user_log_result(CMN_WORK *cwp, RCPT_ST *rcpt_st, const char *result) |
|
2306 { |
|
2307 /* create the per-user log file */ |
|
2308 if ((rcpt_st->fgs & RCPT_FG_ISSPAM) |
|
2309 || ((cwp->ask_st & ASK_ST_GREY_LOGIT) |
|
2310 && !(rcpt_st->sws & FLTR_SW_GREY_LOG_OFF)) |
|
2311 || (rcpt_st->sws & FLTR_SW_LOG_ALL)) |
|
2312 user_log_msg(cwp, rcpt_st); |
|
2313 |
|
2314 if (cwp->ask_st & ASK_ST_INVALID_MSG) { |
|
2315 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", result); |
|
2316 return; |
|
2317 } |
|
2318 |
|
2319 if (rcpt_st->rej_msg[0] != '\0') { |
|
2320 /* rejection result for this recipient in the main log */ |
|
2321 thr_log_print(cwp, 0, |
|
2322 DCC_XHDR_RESULT_REJECT" %s: %s\n", |
|
2323 rcpt_st->env_to, rcpt_st->rej_result); |
|
2324 /* per-user log file */ |
|
2325 if (!(rcpt_st->fgs & RCPT_FG_INCOMPAT_REJ) |
|
2326 || (rcpt_st->sws & FLTR_SW_LOG_ALL)) { |
|
2327 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_REJ_RCPT_MSG); |
|
2328 user_log_write(rcpt_st, rcpt_st->rej_msg, 0); |
|
2329 if (rcpt_st->rej_msg[0] == '4') |
|
2330 USER_LOG_CAPTION(rcpt_st, |
|
2331 "\n"DCC_XHDR_RESULT |
|
2332 DCC_XHDR_RESULT_REJECT |
|
2333 " temporarily\n"); |
|
2334 else |
|
2335 USER_LOG_CAPTION(rcpt_st, |
|
2336 "\n"DCC_XHDR_RESULT |
|
2337 DCC_XHDR_RESULT_REJECT); |
|
2338 } |
|
2339 return; |
|
2340 } |
|
2341 |
|
2342 if (cwp->ask_st & ASK_ST_GREY_EMBARGO) { |
|
2343 user_log_smtp_reply(cwp, rcpt_st); |
|
2344 if (rcpt_st->embargo_num != 0) { |
|
2345 user_log_print(rcpt_st, |
|
2346 DCC_XHDR_RESULT"%s #%d\n", |
|
2347 cwp->reply.log_result, |
|
2348 rcpt_st->embargo_num); |
|
2349 } else { |
|
2350 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", |
|
2351 cwp->reply.log_result); |
|
2352 } |
|
2353 return; |
|
2354 } |
|
2355 |
|
2356 if (!(rcpt_st->fgs & RCPT_FG_ISSPAM)) { |
|
2357 /* It is not spam for this target. |
|
2358 * |
|
2359 * If it was rejected late, e.g. by the SMTP server for dccifd |
|
2360 * in proxy, mode, then log the rejection message */ |
|
2361 if (result) { |
|
2362 if (cwp->reply.str && cwp->reply.str[0] != '\0') |
|
2363 user_log_print(rcpt_st, |
|
2364 DCC_XHDR_REJ_DATA_MSG"%s\n", |
|
2365 cwp->reply.str); |
|
2366 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", |
|
2367 result); |
|
2368 return; |
|
2369 } |
|
2370 |
|
2371 /* If it was spam for some other target and we cannot |
|
2372 * discard for that target, then log the rejection */ |
|
2373 if (cwp->reject_tgts != 0 |
|
2374 && ((cwp->cmn_fgs & CMN_FG_FROM_SUBMIT) |
|
2375 || cannot_discard)) { |
|
2376 user_log_smtp_reply(cwp, rcpt_st); |
|
2377 USER_LOG_CAPTION(rcpt_st, |
|
2378 DCC_XHDR_RESULT |
|
2379 DCC_XHDR_RESULT_REJECT |
|
2380 DCC_XHDR_RESULT_FORCED"\n"); |
|
2381 return; |
|
2382 } |
|
2383 |
|
2384 if (cwp->ask_st & (ASK_ST_CLNT_ISSPAM |
|
2385 | ASK_ST_SRVR_ISSPAM |
|
2386 | ASK_ST_REP_ISSPAM)) { |
|
2387 if (cwp->rcpt_fgs & RCPT_FG_GREY_END) |
|
2388 USER_LOG_CAPTION(rcpt_st, |
|
2389 DCC_XHDR_RESULT |
|
2390 DCC_XHDR_RESULT_I_A |
|
2391 " "DCC_XHDR_RESULT_A_GREY"\n"); |
|
2392 else |
|
2393 USER_LOG_CAPTION(rcpt_st, |
|
2394 DCC_XHDR_RESULT |
|
2395 DCC_XHDR_RESULT_I_A"\n"); |
|
2396 return; |
|
2397 } |
|
2398 if (cwp->rcpt_fgs & RCPT_FG_GREY_END) { |
|
2399 USER_LOG_CAPTION(rcpt_st, |
|
2400 DCC_XHDR_RESULT |
|
2401 DCC_XHDR_RESULT_ACCEPT |
|
2402 " "DCC_XHDR_RESULT_A_GREY"\n"); |
|
2403 return; |
|
2404 } |
|
2405 if (rcpt_st->fgs & RCPT_FG_GREY_WHITE) { |
|
2406 USER_LOG_CAPTION(rcpt_st, |
|
2407 DCC_XHDR_RESULT |
|
2408 DCC_XHDR_RESULT_ACCEPT |
|
2409 "; greylist whitelist\n"); |
|
2410 return; |
|
2411 } |
|
2412 USER_LOG_CAPTION(rcpt_st, |
|
2413 DCC_XHDR_RESULT DCC_XHDR_RESULT_ACCEPT"\n"); |
|
2414 return; |
|
2415 } |
|
2416 |
|
2417 /* It was spam for this target |
|
2418 * |
|
2419 * If some other target wanted the message, then we should discard it |
|
2420 * for this target. Dccifd in proxy mode cannot discard for an |
|
2421 * individual target, and so must avoid the possibility by |
|
2422 * recipients that might have differing choices with cannot_discard. */ |
|
2423 if (cwp->deliver_tgts != 0) { |
|
2424 /* Prefer to discard for spam traps, but just accept |
|
2425 * spam when we cannot discard it for dccifd */ |
|
2426 if (rcpt_st->sws & FLTR_SW_TRAPS) { |
|
2427 if (!cannot_discard) { |
|
2428 user_reject_discard(cwp, rcpt_st); |
|
2429 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT |
|
2430 DCC_XHDR_RESULT_DISCARD"\n"); |
|
2431 } else { |
|
2432 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT |
|
2433 DCC_XHDR_RESULT_ACCEPT"\n"); |
|
2434 } |
|
2435 |
|
2436 } else { |
|
2437 --cwp->reject_tgts; |
|
2438 ++totals.tgts_discarded; |
|
2439 user_reject_discard(cwp, rcpt_st); |
|
2440 if (cwp->action == CMN_DISCARD) { |
|
2441 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT |
|
2442 DCC_XHDR_RESULT_DISCARD"\n"); |
|
2443 } else { |
|
2444 thr_log_print(cwp, 0, |
|
2445 DCC_XHDR_RESULT |
|
2446 DCC_XHDR_RESULT_DISCARD |
|
2447 " forced for %s\n", |
|
2448 rcpt_st->env_to); |
|
2449 USER_LOG_CAPTION(rcpt_st, |
|
2450 DCC_XHDR_RESULT |
|
2451 DCC_XHDR_RESULT_DISCARD |
|
2452 DCC_XHDR_RESULT_FORCED"\n"); |
|
2453 } |
|
2454 } |
|
2455 return; |
|
2456 } |
|
2457 |
|
2458 /* spam for all targets including this one */ |
|
2459 |
|
2460 if (cwp->action == CMN_DISCARD) { |
|
2461 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT |
|
2462 DCC_XHDR_RESULT_DISCARD"\n"); |
|
2463 return; |
|
2464 } |
|
2465 |
|
2466 if (cwp->action == CMN_IGNORE) { |
|
2467 USER_LOG_CAPTION(rcpt_st, DCC_XHDR_RESULT |
|
2468 DCC_XHDR_RESULT_I_A"\n"); |
|
2469 return; |
|
2470 } |
|
2471 |
|
2472 user_log_smtp_reply(cwp, rcpt_st); |
|
2473 if (rcpt_st->sws & FLTR_SW_TRAP_ACC) { |
|
2474 user_log_print(rcpt_st, DCC_XHDR_RESULT |
|
2475 "%s"DCC_XHDR_RESULT_FORCED"\n", |
|
2476 cwp->reply.log_result); |
|
2477 } else { |
|
2478 user_log_print(rcpt_st, DCC_XHDR_RESULT"%s\n", |
|
2479 cwp->reply.log_result); |
|
2480 } |
|
2481 } |
|
2482 |
|
2483 |
|
2484 |
|
2485 /* log the consensus in each target's log file */ |
|
2486 void |
|
2487 users_log_result(CMN_WORK *cwp, |
|
2488 const char *result) /* 0 or reject by dccifd's server */ |
|
2489 { |
|
2490 RCPT_ST *rcpt_st; |
|
2491 int error; |
|
2492 |
|
2493 error = pthread_mutex_lock(&user_log_mutex); |
|
2494 if (error) |
|
2495 dcc_logbad(EX_SOFTWARE, "pthread_mutex_lock(user_log): %s", |
|
2496 ERROR_STR1(error)); |
|
2497 user_log_owner = pthread_self(); |
|
2498 |
|
2499 /* create individual log files and trim target list */ |
|
2500 for (rcpt_st = cwp->rcpt_st_first; rcpt_st; rcpt_st = rcpt_st->fwd) { |
|
2501 if (rcpt_st->fgs & RCPT_FG_BAD_USERNAME) |
|
2502 continue; |
|
2503 if ((cwp->ask_st & ASK_ST_INVALID_MSG) |
|
2504 && !(rcpt_st->sws & FLTR_SW_LOG_ALL)) |
|
2505 continue; |
|
2506 |
|
2507 user_log_result(cwp, rcpt_st, result); |
|
2508 |
|
2509 if (user_log_fd >= 0) { |
|
2510 dcc_log_close(0, rcpt_st->user_log_nm, |
|
2511 user_log_fd, &cwp->ldate); |
|
2512 user_log_fd = -1; |
|
2513 } |
|
2514 } |
|
2515 |
|
2516 log_fd2_close(-1); |
|
2517 |
|
2518 user_log_owner = 0; |
|
2519 error = pthread_mutex_unlock(&user_log_mutex); |
|
2520 if (error) |
|
2521 dcc_logbad(EX_SOFTWARE, "pthread_mutex_unlock(user_log): %s", |
|
2522 ERROR_STR1(error)); |
|
2523 } |