0
|
1 /* Distributed Checksum Clearinghouse server |
|
2 * |
|
3 * report the previously computed checksums of a message |
|
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.70 $Revision$ |
|
40 */ |
|
41 |
|
42 #include "dcc_ck.h" |
|
43 #include "dcc_xhdr.h" |
|
44 |
|
45 |
|
46 static DCC_EMSG dcc_emsg; |
|
47 |
|
48 static const char *homedir; |
|
49 static const char *mapfile_nm = DCC_MAP_NM_DEF; |
|
50 |
|
51 static char *local_tgts_str; |
|
52 static DCC_TGTS local_tgts = 1; |
|
53 static u_char local_tgts_spam, local_tgts_set; |
|
54 |
|
55 static const char* white_nm; |
|
56 static const char *ifile_nm; |
|
57 static FILE *ifile; |
|
58 static const char *grey_sum; |
|
59 |
|
60 static DCC_CLNT_CTXT *ctxt; |
|
61 static u_char print_cksums; |
|
62 |
|
63 static ASK_ST ask_st; |
|
64 static FLTR_SWS rcpt_sws; |
|
65 |
|
66 static DCC_GOT_CKS cks; |
|
67 static DCC_CKS_WTGTS wtgts; |
|
68 static u_char have_sum; |
|
69 |
|
70 static DCC_HEADER_BUF header; |
|
71 |
|
72 /* kludges to avoid linking with dnsbl.c */ |
|
73 DNSBL *dnsbls; |
|
74 u_char have_dnsbl_groups; |
|
75 |
|
76 static void do_grey(void); |
|
77 static int add_cksum(DCC_EMSG, DCC_WF *, DCC_CK_TYPES, DCC_SUM, DCC_TGTS); |
|
78 static void print_ck(void *arg, const char *, u_int); |
|
79 |
|
80 |
|
81 static void NRATTRIB |
|
82 usage(void) |
|
83 { |
|
84 dcc_logbad(EX_USAGE, |
|
85 "usage: [-VdCQ] [-h homedir] [-m map] [-w whiteclnt]" |
|
86 " [-t targets]\n" |
|
87 " [-c type,[log-thold,][spam-thold]] [-g [not-]type]\n" |
|
88 " [-i infile] [-G grey-cksum] [-L ltype,facility.level]"); |
|
89 } |
|
90 |
|
91 |
|
92 |
|
93 int NRATTRIB |
|
94 main(int argc, char **argv) |
|
95 { |
|
96 char buf[200]; |
|
97 const char *bufp; |
|
98 char type_str[DCC_XHDR_MAX_TYPE_LEN+1]; |
|
99 DCC_CK_TYPES type; |
|
100 u_long l; |
|
101 u_char skip_heading, result; |
|
102 char c1, c2, *p; |
|
103 int i; |
|
104 |
|
105 dcc_syslog_init(0, argv[0], 0); |
|
106 dcc_clear_tholds(); |
|
107 |
|
108 /* we must be SUID to read and write the system's common connection |
|
109 * parameter memory mapped file. We also need to read the common |
|
110 * local white list and write the mmap()'ed hash file */ |
|
111 dcc_init_priv(); |
|
112 |
|
113 ifile = stdin; |
|
114 while ((i = getopt(argc, argv, "VdCQh:m:w:t:c:g:i:G:L:")) != -1) { |
|
115 switch (i) { |
|
116 case 'V': |
|
117 fprintf(stderr, DCC_VERSION"\n"); |
|
118 exit(EX_OK); |
|
119 break; |
|
120 |
|
121 case 'd': |
|
122 ++dcc_clnt_debug; |
|
123 break; |
|
124 |
|
125 case 'Q': |
|
126 ask_st |= ASK_ST_QUERY; |
|
127 break; |
|
128 |
|
129 case 'C': |
|
130 print_cksums = 1; |
|
131 break; |
|
132 |
|
133 case 'h': |
|
134 homedir = optarg; |
|
135 break; |
|
136 |
|
137 case 'm': |
|
138 mapfile_nm = optarg; |
|
139 break; |
|
140 |
|
141 case 'w': |
|
142 white_nm = optarg; |
|
143 break; |
|
144 |
|
145 case 't': |
|
146 local_tgts_str = optarg; |
|
147 if (!strcasecmp(optarg, "many")) { |
|
148 local_tgts_spam = 1; |
|
149 local_tgts = 1; |
|
150 local_tgts_set = 1; |
|
151 } else { |
|
152 l = strtoul(optarg, &p, 10); |
|
153 if (*p != '\0' || l > 1000) |
|
154 dcc_error_msg("invalid count \"%s\"", |
|
155 optarg); |
|
156 else |
|
157 local_tgts = l; |
|
158 local_tgts_spam = 0; |
|
159 local_tgts_set = 1; |
|
160 } |
|
161 break; |
|
162 |
|
163 case 'c': |
|
164 dcc_parse_tholds("-c ", optarg); |
|
165 break; |
|
166 |
|
167 case 'g': /* honor not-spam "counts" */ |
|
168 dcc_parse_honor(optarg); |
|
169 break; |
|
170 |
|
171 case 'i': |
|
172 /* open the input file now, before changing to the |
|
173 * home DCC directory */ |
|
174 ifile_nm = optarg; |
|
175 ifile = fopen(ifile_nm, "r"); |
|
176 if (!ifile) |
|
177 dcc_logbad(EX_USAGE, |
|
178 "bad input file \"%s\": %s", |
|
179 ifile_nm, ERROR_STR()); |
|
180 break; |
|
181 |
|
182 case 'G': |
|
183 grey_sum = optarg; |
|
184 break; |
|
185 |
|
186 case 'L': |
|
187 dcc_parse_log_opt(optarg); |
|
188 break; |
|
189 |
|
190 default: |
|
191 usage(); |
|
192 } |
|
193 } |
|
194 argc -= optind; |
|
195 argv += optind; |
|
196 if (argc != 0) |
|
197 usage(); |
|
198 |
|
199 dcc_clnt_unthread_init(); |
|
200 dcc_cdhome(0, homedir, 1); |
|
201 |
|
202 /* open /var/dcc/map and start a connection to a DCC server */ |
|
203 ctxt = dcc_clnt_init(dcc_emsg, 0, mapfile_nm, DCC_CLNT_FG_NONE); |
|
204 if (!ctxt) |
|
205 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
206 |
|
207 if (grey_sum) { |
|
208 if (ifile_nm) |
|
209 dcc_logbad(EX_USAGE, "-i and -G incompatible"); |
|
210 do_grey(); |
|
211 exit(EX_OK); |
|
212 } |
|
213 |
|
214 if (!ifile_nm) |
|
215 ifile_nm = "stdin"; |
|
216 |
|
217 /* get the checksums */ |
|
218 skip_heading = 0; |
|
219 for (;;) { |
|
220 bufp = fgets(buf, sizeof(buf), ifile); |
|
221 if (!bufp) { |
|
222 if (ferror(ifile)) |
|
223 dcc_logbad(EX_DATAERR, "fgets(%s): %s", |
|
224 ifile_nm, ERROR_STR()); |
|
225 break; |
|
226 } |
|
227 |
|
228 /* ignore blank lines */ |
|
229 i = strlen(buf); |
|
230 if (!i) { |
|
231 skip_heading = 0; |
|
232 continue; |
|
233 } |
|
234 |
|
235 /* trim leading and trailing whitespace */ |
|
236 p = &buf[i-1]; |
|
237 bufp += strspn(bufp, DCC_WHITESPACE); |
|
238 c2 = *p; /* should be '\n' */ |
|
239 while (p > bufp) { |
|
240 c1 = *(p-1); |
|
241 if (c1 != '\r' && c1 != ' ' && c1 != '\t') |
|
242 break; |
|
243 *--p = c2; |
|
244 } |
|
245 if (*bufp == '\n' || *bufp == '\0') { |
|
246 skip_heading = 0; |
|
247 continue; |
|
248 } |
|
249 |
|
250 /* ignore DCC header lines such as in the output |
|
251 * of `dccproc -C` */ |
|
252 if (skip_heading == 0 |
|
253 && !CLITCMP(bufp, DCC_XHDR_START)) { |
|
254 skip_heading = 1; |
|
255 continue; |
|
256 } |
|
257 /* skip headings for the checksums */ |
|
258 if (skip_heading <= 1 |
|
259 && !CLITCMP(bufp, DCC_XHDR_REPORTED)) { |
|
260 skip_heading = 2; |
|
261 continue; |
|
262 } |
|
263 |
|
264 /* handle the next checksum */ |
|
265 bufp = dcc_parse_word(dcc_emsg, type_str, sizeof(type_str), |
|
266 bufp, "checksum type", 0, 0); |
|
267 if (!bufp) |
|
268 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
269 type = dcc_str2type_wf(type_str, -1); |
|
270 if (type != DCC_CK_ENV_TO |
|
271 && 0 >= dcc_parse_hex_ck(dcc_emsg, &cmn_wf, |
|
272 type_str, type, |
|
273 bufp, 1, add_cksum)) |
|
274 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
275 } |
|
276 fclose(ifile); |
|
277 |
|
278 if (!have_sum) |
|
279 dcc_logbad(EX_DATAERR, "no reportable checksums"); |
|
280 |
|
281 dcc_wf_init(&cmn_wf, 0); |
|
282 if (!unthr_ask_white(dcc_emsg, &ask_st, &rcpt_sws, |
|
283 white_nm, &cks, wtgts)) |
|
284 dcc_error_msg("%s", dcc_emsg); |
|
285 |
|
286 if (!local_tgts_set) { |
|
287 local_tgts = (ask_st & ASK_ST_QUERY) ? 0 : 1; |
|
288 local_tgts_spam = 0; |
|
289 } else if (local_tgts == 0) { |
|
290 ask_st |= ASK_ST_QUERY; |
|
291 local_tgts_spam = 0; |
|
292 } else if (ask_st & ASK_ST_QUERY) { |
|
293 dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"", |
|
294 local_tgts_str); |
|
295 local_tgts = 0; |
|
296 local_tgts_spam = 0; |
|
297 } |
|
298 if (local_tgts == DCC_TGTS_TOO_MANY) { |
|
299 local_tgts = 1; |
|
300 local_tgts_spam = 1; |
|
301 } |
|
302 if (local_tgts != 0 |
|
303 && (ask_st & ASK_ST_CLNT_ISSPAM)) |
|
304 local_tgts_spam = 1; |
|
305 |
|
306 result = unthr_ask_dcc(dcc_emsg, ctxt, &header, &ask_st, |
|
307 &cks, local_tgts_spam, local_tgts); |
|
308 if (!result) { |
|
309 dcc_error_msg("%s", dcc_emsg); |
|
310 } else if (header.buf[0] != '\0') { |
|
311 printf("%s", header.buf); |
|
312 } else if (dcc_clnt_debug) { |
|
313 if (ask_st & ASK_ST_WLIST_NOTSPAM) |
|
314 printf("no header; checksums are locally whitelisted"); |
|
315 else |
|
316 printf("no header"); |
|
317 } |
|
318 |
|
319 if (print_cksums) |
|
320 dcc_print_cks(print_ck, 0, local_tgts_spam, local_tgts, |
|
321 &cks, wtgts, 0); |
|
322 |
|
323 exit(EX_OK); |
|
324 } |
|
325 |
|
326 |
|
327 |
|
328 static void NRATTRIB |
|
329 do_grey(void) |
|
330 { |
|
331 union { |
|
332 u_int32_t n[4]; |
|
333 DCC_SUM sum; |
|
334 } u; |
|
335 DCC_REPORT rpt; |
|
336 DCC_OP_RESP resp; |
|
337 |
|
338 if (4 != sscanf(grey_sum, DCC_CKSUM_HEX_PAT, |
|
339 &u.n[0], &u.n[1], &u.n[2], &u.n[3])) |
|
340 dcc_logbad(EX_USAGE, |
|
341 "unrecognized greylist checksum"); |
|
342 u.n[0] = htonl(u.n[0]); |
|
343 u.n[1] = htonl(u.n[1]); |
|
344 u.n[2] = htonl(u.n[2]); |
|
345 u.n[3] = htonl(u.n[3]); |
|
346 |
|
347 memset(&rpt, 0, sizeof(rpt)); |
|
348 memcpy(rpt.cks[0].sum, u.sum, sizeof(rpt.cks[0].sum)); |
|
349 rpt.cks[0].type = DCC_CK_GREY3; |
|
350 rpt.cks[0].len = sizeof(rpt.cks[0]); |
|
351 if (!dcc_clnt_op(dcc_emsg, ctxt, DCC_CLNT_FG_GREY, 0, 0, 0, |
|
352 &rpt.hdr, (sizeof(rpt) - sizeof(rpt.cks) |
|
353 + sizeof(rpt.cks[0])), |
|
354 (ask_st & ASK_ST_QUERY) |
|
355 ? DCC_OP_GREY_QUERY : DCC_OP_GREY_WHITE, |
|
356 &resp, sizeof(resp))) |
|
357 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
358 |
|
359 if (!dcc_ck_grey_answer(dcc_emsg, &resp)) |
|
360 dcc_logbad(dcc_ex_code, "%s", dcc_emsg); |
|
361 |
|
362 switch (ntohl(resp.gans.triple)) { |
|
363 case DCC_TGTS_OK: /* embargo ended just now */ |
|
364 printf(DCC_XHDR_EMBARGO_ENDED"\n"); |
|
365 break; |
|
366 case DCC_TGTS_TOO_MANY: /* no current embargo */ |
|
367 printf(DCC_XHDR_EMBARGO_PASS"\n"); |
|
368 break; |
|
369 case DCC_TGTS_GREY_WHITE: /* whitelisted for greylisting */ |
|
370 printf(DCC_XHDR_EMBARGO_OK"\n"); |
|
371 break; |
|
372 default: /* embargoed */ |
|
373 printf(DCC_XHDR_EMBARGO"\n"); |
|
374 break; |
|
375 } |
|
376 exit(EX_OK); |
|
377 } |
|
378 |
|
379 |
|
380 |
|
381 static int |
|
382 add_cksum(DCC_EMSG emsg, DCC_WF *wf UATTRIB, |
|
383 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts) |
|
384 { |
|
385 static DCC_SUM zero; |
|
386 |
|
387 if (cks.sums[type].type != DCC_CK_INVALID |
|
388 && type != DCC_CK_SUB) { |
|
389 dcc_pemsg(EX_DATAERR, emsg, "duplicate %s checksum", |
|
390 dcc_type2str_err(type, 0, 0, 0)); |
|
391 } |
|
392 |
|
393 /* envelope Rcpt_To values are never sent to the server */ |
|
394 if (type == DCC_CK_ENV_TO) |
|
395 return 1; |
|
396 |
|
397 /* do not send FUZ2 missing checksum */ |
|
398 if (type == DCC_CK_FUZ2 |
|
399 && !memcmp(sum, zero, sizeof(DCC_SUM))) |
|
400 return 1; |
|
401 |
|
402 memcpy(cks.sums[type].sum, sum, sizeof(cks.sums[type].sum)); |
|
403 cks.sums[type].type = type; |
|
404 cks.sums[type].rpt2srvr = 1; |
|
405 cks.sums[type].tgts = DCC_TGTS_INVALID; |
|
406 if (tgts) |
|
407 have_sum = 1; |
|
408 return 1; |
|
409 } |
|
410 |
|
411 |
|
412 |
|
413 static void |
|
414 print_ck(void *arg UATTRIB, const char *buf, u_int buf_len) |
|
415 { |
|
416 fwrite(buf, buf_len, 1, stdout); |
|
417 } |