comparison dcclib/ask.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * ask about a batch of checksums
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.146 $Revision$
40 */
41
42 #include "dcc_ck.h"
43 #include "dcc_heap_debug.h"
44 #include "dcc_xhdr.h"
45
46 static DCC_CKSUM_THOLDS dcc_tholds_log;
47 DCC_CKSUM_THOLDS dcc_tholds_rej;
48 static u_char dcc_honor_nospam[DCC_DIM_CKS];
49
50 static u_char trim_grey_ip_addr; /* remove IP address from grey triple */
51 static struct in6_addr grey_ip_mask;
52
53 static void honor_cnt(const DCC_GOT_CKS *cks, u_int *, DCC_CK_TYPES, DCC_TGTS);
54
55
56
57 #ifdef DCC_PKT_VERSION5
58 /* figure old server's target count before our latest report */
59 static DCC_TGTS /* return corrected current count */
60 save_p_tgts(DCC_GOT_SUM *g, /* put previous count in g->tgts */
61 DCC_OPS op,
62 const DCC_TGTS local_tgts, /* real local target count */
63 const DCC_TGTS gross_tgts, /* local count adjusted by blacklist */
64 DCC_TGTS c_tgts) /* what the old DCC server said */
65 {
66 DCC_CK_TYPES type = g->type;
67
68 if (op == DCC_OP_QUERY) {
69 /* if we switched servers and converted a report
70 * to a query, then guess the total that the
71 * server would have produced for a report
72 * instead of the query we sent.
73 *
74 * Assume the server is not running with -K.
75 * If the server's current value is 0 for a body checksum
76 * then assume the report we sent to the other server has not
77 * been flooded.
78 * Assume other checksums will always be zero/unknown. */
79 if (DB_GLOBAL_NOKEEP(0, type))
80 return 0;
81
82 /* Assume the current value is really the previous value
83 * because flooding has not happened */
84 g->tgts = c_tgts;
85
86 if (c_tgts < DCC_TGTS_TOO_MANY
87 && DCC_CK_IS_BODY(type)) {
88 c_tgts += local_tgts;
89 if (c_tgts > DCC_TGTS_TOO_MANY)
90 c_tgts = DCC_TGTS_TOO_MANY;
91 }
92 return c_tgts;
93
94 } else if (c_tgts >= gross_tgts
95 && gross_tgts < DCC_TGTS_TOO_MANY) {
96 /* if possible infer server's value before our report */
97 if (c_tgts >= DCC_TGTS_TOO_MANY)
98 g->tgts = c_tgts;
99 else
100 g->tgts = c_tgts - gross_tgts;
101 }
102
103 return c_tgts;
104 }
105
106
107
108 #endif /* DCC_PKT_VERSION5 */
109 int /* 1=ok, 0=no answer, -1=fatal */
110 ask_dcc(DCC_EMSG emsg,
111 DCC_CLNT_CTXT *ctxt,
112 DCC_CLNT_FGS clnt_fgs, /* DCC_CLNT_FG_* */
113 DCC_HEADER_BUF *hdr, /* put results here */
114 DCC_GOT_CKS *cks, /* and here */
115 ASK_ST *ask_stp, /* and here */
116 u_char spam, /* spam==0 && local_tgts==0 --> query */
117 DCC_TGTS local_tgts) /* report these targets to DCC server */
118 {
119 union {
120 DCC_HDR hdr;
121 DCC_REPORT r;
122 } rpt;
123 DCC_OP_RESP resp;
124 DCC_OPS op;
125 DCC_CK *ck;
126 DCC_GOT_SUM *g;
127 DCC_TGTS gross_tgts;
128 DCC_TGTS c_tgts; /* server's current, total count */
129 DCC_CKS_WTGTS hdr_tgts; /* values for X-DCC header */
130 DCC_CK_TYPES type;
131 DCC_SRVR_ID srvr_id;
132 int pkt_len, recv_len, exp_len;
133 int num_cks, ck_num, result;
134
135 memset(hdr_tgts, 0, sizeof(hdr_tgts));
136
137 /* prepare a report for the nearest DCC server */
138 if (local_tgts == 0 && !spam) {
139 /* because of greylisting, we can have a target count of 0
140 * but need to report spam discovered by a DNSBL */
141 op = DCC_OP_QUERY;
142 gross_tgts = 0;
143 rpt.r.tgts = 0;
144 } else {
145 op = DCC_OP_REPORT;
146 if (local_tgts == DCC_TGTS_TOO_MANY
147 || local_tgts == 0) {
148 spam = 1;
149 local_tgts = 1;
150 }
151 if (spam) {
152 *ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
153 gross_tgts = DCC_TGTS_TOO_MANY;
154 rpt.r.tgts = htonl(local_tgts | DCC_TGTS_SPAM);
155 } else {
156 gross_tgts = local_tgts;
157 rpt.r.tgts = htonl(local_tgts);
158 }
159 }
160
161 ck = rpt.r.cks;
162 num_cks = 0;
163 for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
164 /* never tell the DCC server about some headers */
165 if (!g->rpt2srvr)
166 continue;
167 ck->len = sizeof(*ck);
168 ck->type = g->type;
169 memcpy(ck->sum, g->sum, sizeof(ck->sum));
170 ++ck;
171 ++num_cks;
172 }
173 if (num_cks == 0) {
174 /* pretend we always have at least a basic body checksum
175 * guess the DCC would have answered 0 */
176 xhdr_init(hdr, 0);
177 xhdr_add_ck(hdr, DCC_CK_BODY, gross_tgts);
178 honor_cnt(cks, ask_stp, DCC_CK_BODY, local_tgts);
179 return 1;
180 }
181
182 /* send the report and see what the DCC has to say */
183 pkt_len = (sizeof(rpt.r) - sizeof(rpt.r.cks)
184 + num_cks * sizeof(rpt.r.cks[0]));
185 result = dcc_clnt_op(emsg, ctxt, clnt_fgs, 0, &srvr_id, 0,
186 &rpt.hdr, pkt_len, op, &resp, sizeof(resp));
187
188 /* try a query to different server if the first failed
189 * but a second was found */
190 if (!result && srvr_id != DCC_ID_INVALID) {
191 if (dcc_clnt_debug) {
192 if (emsg && *emsg != '\0') {
193 dcc_trace_msg("retry with different server"
194 " after: %s", emsg);
195 *emsg = '\0';
196 } else {
197 dcc_trace_msg("retry with different server");
198 }
199 }
200 op = DCC_OP_QUERY;
201 result = dcc_clnt_op(emsg, ctxt, clnt_fgs | DCC_CLNT_FG_RETRY,
202 0, &srvr_id, 0,
203 &rpt.hdr, pkt_len,
204 op, &resp, sizeof(resp));
205 }
206 if (!result) {
207 *ask_stp |= ASK_ST_LOGIT;
208 } else {
209 /* forget about it if the DCC server responded too strangely */
210 recv_len = ntohs(resp.hdr.len);
211 #ifdef DCC_PKT_VERSION5
212 if (resp.hdr.pkt_vers <= DCC_PKT_VERSION5)
213 exp_len = (sizeof(resp.ans5) - sizeof(resp.ans5.b)
214 + num_cks*sizeof(DCC_TGTS));
215 else
216 #endif
217 exp_len = (sizeof(resp.ans) - sizeof(resp.ans.b)
218 + num_cks*sizeof(resp.ans.b[0]));
219 if (recv_len != exp_len) {
220 dcc_pemsg(EX_UNAVAILABLE, emsg,
221 "DCC %s: answered with %d instead of %d bytes",
222 dcc_srvr_nm(0), recv_len, exp_len);
223 *ask_stp |= ASK_ST_LOGIT;
224 result = -1;
225 }
226 }
227
228 /* check the server's response to see if we have spam */
229 ck_num = 0;
230 for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
231 if (!g->rpt2srvr) {
232 /* pretend we always have a basic body checksum */
233 if (g == &cks->sums[DCC_CK_BODY])
234 honor_cnt(cks, ask_stp,
235 DCC_CK_BODY, local_tgts);
236 continue;
237 }
238 type = g->type; /* g->type is valid only if rpt2srvr */
239
240 if (result <= 0) {
241 c_tgts = (DCC_CK_IS_BODY(type)) ? gross_tgts : 0;
242
243 #ifdef DCC_PKT_VERSION5
244 } else if (resp.hdr.pkt_vers <= DCC_PKT_VERSION5) {
245 c_tgts = save_p_tgts(g, op,
246 local_tgts, gross_tgts,
247 ntohl(resp.ans5.b[ck_num]));
248 } else {
249 #endif /* DCC_PKT_VERSION5 */
250 /* server's total before our report */
251 g->tgts = ntohl(resp.ans.b[ck_num].p);
252 /* new total */
253 c_tgts = ntohl(resp.ans.b[ck_num].c);
254 #ifdef DCC_PKT_VERSION5
255 }
256 #endif
257 ++ck_num;
258
259 hdr_tgts[type] = c_tgts;
260
261 /* notice DCC server's whitelist */
262 if (dcc_honor_nospam[type]) {
263 if (c_tgts == DCC_TGTS_OK) {
264 *ask_stp |= ASK_ST_SRVR_NOTSPAM;
265
266 } else if (c_tgts == DCC_TGTS_OK2) {
267 /* if server says it is half ok,
268 * look for two halves */
269 if (*ask_stp & ASK_ST_SRVR_OK2) {
270 *ask_stp |= ASK_ST_SRVR_NOTSPAM;
271 } else {
272 *ask_stp |= ASK_ST_SRVR_OK2;
273 }
274 }
275 }
276
277 honor_cnt(cks, ask_stp, type, c_tgts);
278 }
279
280 /* honor server whitelist */
281 if (*ask_stp & ASK_ST_SRVR_NOTSPAM)
282 *ask_stp &= ~ASK_ST_SRVR_ISSPAM;
283
284 /* generate the header line now that we have checked all of
285 * the counts against their thresholds and so know if we
286 * must add "bulk". Add the header even if checking is turned off
287 * and we won't reject affected messages. Say "many" for DNSBL
288 * or local blacklist spam even without an answer from the DCC server
289 * so that SpamAssassin gets the message. */
290 xhdr_init(hdr, srvr_id);
291 if (*ask_stp & ASK_ST_SRVR_ISSPAM) {
292 xhdr_add_str(hdr, DCC_XHDR_BULK);
293 } else if (*ask_stp & ASK_ST_CLNT_ISSPAM) {
294 xhdr_add_str(hdr, DCC_XHDR_BULK);
295 hdr_tgts[DCC_CK_BODY] = DCC_TGTS_TOO_MANY;
296 } else if (*ask_stp & ASK_ST_REP_ISSPAM) {
297 xhdr_add_str(hdr, DCC_XHDR_BULK_REP);
298 hdr_tgts[DCC_CK_BODY] = DCC_TGTS_TOO_MANY;
299 }
300
301 for (g = cks->sums; g <= &cks->sums[DCC_CK_TYPE_LAST]; ++g) {
302 if (!g->rpt2srvr) {
303 /* pretend we always have a body checksum */
304 if (g == &cks->sums[DCC_CK_BODY])
305 xhdr_add_ck(hdr, DCC_CK_BODY,
306 hdr_tgts[DCC_CK_BODY]);
307 continue;
308 }
309 /* Add interesing counts to the header.
310 * Body checksums are always interestig if we have them.
311 * Pretend we always have a basic body checksum. */
312 type = g->type;
313 if (DCC_CK_IS_BODY(type)) {
314 xhdr_add_ck(hdr, type, hdr_tgts[type]);
315 continue;
316 }
317 if (hdr_tgts[type] != 0)
318 xhdr_add_ck(hdr, type, hdr_tgts[type]);
319 }
320
321 return result;
322 }
323
324
325
326 /* check message's checksums in whiteclnt for dccproc or dccsight */
327 u_char /* 1=ok 0=something to complain about */
328 unthr_ask_white(DCC_EMSG emsg,
329 ASK_ST *ask_stp,
330 FLTR_SWS *swsp,
331 const char *white_nm,
332 DCC_GOT_CKS *cks,
333 DCC_CKS_WTGTS wtgts)
334 {
335 DCC_WHITE_LISTING listing;
336 int retval;
337
338 /* assume DNSBLs are on unless turned off, because there is no reason
339 * to use `dccproc -B` if you don't want to use them */
340 *swsp |= FLTR_SW_DNSBL_M;
341
342 /* fake whiteclnt if not specified */
343 if (!white_nm) {
344 dcc_merge_tholds(cks->tholds_rej, dcc_tholds_rej, 0);
345 return 1;
346 }
347
348 /* don't filter if something is wrong with the file */
349 if (!dcc_new_white_nm(emsg, &cmn_wf, white_nm)) {
350 *ask_stp |= ASK_ST_WLIST_NOTSPAM | ASK_ST_LOGIT;
351 return 0;
352 }
353
354 /* let whiteclnt file turn off the DCC and other filters */
355 *swsp = wf2sws(*swsp, &cmn_wf);
356
357 /* combine the command-line thresholds with the thresholds from
358 * from the common /var/dcc/whiteclnt file */
359 dcc_merge_tholds(cks->tholds_rej, dcc_tholds_rej, cmn_wf.wtbl);
360
361 retval = 1;
362 switch (dcc_white_cks(emsg, &cmn_wf, cks, wtgts, &listing)) {
363 case DCC_WHITE_OK:
364 case DCC_WHITE_NOFILE:
365 break;
366 case DCC_WHITE_SILENT:
367 *ask_stp |= ASK_ST_LOGIT;
368 break;
369 case DCC_WHITE_COMPLAIN:
370 case DCC_WHITE_CONTINUE:
371 retval = 0;
372 *ask_stp |= ASK_ST_LOGIT;
373 break;
374 }
375
376 switch (listing) {
377 case DCC_WHITE_LISTED:
378 /* do not send whitelisted checksums to DCC server */
379 *ask_stp |= ASK_ST_WLIST_NOTSPAM;
380 break;
381 case DCC_WHITE_USE_DCC:
382 case DCC_WHITE_UNLISTED:
383 if (*swsp & FLTR_SW_TRAPS)
384 *ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_WLIST_ISSPAM
385 | ASK_ST_LOGIT);
386 break;
387 case DCC_WHITE_BLACK:
388 *ask_stp |= (ASK_ST_WLIST_ISSPAM
389 | ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
390 break;
391 }
392
393 if (*swsp & FLTR_SW_LOG_ALL)
394 *ask_stp |= ASK_ST_LOGIT;
395
396 return retval;
397 }
398
399
400
401 /* ask the DCC for dccproc or dccsight but not dccifd or dccm */
402 u_char /* 1=ok 0=something to complain about */
403 unthr_ask_dcc(DCC_EMSG emsg,
404 DCC_CLNT_CTXT *ctxt,
405 DCC_HEADER_BUF *hdr, /* put header here */
406 ASK_ST *ask_stp, /* put state bites here */
407 DCC_GOT_CKS *cks, /* these checksums */
408 u_char spam, /* spam==0 && local_tgts==0 --> query */
409 DCC_TGTS local_tgts) /* number of addressees */
410 {
411 if (*ask_stp & ASK_ST_WLIST_NOTSPAM) {
412 if (spam) {
413 /* if dccproc says it is spam, then it is, even if
414 * the whiteclnt file says we cannot report it */
415 *ask_stp |= (ASK_ST_CLNT_ISSPAM | ASK_ST_LOGIT);
416 xhdr_init(hdr, 0);
417 xhdr_add_ck(hdr, DCC_CK_BODY, DCC_TGTS_TOO_MANY);
418 } else {
419 xhdr_whitelist(hdr);
420 }
421 /* honor log threshold for whitelisted messages */
422 dcc_honor_log_cnts(ask_stp, cks, local_tgts);
423 return 1;
424
425 } else {
426 /* if allowed by whitelisting, report our checksums to the DCC
427 * and return with that result including setting logging */
428 return (0 < ask_dcc(emsg, ctxt, DCC_CLNT_FG_NONE,
429 hdr, cks, ask_stp, spam,
430 local_tgts));
431 }
432 }
433
434
435
436 /* parse -g for dccm and dccproc */
437 void
438 dcc_parse_honor(const char *arg0)
439 {
440 const char *arg;
441 DCC_CK_TYPES type, t2;
442 int i;
443
444 arg = arg0;
445 if (!CLITCMP(arg, "not_") || !CLITCMP(arg, "not-")) {
446 arg += LITZ("not_");
447 i = 0;
448 } else if (!CLITCMP(arg, "no_") || !CLITCMP(arg, "no-")) {
449 arg += LITZ("no_");
450 i = 0;
451 } else {
452 i = 1;
453 }
454
455 /* allow -g for ordinary checksums but not reputations or greylisting */
456 type = dcc_str2type_thold(arg, -1);
457 if (type == DCC_CK_INVALID) {
458 dcc_error_msg("unrecognized checksum type in \"-g %s\"",
459 arg0);
460 return;
461 }
462 for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
463 if (t2 == type
464 || (type == SET_ALL_THOLDS && IS_ALL_CKSUM(t2))
465 || (type == SET_CMN_THOLDS && IS_CMN_CKSUM(t2)))
466 dcc_honor_nospam[t2] = i;
467 }
468 }
469
470
471
472 void
473 dcc_clear_tholds(void)
474 {
475 DCC_CK_TYPES type;
476
477 memset(dcc_honor_nospam, 0, sizeof(dcc_honor_nospam));
478 dcc_honor_nospam[DCC_CK_IP] = 1;
479 dcc_honor_nospam[DCC_CK_ENV_FROM] = 1;
480 dcc_honor_nospam[DCC_CK_FROM] = 1;
481
482 for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
483 dcc_tholds_log[type] = DCC_THOLD_UNSET;
484 dcc_tholds_rej[type] = DCC_THOLD_UNSET;
485 }
486 }
487
488
489
490 u_char /* 1=merged from whiteclnt wtbl */
491 dcc_merge_tholds(DCC_CKSUM_THOLDS out,
492 const DCC_CKSUM_THOLDS in,
493 const DCC_WHITE_TBL *wtbl)
494 {
495 DCC_CK_TYPES type;
496 DCC_TGTS tgts;
497 u_char result;
498
499 if (in != out)
500 memcpy(out, in, sizeof(DCC_CKSUM_THOLDS));
501 if (!wtbl)
502 return 0;
503
504 result = 0;
505 for (type = DCC_CK_TYPE_FIRST; type <= DCC_CK_TYPE_LAST; ++type) {
506 tgts = wtbl->hdr.tholds_rej[type];
507 if (tgts != DCC_THOLD_UNSET) {
508 out[type] = tgts;
509 result = 1;
510 }
511 }
512 return result;
513 }
514
515
516
517 /* parse type,[log-thold,]rej-thold */
518 u_char /* 1=need a log directory */
519 dcc_parse_tholds(const char *f, /* "-c " or "-t " */
520 const char *arg) /* optarg */
521 {
522 DCC_CK_TYPES type;
523 DCC_TGTS log_tgts, rej_tgts;
524 char *thold_rej, *thold_log;
525 u_char log_tgts_set, rej_tgts_set;
526
527 thold_log = strchr(arg, ',');
528 if (!thold_log) {
529 dcc_error_msg("missing comma in \"%s%s\"", f, arg);
530 return 0;
531 }
532 type = dcc_str2type_thold(arg, thold_log-arg);
533 if (type == DCC_CK_INVALID) {
534 dcc_error_msg("unrecognized checksum type in \"%s%s\"", f, arg);
535 return 0;
536 }
537
538 thold_log = dcc_strdup(++thold_log);
539
540 /* if there is only one threshold, take it as the spam threshold */
541 thold_rej = strchr(thold_log, ',');
542 if (!thold_rej) {
543 thold_rej = thold_log;
544 thold_log = 0;
545 } else {
546 *thold_rej++ = '\0';
547 }
548
549 log_tgts_set = log_tgts = 0;
550 if (thold_log && *thold_log != '\0') {
551 log_tgts = dcc_str2thold(type, thold_log);
552 if (log_tgts == DCC_TGTS_INVALID)
553 dcc_error_msg("unrecognized logging threshold"
554 " \"%s\" in \"%s%s\"",
555 thold_log, f, arg);
556 else
557 log_tgts_set = 1;
558 }
559
560
561 rej_tgts_set = rej_tgts = 0;
562 if (!thold_rej || *thold_rej == '\0') {
563 if (!thold_log || *thold_log == '\0')
564 dcc_error_msg("no thresholds in \"%s%s\"", f, arg);
565 } else {
566 rej_tgts = dcc_str2thold(type, thold_rej);
567 if (rej_tgts == DCC_TGTS_INVALID)
568 dcc_error_msg("unrecognized rejection threshold"
569 " \"%s\" in \"%s%s\"",
570 thold_rej, f, arg);
571 else
572 rej_tgts_set = 1;
573 }
574
575
576 if (log_tgts_set || rej_tgts_set) {
577 DCC_CK_TYPES t2;
578
579 for (t2 = DCC_CK_TYPE_FIRST; t2 <= DCC_CK_TYPE_LAST; ++t2) {
580 if (t2 == type
581 || (type == SET_ALL_THOLDS && IS_ALL_CKSUM(t2))
582 || (type == SET_CMN_THOLDS && IS_CMN_CKSUM(t2))) {
583 if (log_tgts_set)
584 dcc_tholds_log[t2] = log_tgts;
585 if (rej_tgts_set)
586 dcc_tholds_rej[t2] = rej_tgts;
587 }
588 }
589 }
590
591 dcc_free(thold_log);
592 return log_tgts_set;
593 }
594
595
596
597 static void
598 honor_cnt(const DCC_GOT_CKS *cks,
599 ASK_ST *ask_stp, /* previous flag bits */
600 DCC_CK_TYPES type, /* which kind of checksum */
601 DCC_TGTS type_tgts) /* total count for the checksum */
602 {
603 if (type >= DIM(dcc_honor_nospam))
604 return;
605
606 /* reject and log spam */
607 if (cks->tholds_rej[type] <= DCC_TGTS_TOO_MANY
608 && cks->tholds_rej[type] <= type_tgts
609 && type_tgts <= DCC_TGTS_TOO_MANY) {
610 *ask_stp |= (ASK_ST_SRVR_ISSPAM | ASK_ST_LOGIT);
611 return;
612 }
613
614 /* log messages that are bulkier than the log threshold */
615 if (dcc_tholds_log[type] <= DCC_TGTS_TOO_MANY
616 && dcc_tholds_log[type] <= type_tgts)
617 *ask_stp |= ASK_ST_LOGIT;
618 }
619
620
621
622 /* honor log threshold for local counts and white-/blacklists */
623 void
624 dcc_honor_log_cnts(ASK_ST *ask_stp, /* previous flag bits */
625 const DCC_GOT_CKS *cks, /* these server counts */
626 DCC_TGTS tgts)
627 {
628 const DCC_GOT_SUM *g;
629 DCC_CK_TYPES type;
630
631 if (*ask_stp & ASK_ST_LOGIT)
632 return;
633
634 if (tgts == DCC_TGTS_TOO_MANY) {
635 *ask_stp |= ASK_ST_LOGIT;
636 return;
637 }
638
639 /* pretend we always have a body checksum for the log threshold */
640 if (dcc_tholds_log[DCC_CK_BODY] <= DCC_TGTS_TOO_MANY
641 && dcc_tholds_log[DCC_CK_BODY] <= tgts) {
642 *ask_stp |= ASK_ST_LOGIT;
643 return;
644 }
645
646 for (g = cks->sums; g <= LAST(cks->sums); ++g) {
647 type = g->type;
648 if (type == DCC_CK_INVALID
649 || type == DCC_CK_ENV_TO)
650 continue;
651 if (dcc_tholds_log[type] > DCC_TGTS_TOO_MANY)
652 continue;
653 if (dcc_tholds_log[type] <= tgts) {
654 *ask_stp |= ASK_ST_LOGIT;
655 return;
656 }
657 }
658 }
659
660
661
662 /* compute switch settings from bits in a whiteclnt file */
663 FLTR_SWS
664 wf2sws(FLTR_SWS sws, const DCC_WF *wf)
665 {
666 static time_t complained;
667 time_t now;
668 DCC_PATH abs_nm;
669 int i;
670
671 if (!grey_on
672 && (wf->wtbl_flags & (DCC_WHITE_FG_GREY_ON
673 | DCC_WHITE_FG_GREY_LOG_ON))
674 && (now = time(0)) > complained+24*60*60) {
675 complained = now;
676 dcc_error_msg("%s wants greylisting"
677 " but it is turned off",
678 fnm2abs_err(abs_nm, wf->ascii_nm));
679 }
680
681 /* compute switch values from whiteclnt bits */
682
683 if (wf->wtbl_flags & DCC_WHITE_FG_NO_DISCARD)
684 sws |= FLTR_SW_NO_DISCARD;
685 else if (wf->wtbl_flags & DCC_WHITE_FG_DISCARD_OK)
686 sws &= ~FLTR_SW_NO_DISCARD;
687
688 if ((wf->wtbl_flags & DCC_WHITE_FG_DCC_OFF))
689 sws |= FLTR_SW_DCC_OFF;
690 else if (wf->wtbl_flags & DCC_WHITE_FG_DCC_ON)
691 sws &= ~FLTR_SW_DCC_OFF;
692
693 if (grey_on && (wf->wtbl_flags & DCC_WHITE_FG_GREY_ON)) {
694 sws &= ~FLTR_SW_GREY_OFF;
695 } else if (!grey_on || (wf->wtbl_flags & DCC_WHITE_FG_GREY_OFF)) {
696 sws |= FLTR_SW_GREY_OFF;
697 }
698
699 if (wf->wtbl_flags & DCC_WHITE_FG_LOG_ALL) {
700 sws |= FLTR_SW_LOG_ALL;
701 } else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_NORMAL) {
702 sws &= ~FLTR_SW_LOG_ALL;
703 }
704
705 if (wf->wtbl_flags & DCC_WHITE_FG_GREY_LOG_ON) {
706 sws &= ~FLTR_SW_GREY_LOG_OFF;
707 } else if (wf->wtbl_flags & DCC_WHITE_FG_GREY_LOG_OFF) {
708 sws |= FLTR_SW_GREY_LOG_OFF;
709 }
710
711 if (wf->wtbl_flags & DCC_WHITE_FG_LOG_M) {
712 sws |= FLTR_SW_LOG_M;
713 } else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_H) {
714 sws |= FLTR_SW_LOG_H;
715 } else if (wf->wtbl_flags & DCC_WHITE_FG_LOG_D) {
716 sws |= FLTR_SW_LOG_D;
717 }
718
719 if (wf->wtbl_flags & DCC_WHITE_FG_MTA_FIRST) {
720 sws |= FLTR_SW_MTA_FIRST;
721 } else if (wf->wtbl_flags & DCC_WHITE_FG_MTA_LAST) {
722 sws &= ~FLTR_SW_MTA_FIRST;
723 }
724
725 for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
726 if ((wf->wtbl_flags & DCC_WHITE_FG_DNSBL_ON(i))
727 && dnsbls) {
728 sws |= FLTR_SW_DNSBL(i);
729 } else if (wf->wtbl_flags & DCC_WHITE_FG_DNSBL_OFF(i)) {
730 sws &= ~FLTR_SW_DNSBL(i);
731 }
732 }
733
734 if (wf->wtbl_flags & DCC_WHITE_FG_TRAP_ACC) {
735 sws |= FLTR_SW_TRAP_ACC;
736 } else if (wf->wtbl_flags & DCC_WHITE_FG_TRAP_REJ) {
737 sws |= FLTR_SW_TRAP_REJ;
738 }
739
740 return sws | FLTR_SW_SET;
741 }
742
743
744
745 #define LOG_ASK_ST_BLEN 160
746 #define LOG_ASK_ST_OFF "(off)"
747 #define LOG_ASK_ST_OVF " ...\n\n"
748 static int
749 log_ask_st_sub(char *buf, int blen,
750 const char *s, int slen,
751 u_char off)
752 {
753 int dlen, tlen;
754
755 /* quit if no room at all in the log */
756 if (blen >= LOG_ASK_ST_BLEN)
757 return LOG_ASK_ST_BLEN;
758
759 /* quit if nothing to say */
760 if (!s || !slen)
761 return blen;
762
763 dlen = LOG_ASK_ST_BLEN - blen;
764 tlen = LITZ(LOG_ASK_ST_OVF)+2+slen;
765 if (off) /* notice if we need to say "(off)" */
766 tlen += LITZ(LOG_ASK_ST_OFF);
767 if (dlen <= tlen) {
768 /* show truncation of the message with "..." */
769 memcpy(&buf[blen], LOG_ASK_ST_OVF, LITZ(LOG_ASK_ST_OVF));
770 blen += LITZ(LOG_ASK_ST_OVF);
771 if (blen < LOG_ASK_ST_BLEN)
772 memset(&buf[blen], ' ', LOG_ASK_ST_BLEN-blen);
773 return LOG_ASK_ST_BLEN;
774 }
775
776 if (blen > 0 && buf[blen-1] != '\n') {
777 buf[blen++] = ' ';
778 buf[blen++] = ' ';
779 }
780 memcpy(buf+blen, s, slen);
781 blen += slen;
782 if (off) {
783 memcpy(buf+blen, LOG_ASK_ST_OFF, LITZ(LOG_ASK_ST_OFF));
784 blen += LITZ(LOG_ASK_ST_OFF);
785 }
786 return blen;
787 }
788
789
790
791 /* generate log file line of results */
792 void
793 log_ask_st(LOG_WRITE_FNC fnc, void *cp, ASK_ST ask_st, FLTR_SWS sws,
794 u_char log_type, /* 0="" 1="per-user" 2="global" */
795 const DCC_HEADER_BUF *hdr)
796 {
797 char buf[LOG_ASK_ST_BLEN+3];
798 char dnsbl_buf[24];
799 int blen, len, i;
800 #define S(str,off) (blen = log_ask_st_sub(buf, blen, str, LITZ(str), off))
801 #define S0(bit,off,str) if (ask_st & bit) S(str,off)
802 #define S1(bit,s1off,str) S0(bit,(log_type != 2 && (s1off)),str)
803 #define S2(bit,str) S0(bit,0,str)
804
805 blen = 0;
806 S2(ASK_ST_QUERY, "query");
807
808 /* the CGI scripts want to know why */
809 if (sws & FLTR_SW_MTA_FIRST) {
810 S2(ASK_ST_MTA_ISSPAM, "MTA"DCC_XHDR_ISSPAM);
811 S2(ASK_ST_MTA_NOTSPAM, "MTA"DCC_XHDR_ISOK);
812 }
813
814 S2(ASK_ST_WLIST_NOTSPAM, "wlist"DCC_XHDR_ISOK);
815 if (log_type != 2 && !(ask_st & ASK_ST_WLIST_NOTSPAM))
816 S2(ASK_ST_WLIST_ISSPAM, "wlist"DCC_XHDR_ISSPAM);
817
818 S1(ASK_ST_SRVR_ISSPAM, (sws & FLTR_SW_DCC_OFF), "DCC"DCC_XHDR_ISSPAM);
819 S1(ASK_ST_SRVR_NOTSPAM, (sws & FLTR_SW_DCC_OFF), "DCC"DCC_XHDR_ISOK);
820 S1(ASK_ST_REP_ISSPAM, !(sws & FLTR_SW_REP_ON), "Rep"DCC_XHDR_ISSPAM);
821
822 for (i = 0; i < MAX_DNSBL_GROUPS; ++i) {
823 if (ask_st & ASK_ST_DNSBL_HIT(i)) {
824 if (have_dnsbl_groups)
825 len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
826 "DNSBL%d"DCC_XHDR_ISSPAM, i+1);
827 else
828 len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
829 "DNSBL"DCC_XHDR_ISSPAM);
830 /* log "DNSBLx-->spam" or "DNSBLx-->spam(off)" */
831 blen = log_ask_st_sub(buf, blen,
832 dnsbl_buf, len,
833 log_type != 2
834 && !(sws & FLTR_SW_DNSBL(i)));
835 } else if (ask_st & ASK_ST_DNSBL_TIMEO(i)) {
836 if (have_dnsbl_groups)
837 len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
838 "DNSBL%d(timeout)", i+1);
839 else
840 len = snprintf(dnsbl_buf, sizeof(dnsbl_buf),
841 "DNSBL(timeout)");
842 blen = log_ask_st_sub(buf, blen,
843 dnsbl_buf, len, 0);
844 }
845 }
846
847 if (!(sws & FLTR_SW_MTA_FIRST)) {
848 S2(ASK_ST_MTA_ISSPAM, "MTA"DCC_XHDR_ISSPAM);
849 S2(ASK_ST_MTA_NOTSPAM, "MTA"DCC_XHDR_ISOK);
850 }
851 blen = log_ask_st_sub(buf, blen, dcc_progname, dcc_progname_len, 0);
852 if (log_type == 1) {
853 blen = log_ask_st_sub(buf, blen,
854 "per-user", LITZ("per-user"), 0);
855 } else if (log_type == 2) {
856 blen = log_ask_st_sub(buf, blen,
857 "global", LITZ("global"), 0);
858 }
859 blen = log_ask_st_sub(buf, blen, "\n\n", 2, 0);
860 fnc(cp, buf, blen);
861
862 if (hdr->used != 0)
863 xhdr_write(fnc, cp, hdr->buf, hdr->used, 0);
864 #undef S
865 #undef S0
866 #undef S1
867 #undef S2
868 }
869
870
871
872 /* parse -G options for DCC clients */
873 u_char /* 0=bad */
874 dcc_parse_client_grey(const char *arg)
875 {
876 int bits;
877 const char *p;
878
879 while (*arg != '\0') {
880 if (dcc_ck_word_comma(&arg, "on")) {
881 grey_on = 1;
882 continue;
883 }
884 if (dcc_ck_word_comma(&arg, "off")) {
885 grey_on = 0;
886 continue;
887 }
888 if (dcc_ck_word_comma(&arg, "query")) {
889 grey_query_only = 1;
890 continue;
891 }
892 if (dcc_ck_word_comma(&arg, "noIP")) {
893 grey_on = 1;
894 trim_grey_ip_addr = 1;
895 memset(&grey_ip_mask, 0, sizeof(grey_ip_mask));
896 continue;
897 }
898 if (!CLITCMP(arg, "IPmask/")) {
899 bits = 0;
900 for (p = arg+LITZ("IPmask/");
901 *p >= '0' && *p <= '9';
902 ++p)
903 bits = bits*10 + *p - '0';
904 if (bits > 0 && bits < 128
905 && (*p == '\0' || *p == ',')) {
906 arg = p;
907 if (*p == ',')
908 ++arg;
909 grey_on = 1;
910 trim_grey_ip_addr = 1;
911 /* assume giant blocks are really IPv4 */
912 if (bits <= 32)
913 bits += 128-32;
914 dcc_bits2mask(&grey_ip_mask, bits);
915 continue;
916 }
917 }
918 return 0;
919 }
920 return 1;
921 }
922
923
924
925 /* sanity check the DCC server's answer */
926 u_char
927 dcc_ck_grey_answer(DCC_EMSG emsg, const DCC_OP_RESP *resp)
928 {
929 int recv_len;
930
931 recv_len = ntohs(resp->hdr.len);
932 if (resp->hdr.op != DCC_OP_ANSWER) {
933 dcc_pemsg(EX_UNAVAILABLE, emsg, "DCC %s: %s %*s",
934 dcc_srvr_nm(1),
935 dcc_hdr_op2str(0, 0, &resp->hdr),
936 (resp->hdr.op == DCC_OP_ERROR
937 ? (recv_len - (ISZ(resp->error)
938 - ISZ(resp->error.msg)))
939 : 0),
940 resp->error.msg);
941 return 0;
942 }
943
944 if (recv_len != sizeof(DCC_GREY_ANSWER)) {
945 dcc_pemsg(EX_UNAVAILABLE, emsg,
946 "greylist server %s answered with %d instead of"
947 " %d bytes",
948 dcc_srvr_nm(1), recv_len, ISZ(DCC_GREY_ANSWER));
949 return 0;
950 }
951
952 return 1;
953 }
954
955
956
957 ASK_GREY_RESULT
958 ask_grey(DCC_EMSG emsg,
959 DCC_CLNT_CTXT *ctxt,
960 DCC_OPS op, /* DCC_OP_GREY_{REPORT,QUERY,WHITE} */
961 DCC_SUM msg_sum, /* put msg+sender+target cksum here */
962 DCC_SUM triple_sum, /* put greylist triple checksum here */
963 const DCC_GOT_CKS *cks,
964 const DCC_SUM env_to_sum,
965 DCC_TGTS *pembargo_num,
966 DCC_TGTS *pearly_tgts, /* ++ report to DCC even if embargoed */
967 DCC_TGTS *plate_tgts) /* ++ don't report to DCC */
968 {
969 MD5_CTX ctx;
970 DCC_REPORT rpt;
971 DCC_OP_RESP resp;
972 DCC_CK *ck;
973 DCC_CK_TYPES type;
974 const DCC_GOT_SUM *g;
975 DCC_TGTS result_tgts;
976 int num_cks;
977
978 if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
979 dcc_pemsg(EX_UNAVAILABLE, emsg,
980 "IP address not available for greylisting");
981 memset(triple_sum, 0, sizeof(*triple_sum));
982 memset(msg_sum, 0, sizeof(*msg_sum));
983 return ASK_GREY_FAIL;
984 }
985 if (cks->sums[DCC_CK_ENV_FROM].type != DCC_CK_ENV_FROM) {
986 dcc_pemsg(EX_UNAVAILABLE, emsg,
987 "env_From not available for greylisting");
988 memset(triple_sum, 0, sizeof(*triple_sum));
989 memset(msg_sum, 0, sizeof(*msg_sum));
990 return ASK_GREY_FAIL;
991 }
992
993 /* Check the common checksums for whitelisting at the greylist server.
994 * This assumes DCC_CK_GREY_TRIPLE > DCC_CK_GREY_MSG > other types */
995 ck = rpt.cks;
996 num_cks = 0;
997 for (type = 0, g = cks->sums;
998 type <= DCC_CK_TYPE_LAST;
999 ++type, ++g) {
1000 /* greylisting needs a body checksum, even if
1001 * it is the fake checksum for a missing body */
1002 if (!g->rpt2srvr && type != DCC_CK_BODY)
1003 continue;
1004 ck->type = type;
1005 ck->len = sizeof(*ck);
1006 memcpy(ck->sum, g->sum, sizeof(ck->sum));
1007 ++ck;
1008 ++num_cks;
1009 }
1010
1011 /* include in the request the grey message checksum as the checksum
1012 * of the body, the env_From sender, and env_To target checksums */
1013 MD5Init(&ctx);
1014 MD5Update(&ctx, cks->sums[DCC_CK_BODY].sum, sizeof(DCC_SUM));
1015 MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
1016 MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
1017 MD5Final(msg_sum, &ctx);
1018 ck->type = DCC_CK_GREY_MSG;
1019 ck->len = sizeof(*ck);
1020 memcpy(ck->sum, msg_sum, sizeof(ck->sum));
1021 ++ck;
1022 ++num_cks;
1023
1024 /* include the triple checksum of the sender, the sender's IP
1025 * address, and the target */
1026 MD5Init(&ctx);
1027 if (trim_grey_ip_addr) {
1028 struct in6_addr addr;
1029 DCC_SUM sum;
1030 int wno;
1031
1032 for (wno = 0; wno < 4; ++wno) {
1033 addr.s6_addr32[wno] = (cks->ip_addr.s6_addr32[wno]
1034 & grey_ip_mask.s6_addr32[wno]);
1035 }
1036 dcc_ck_ipv6(sum, &addr);
1037 MD5Update(&ctx, sum, sizeof(DCC_SUM));
1038 } else {
1039 MD5Update(&ctx, cks->sums[DCC_CK_IP].sum, sizeof(DCC_SUM));
1040 }
1041 MD5Update(&ctx, cks->sums[DCC_CK_ENV_FROM].sum, sizeof(DCC_SUM));
1042 MD5Update(&ctx, env_to_sum, sizeof(DCC_SUM));
1043 MD5Final(triple_sum, &ctx);
1044 ck->type = DCC_CK_GREY3;
1045 ck->len = sizeof(*ck);
1046 memcpy(ck->sum, triple_sum, sizeof(ck->sum));
1047 ++num_cks;
1048
1049 if (!dcc_clnt_op(emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0, 0,
1050 &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks)
1051 + num_cks*sizeof(rpt.cks[0])),
1052 op, &resp, sizeof(resp))) {
1053 return ASK_GREY_FAIL;
1054 }
1055
1056 if (!dcc_ck_grey_answer(emsg, &resp))
1057 return ASK_GREY_FAIL;
1058
1059 /* see what the greylist server had to say */
1060 result_tgts = ntohl(resp.gans.triple);
1061 switch (result_tgts) {
1062 case DCC_TGTS_OK: /* embargo ended just now */
1063 /* if we have previously included this target in a count of
1064 * targets sent to the DCC, then do not include it now */
1065 if (resp.gans.msg != 0 && plate_tgts)
1066 ++*plate_tgts;
1067 if (pembargo_num)
1068 *pembargo_num = 0;
1069 return ASK_GREY_EMBARGO_END;
1070
1071 case DCC_TGTS_TOO_MANY: /* no current embargo */
1072 if (pembargo_num)
1073 *pembargo_num = 0;
1074 return ((resp.gans.msg != 0)
1075 ? ASK_GREY_EMBARGO_END
1076 : ASK_GREY_PASS);
1077
1078 case DCC_TGTS_GREY_WHITE: /* whitelisted for greylisting */
1079 if (pembargo_num)
1080 *pembargo_num = 0;
1081 return ASK_GREY_WHITE;
1082
1083 default: /* embargoed */
1084 /* if this is a brand new embargo,
1085 * then count this target in the DCC report */
1086 if (resp.gans.msg == 0 && pearly_tgts)
1087 ++*pearly_tgts;
1088 if (pembargo_num)
1089 *pembargo_num = result_tgts+1;
1090 return ASK_GREY_EMBARGO;
1091 }
1092 }