comparison dcclib/ckwhite.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 * check checksums in the local whitelist
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.133 $Revision$
40 */
41
42 #include "dcc_ck.h"
43
44
45 static const DCC_WHITE_MAGIC white_magic = WHITE_MAGIC_B_STR WHITE_MAGIC_V_STR;
46
47 #define EMPTY_WHITE_SIZE (sizeof(DCC_WHITE_TBL) - sizeof(DCC_WHITE_ENTRY))
48 #define MAX_WHITE_ENTRIES (DCC_WHITE_TBL_BINS*10)
49 #define ENTRIES2SIZE0(_l) (sizeof(DCC_WHITE_TBL) \
50 - sizeof(DCC_WHITE_ENTRY) \
51 + ((_l) * sizeof(DCC_WHITE_ENTRY)))
52 #ifdef DCC_WIN32
53 /* Make the hash table files maximum size on WIN32.
54 * You cannot change the size of a WIN32 mapping object without
55 * getting all processes using it to release it so that it can be
56 * recreated. This may cause problems if the size of hash table
57 * header changes.
58 * Since the file does not change size, there is no need to remap it */
59 #define ENTRIES2SIZE(_l) ENTRIES2SIZE0(MAX_WHITE_ENTRIES)
60 #else
61 #define ENTRIES2SIZE(_l) ENTRIES2SIZE0(_l)
62 #endif
63
64
65
66 void
67 dcc_wf_init(DCC_WF *wf,
68 u_int wf_flags) /* DCC_WF_* */
69 {
70 memset(wf, 0, sizeof(*wf));
71 wf->ht_fd = -1;
72 wf->wf_flags = wf_flags & ~DCC_WF_NOFILE;
73 }
74
75
76
77 /* this is needed only on systems without coherent mmap()/read()/write() */
78 static void
79 sync_white(DCC_WF *wf)
80 {
81 if (wf->wtbl
82 && 0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC))
83 dcc_error_msg("msync(%s): %s", wf->ht_nm, ERROR_STR());
84 }
85
86
87
88 #define STILL_BROKE(wf,now) ((wf)->broken != 0 \
89 && !DCC_IS_TIME(now, (wf)->broken, \
90 DCC_WHITE_BROKEN_DELAY))
91 static void
92 is_broken(DCC_WF *wf)
93 {
94 wf->broken = time(0) + DCC_WHITE_BROKEN_DELAY;
95
96 /* This is racy, but the worst that can happen is that
97 * another process races to set the retry timer */
98 if (!(wf->wf_flags & DCC_WF_RO) && wf->wtbl) {
99 wf->wtbl->hdr.broken = wf->broken;
100 sync_white(wf);
101 }
102 }
103
104
105
106 static void
107 unmap_white_ht(DCC_WF *wf)
108 {
109 if (!wf->wtbl)
110 return;
111
112 sync_white(wf);
113 #ifdef DCC_WIN32
114 win32_unmap(&wf->ht_map, wf->wtbl, wf->ht_nm);
115 #else
116 if (0 > munmap((void *)wf->wtbl, wf->wtbl_size))
117 dcc_error_msg("munmap(%s,%d): %s",
118 wf->ht_nm, wf->wtbl_size, ERROR_STR());
119 #endif
120 wf->wtbl = 0;
121 }
122
123
124
125 static u_char /* 1=done, 0=failed */
126 map_white_ht(DCC_EMSG emsg, DCC_WF *wf, DCC_WHITE_INX entries)
127 {
128 size_t size;
129 #ifndef DCC_WIN32
130 void *p;
131 #endif
132
133 unmap_white_ht(wf);
134
135 if (entries > MAX_WHITE_ENTRIES) {
136 dcc_pemsg(EX_IOERR, emsg, "%s should not contain %d entries",
137 wf->ht_nm, entries);
138 return 0;
139 }
140
141 size = ENTRIES2SIZE(entries);
142 #ifdef DCC_WIN32
143 if (!wf->wtbl) {
144 wf->wtbl = win32_map(emsg, &wf->ht_map, wf->ht_nm, wf->ht_fd,
145 size);
146 if (!wf->wtbl)
147 return 0;
148 }
149 #else
150 p = mmap(0, size,
151 (wf->wf_flags & DCC_WF_RO)
152 ? PROT_READ : (PROT_READ|PROT_WRITE),
153 MAP_SHARED, wf->ht_fd, 0);
154 if (p == MAP_FAILED) {
155 dcc_pemsg(EX_IOERR, emsg, "mmap(%s,%d): %s",
156 wf->ht_nm, (int)size, ERROR_STR());
157 return 0;
158 }
159 wf->wtbl = p;
160 #endif
161 wf->wtbl_size = size;
162 wf->wtbl_entries = entries;
163 wf->wtbl_flags = wf->wtbl->hdr.flags;
164
165 return 1;
166 }
167
168
169
170 static u_char
171 close_white_ht(DCC_EMSG emsg, DCC_WF *wf)
172 {
173 u_char result = 1;
174
175 if (wf->ht_fd < 0)
176 return result;
177
178 wf->closed = 1;
179
180 unmap_white_ht(wf);
181
182 #ifdef DCC_WIN32
183 /* unlock the file before closing it to keep Win95 happy */
184 if (!dcc_unlock_fd(emsg, wf->ht_fd, DCC_LOCK_ALL_FILE,
185 "whitelist ", wf->ht_nm))
186 result = 0;
187 #endif
188 if (0 > close(wf->ht_fd)) {
189 dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
190 wf->ht_nm, ERROR_STR());
191 result = 0;
192 }
193
194 wf->ht_fd = -1;
195 memset(&wf->ht_sb, 0, sizeof(wf->ht_sb));
196 #ifndef DCC_WIN32
197 wf->ht_sb.st_dev = -1;
198 wf->ht_sb.st_ino = -1;
199 #endif
200 return result;
201 }
202
203
204
205 static int /* 0=ok -1=failed */
206 unlink_white_ht(DCC_WF *wf,
207 time_t now) /* 0=our own new file */
208 {
209 int result;
210
211 /* mark it bad if it is a brand new hash table */
212 if (!now && wf->wtbl && !(wf->wf_flags & DCC_WF_RO))
213 wf->wtbl->hdr.ascii_mtime = 0;
214
215 #ifdef DCC_WIN32
216 /* racy but you cannot unlink an open file on WIN32 */
217 close_white_ht(0, wf);
218 #endif
219 result = -1;
220 if (!(wf->wf_flags & DCC_WF_RO)) {
221 if (0 <= unlink(wf->ht_nm)) {
222 result = 0;
223 #ifndef DCC_WIN32
224 } else if ((errno == EACCES || errno == EPERM)
225 && dcc_get_priv_home(wf->ht_nm)) {
226 if (0 <= unlink(wf->ht_nm))
227 result = 0;
228 dcc_rel_priv();
229 #endif
230 }
231 if (result < 0)
232 dcc_error_msg("unlink(%s): %s",
233 wf->ht_nm, ERROR_STR());
234 }
235
236 /* If we failed to unlink the old hash table,
237 * remember in core that things are broken but do not touch the file
238 * in case it is a link to /etc/passwd or something else dangerous */
239 if (result < 0 && now)
240 wf->broken = now + DCC_WHITE_BROKEN_DELAY;
241 #ifndef DCC_WIN32
242 close_white_ht(0, wf);
243 #endif
244
245 return result;
246 }
247
248
249
250 static u_char
251 new_ht_nm(DCC_EMSG emsg, DCC_WF *wf, u_char new)
252 {
253 if (wf->ascii_nm_len >= ISZ(wf->ht_nm) - LITZ(DCC_WHITE_SUFFIX)) {
254 dcc_pemsg(EX_NOINPUT, emsg, "bad whitelist file name \"%s\"",
255 wf->ascii_nm);
256 is_broken(wf);
257 return 0;
258 }
259
260 memcpy(wf->ht_nm, wf->ascii_nm, wf->ascii_nm_len);
261 strcpy(&wf->ht_nm[wf->ascii_nm_len],
262 new ? DCC_WHITE_NEW_SUFFIX : DCC_WHITE_SUFFIX);
263 return 1;
264 }
265
266
267
268 u_char
269 dcc_new_white_nm(DCC_EMSG emsg, DCC_WF *wf,
270 const char *new_white_nm)
271 {
272 DCC_PATH new_path;
273 const char *new;
274 u_int len;
275 int i;
276
277 if (!strcmp(new_white_nm, wf->ascii_nm))
278 return 1;
279
280 close_white_ht(0, wf);
281 dcc_wf_init(wf, wf->wf_flags);
282
283 if (!fnm2rel(new_path, new_white_nm, 0)) {
284 dcc_pemsg(EX_USAGE, emsg, "bad whitelist name \"%s\"",
285 new_white_nm);
286 return 0;
287 }
288
289 len = strlen(new_path);
290 i = len - LITZ(DCC_WHITE_SUFFIX);
291 if (i > 0 && (!strcmp(&new_path[i], DCC_WHITE_SUFFIX)
292 || !strcmp(&new_path[i], DCC_WHITE_NEW_SUFFIX))) {
293 len = i;
294 new_path[len] = '\0';
295 }
296
297 if (len > ISZ(wf->ht_nm) - ISZ(DCC_WHITE_SUFFIX)) {
298 dcc_pemsg(EX_USAGE, emsg, "long whitelist name \"%s\"",
299 new_white_nm);
300 return 0;
301 }
302
303 new = path2fnm(new_path);
304 len = strlen(new);
305 memcpy(wf->ascii_nm, new, len+1);
306 wf->ascii_nm_len = len;
307 return 1;
308 }
309
310
311
312 /* open and shared-lock a hash table file */
313 static int /* -1=fatal 0=rebuild, 1=ok */
314 open_white_ht(DCC_EMSG emsg, DCC_WF *wf)
315 {
316 int size;
317 DCC_WHITE_INX entries;
318
319 close_white_ht(0, wf);
320
321 if (!new_ht_nm(emsg, wf, 0))
322 return -1;
323
324 wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm,
325 (wf->wf_flags & DCC_WF_RO)
326 ? O_RDONLY : O_RDWR,
327 DCC_LOCK_OPEN_SHARE,
328 DCC_LOCK_ALL_FILE, 0);
329 if (wf->ht_fd < 0) {
330 /* if this is a `wlist` command and neither -P nor -Q
331 * were specified, try to open the file read-only */
332 if ((wf->wf_flags & DCC_WF_WLIST)
333 && !(wf->wf_flags & (DCC_WF_WLIST_RO | DCC_WF_WLIST_RW))) {
334 if (emsg)
335 emsg[0] = '\0';
336 wf->wf_flags |= DCC_WF_RO;
337 wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm,
338 O_RDONLY,
339 DCC_LOCK_OPEN_SHARE,
340 DCC_LOCK_ALL_FILE, 0);
341 }
342 if (wf->ht_fd < 0)
343 return 0;
344 }
345
346 if (fstat(wf->ht_fd, &wf->ht_sb) < 0) {
347 dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
348 wf->ht_nm, ERROR_STR());
349 close_white_ht(0, wf);
350 is_broken(wf);
351 return -1;
352 }
353
354 size = wf->ht_sb.st_size - EMPTY_WHITE_SIZE;
355 if (size < 0) {
356 dcc_pemsg(EX_NOINPUT, emsg,
357 "%s is too small to be a DCC whitelist hash table",
358 wf->ht_nm);
359 return unlink_white_ht(wf, time(0));
360
361 } else {
362 entries = size / sizeof(DCC_WHITE_ENTRY);
363 if (!map_white_ht(emsg, wf, entries))
364 return unlink_white_ht(wf, time(0));
365 }
366
367 if (memcmp(&wf->wtbl->magic, &white_magic, sizeof(white_magic))) {
368 /* rebuild old format files */
369 if (!memcmp(&wf->wtbl->magic, WHITE_MAGIC_B_STR,
370 LITZ(WHITE_MAGIC_B_STR))) {
371 if (dcc_clnt_debug)
372 dcc_trace_msg("%s is obsolete %s",
373 wf->ht_nm, wf->wtbl->magic);
374 } else {
375 dcc_pemsg(EX_NOINPUT, emsg,
376 "%s is not a DCC whitelist hash file",
377 wf->ht_nm);
378 }
379 return unlink_white_ht(wf, time(0));
380 }
381
382 if ((size % sizeof(DCC_WHITE_ENTRY)) != 0
383 || entries > MAX_WHITE_ENTRIES
384 || entries < wf->wtbl->hdr.entries) {
385 dcc_pemsg(EX_NOINPUT, emsg,
386 "invalid size of whitelist %s="OFF_DPAT,
387 wf->ht_nm, wf->ht_sb.st_size);
388 return unlink_white_ht(wf, time(0));
389 }
390
391 if (wf->wtbl->hdr.ascii_mtime == 0) {
392 close_white_ht(0, wf);
393 return 0; /* broken hash table */
394 }
395
396 /* we know the hash table is usable
397 * but we might want to rebuild it */
398 wf->need_reopen = 0;
399 wf->wtbl_flags = wf->wtbl->hdr.flags;
400
401 /* wlist and dccproc work on both per-user and global whitelists,
402 * so do not change the nature of the file if it is already known */
403 if (wf->wf_flags & DCC_WF_EITHER) {
404 if (wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
405 wf->wf_flags |= DCC_WF_PER_USER;
406 else
407 wf->wf_flags &= ~DCC_WF_PER_USER;
408 }
409
410 return 1;
411 }
412
413
414
415 static void
416 create_white_ht_sub(DCC_EMSG emsg, DCC_WF *new_wf, const DCC_WF *wf,
417 u_char *busyp)
418 {
419 /* do not use O_EXCL because we want to wait for any other
420 * process to finish
421 *
422 * wait if and only if we do not have a usable file open */
423
424 new_wf->ht_fd = dcc_lock_open(emsg, new_wf->ht_nm,
425 O_RDWR | O_CREAT,
426 wf->ht_fd >= 0 ? DCC_LOCK_OPEN_NOWAIT : 0,
427 DCC_LOCK_ALL_FILE, busyp);
428 if (new_wf->ht_fd < 0)
429 return;
430
431 /* a new hash table must be empty */
432 if (0 > fstat(new_wf->ht_fd, &new_wf->ht_sb)) {
433 dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
434 new_wf->ht_nm, ERROR_STR());
435 close_white_ht(emsg, new_wf);
436 return;
437 }
438
439 if (new_wf->ht_sb.st_size != 0) {
440 dcc_pemsg(EX_IOERR, emsg, "%s has non-zero size %d",
441 new_wf->ht_nm, (int)new_wf->ht_sb.st_size);
442 close_white_ht(emsg, new_wf);
443 return;
444 }
445 }
446
447
448
449 /* create and write-lock a new hash table file
450 * wait for lock if we don't have existing file open */
451 static int /* 1=done, 0=file busy, -1=fatal */
452 create_white_ht(DCC_EMSG emsg,
453 DCC_WF *tmp_wf, /* build with this */
454 const DCC_WF *wf) /* from this */
455 {
456 u_char busy = 0;
457
458 tmp_wf->ascii_nm_len = wf->ascii_nm_len;
459 memcpy(tmp_wf->ascii_nm, wf->ascii_nm, wf->ascii_nm_len+1);
460 if (!new_ht_nm(emsg, tmp_wf, 1)) {
461 tmp_wf->ht_nm[0] = '\0';
462 return -1;
463 }
464
465 if (tmp_wf->wf_flags & DCC_WF_RO) {
466 dcc_pemsg(EX_IOERR, emsg,
467 "read only access; cannot create or update %s",
468 tmp_wf->ht_nm);
469 tmp_wf->ht_nm[0] = '\0';
470 return -1;
471 }
472
473 #ifndef DCC_WIN32
474 /* We want to create a private hash table if the ASCII file
475 * is private, but a hash table owned by the DCC user if the
476 * ASCII file is public */
477 if (0 > access(tmp_wf->ascii_nm, R_OK | W_OK)
478 && dcc_get_priv_home(tmp_wf->ht_nm)) {
479 /* first try to open a public hash table */
480 create_white_ht_sub(emsg, tmp_wf, wf, &busy);
481 if (tmp_wf->ht_fd < 0 && !busy) {
482 if (emsg && dcc_clnt_debug > 2)
483 dcc_error_msg("%s", emsg);
484 unlink(tmp_wf->ht_nm);
485 create_white_ht_sub(emsg, tmp_wf, wf, &busy);
486 }
487 dcc_rel_priv();
488 }
489 #endif
490
491 if (tmp_wf->ht_fd < 0 && !busy) {
492 /* try to open or create a private hash table */
493 create_white_ht_sub(emsg, tmp_wf, wf, &busy);
494 if (tmp_wf->ht_fd < 0 && !busy) {
495 if (emsg && dcc_clnt_debug > 2)
496 dcc_error_msg("%s", emsg);
497 unlink(tmp_wf->ht_nm);
498 create_white_ht_sub(emsg, tmp_wf, wf, &busy);
499 }
500 }
501
502 #ifndef DCC_WIN32
503 /* try one last time with privileges in case the ASCII file has
504 * mode 666 but the directory does not */
505 if (tmp_wf->ht_fd < 0 && !busy) {
506 if (dcc_get_priv_home(tmp_wf->ht_nm)) {
507 if (emsg && dcc_clnt_debug > 2)
508 dcc_error_msg("%s", emsg);
509 unlink(tmp_wf->ht_nm);
510 create_white_ht_sub(emsg, tmp_wf, wf, &busy);
511 dcc_rel_priv();
512 }
513 }
514 #endif
515 if (tmp_wf->ht_fd < 0) {
516 tmp_wf->ht_nm[0] = '\0';
517 if (busy)
518 return 0;
519 return -1;
520 }
521 return 1;
522 }
523
524
525
526 #define FIND_WHITE_BROKEN ((DCC_WHITE_ENTRY *)-1)
527 static DCC_WHITE_ENTRY *
528 find_white(DCC_EMSG emsg, DCC_WF *wf, DCC_CK_TYPES type, const DCC_SUM sum,
529 DCC_WHITE_INX *binp)
530 {
531 u_long accum;
532 DCC_WHITE_INX bin, inx;
533 DCC_WHITE_ENTRY *e;
534 int loop_cnt, i;
535
536 if (!wf->wtbl || wf->wtbl->hdr.ascii_mtime == 0)
537 return FIND_WHITE_BROKEN;
538
539 accum = type;
540 for (i = sizeof(DCC_SUM)-1; i >= 0; --i)
541 accum = (accum >> 28) + (accum << 4) + sum[i];
542 bin = accum % DIM(wf->wtbl->bins);
543 if (binp)
544 *binp = bin;
545 inx = wf->wtbl->bins[bin];
546
547 for (loop_cnt = wf->wtbl->hdr.entries+1;
548 loop_cnt >= 0;
549 --loop_cnt) {
550 if (!inx)
551 return 0;
552 --inx;
553 /* if necessary, expand the mapped window into the file */
554 if (inx >= wf->wtbl_entries) {
555 if (inx >= wf->wtbl->hdr.entries) {
556 dcc_pemsg(EX_DATAERR, emsg,
557 "bogus index %u in %s",
558 inx, wf->ht_nm);
559 if (!(wf->wf_flags & DCC_WF_RO))
560 wf->wtbl->hdr.ascii_mtime = 0;
561 sync_white(wf);
562 return FIND_WHITE_BROKEN;
563 }
564 if (!map_white_ht(emsg, wf, wf->wtbl->hdr.entries))
565 return 0;
566 }
567 e = &wf->wtbl->tbl[inx];
568 if (e->type == type && !memcmp(e->sum, sum, sizeof(DCC_SUM)))
569 return e;
570 inx = e->fwd;
571 }
572
573 dcc_pemsg(EX_DATAERR, emsg, "chain length %d in %s"
574 " starting at %d near %d for %s %s",
575 wf->wtbl->hdr.entries+1,
576 wf->ht_nm,
577 (DCC_WHITE_INX)(accum % DIM(wf->wtbl->bins)), inx,
578 dcc_type2str_err(type, 0, 0, 0),
579 dcc_ck2str_err(type, sum, 0));
580
581 if (!(wf->wf_flags & DCC_WF_RO))
582 wf->wtbl->hdr.ascii_mtime = 0;
583 sync_white(wf);
584 return FIND_WHITE_BROKEN;
585 }
586
587
588
589 static int /* 1=quit stat_white_nms() */
590 stat_1white_nm(int *resultp, /* set=1 if (supposedly) no change */
591 DCC_WF *wf, const char *nm, time_t ascii_mtime)
592 {
593 struct stat sb;
594 time_t now;
595
596 if (stat(nm, &sb) < 0) {
597 if (errno != ENOENT
598 || !wf->wtbl
599 || (wf->wf_flags & DCC_WF_RO)) {
600 dcc_trace_msg("stat(%s): %s", nm, ERROR_STR());
601 is_broken(wf);
602 *resultp = 0;
603 return 1;
604 }
605
606 /* only complain if an ASCII file disappears temporarily */
607 if (wf->broken == 0) {
608 dcc_trace_msg("%s disappeared: %s", nm, ERROR_STR());
609 is_broken(wf);
610 *resultp = 1;
611 return 1;
612 }
613 now = time(0);
614 if (STILL_BROKE(wf, now)) {
615 if (dcc_clnt_debug > 1)
616 dcc_trace_msg("ignoring stat(%s): %s",
617 nm, ERROR_STR());
618 *resultp = 1;
619 } else {
620 if (dcc_clnt_debug > 1)
621 dcc_trace_msg("pay attention to stat(%s): %s",
622 nm, ERROR_STR());
623 *resultp = 0;
624 }
625 return 1;
626 }
627
628 if (sb.st_mtime != ascii_mtime) {
629 *resultp = 0;
630 return 1;
631 }
632
633 return 0;
634 }
635
636
637
638 /* see if the ASCII files have changed */
639 static int /* 1=same 0=changed or sick -1=broken */
640 stat_white_nms(DCC_EMSG emsg, DCC_WF *wf)
641 {
642 struct stat ht_sb;
643 time_t now;
644 int i, result;
645
646 if (!wf->wtbl)
647 return -1;
648
649 now = time(0);
650 wf->next_stat_time = now + DCC_WHITE_STAT_DELAY;
651
652 /* Notice if the hash file has been unlinked */
653 if (stat(wf->ht_nm, &ht_sb) < 0) {
654 if (emsg && dcc_clnt_debug)
655 dcc_error_msg("stat(%s): %s",
656 wf->ht_nm, ERROR_STR());
657 return -1;
658 }
659 #ifdef DCC_WIN32
660 /* open files cannot be unlinked in WIN32, which lets us not
661 * worry about whether WIN32 files have device and i-numbers */
662 #else
663 if (wf->ht_sb.st_dev != ht_sb.st_dev
664 || wf->ht_sb.st_ino != ht_sb.st_ino) {
665 if (emsg && dcc_clnt_debug > 2)
666 dcc_error_msg("%s disappeared", wf->ht_nm);
667 return -1;
668 }
669 #endif /* DCC_WIN32 */
670 wf->ht_sb = ht_sb;
671
672 /* delays on re-parsing and complaining in the file override */
673 if (wf->reparse < wf->wtbl->hdr.reparse)
674 wf->reparse = wf->wtbl->hdr.reparse;
675 if (wf->broken < wf->wtbl->hdr.broken)
676 wf->broken = wf->wtbl->hdr.broken;
677
678 /* seriously broken hash tables are unusable */
679 if (wf->wtbl->hdr.ascii_mtime == 0)
680 return -1;
681
682 /* pretend things are fine if they are recently badly broken */
683 if (STILL_BROKE(wf, now))
684 return 1;
685
686 /* if the main ASCII file has disappeared,
687 * leave the hash file open and just complain,
688 * but only for a while */
689 result = 1;
690 if (stat_1white_nm(&result, wf, wf->ascii_nm,
691 wf->wtbl->hdr.ascii_mtime))
692 return result;
693 /* see if any of the included ASCII files are new */
694 for (i = 0; i < DIM(wf->wtbl->hdr.white_incs); ++i) {
695 if (wf->wtbl->hdr.white_incs[i].nm[0] == '\0')
696 break;
697 /* stop at the first missing or changed included file */
698 if (stat_1white_nm(&result, wf,
699 wf->wtbl->hdr.white_incs[i].nm,
700 wf->wtbl->hdr.white_incs[i].mtime))
701 return result;
702 }
703
704 /* force periodic reparsing of syntax errors to nag in system log */
705 if (wf->reparse != 0
706 && DCC_IS_TIME(now, wf->reparse, DCC_WHITE_REPARSE_DELAY))
707 return 0;
708
709 if ((wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
710 && !(wf->wf_flags & DCC_WF_PER_USER)) {
711 dcc_error_msg("%s is a per-user whitelist"
712 " used as a global whitelist",
713 wf->ht_nm);
714 return 0;
715 }
716 if (!(wf->wtbl_flags & DCC_WHITE_FG_PER_USER)
717 && (wf->wf_flags & DCC_WF_PER_USER)) {
718 dcc_error_msg("%s is a global whitelist"
719 " used as a per-user whitelist",
720 wf->ht_nm);
721 return 0;
722 }
723
724 /* Checksums of SMTP client IP addresses are compared against the
725 * checksums of the IP addresses of the hostnames in the ASCII file.
726 * Occassionaly check for changes in DNS A RR's for entries in
727 * the ASCII file, but only if there are host names or IP
728 * addresses in the file */
729 if ((wf->wtbl->hdr.flags & DCC_WHITE_FG_HOSTNAMES)
730 && DCC_IS_TIME(now, wf->ht_sb.st_mtime+DCC_WHITECLNT_RESOLVE,
731 DCC_WHITECLNT_RESOLVE)) {
732 if (dcc_clnt_debug > 2)
733 dcc_trace_msg("time to rebuild %s", wf->ht_nm);
734 return 0;
735 }
736
737 return 1;
738 }
739
740
741
742 static u_char
743 write_white(DCC_EMSG emsg, DCC_WF *wf, const void *buf, int buf_len, off_t pos)
744 {
745 int i;
746
747 if (wf->wtbl) {
748 #ifdef DCC_WIN32
749 /* Windows disclaims coherence between ordinary writes
750 * and memory mapped writing. The hash tables are
751 * fixed size on Windows because of problems with WIN32
752 * mapping objects, so we do not need to worry about
753 * extending the hash table file. */
754 memcpy((char *)wf->wtbl+pos, buf, buf_len);
755 return 1;
756 #else
757 /* some UNIX systems have coherence trouble without msync() */
758 if (0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC)) {
759 dcc_pemsg(EX_IOERR, emsg, "msync(%s): %s",
760 wf->ht_nm, ERROR_STR());
761 return 0;
762 }
763 #endif
764 }
765
766 i = lseek(wf->ht_fd, pos, SEEK_SET);
767 if (i < 0) {
768 dcc_pemsg(EX_IOERR, emsg, "lseek(%s,"OFF_DPAT"): %s",
769 wf->ht_nm, pos, ERROR_STR());
770 return 0;
771 }
772 i = write(wf->ht_fd, buf, buf_len);
773 if (i != buf_len) {
774 if (i < 0)
775 dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %s",
776 wf->ht_nm, buf_len, ERROR_STR());
777 else
778 dcc_pemsg(EX_IOERR, emsg, "write(%s,%d): %d",
779 wf->ht_nm, buf_len, i);
780 return 0;
781 }
782 return 1;
783 }
784
785
786
787 static int /* 1=ok, 0=bad entry, -1=fatal */
788 add_white(DCC_EMSG emsg, DCC_WF *wf,
789 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
790 {
791 DCC_WHITE_ENTRY *e, new;
792 DCC_WHITE_INX bin;
793 DCC_FNM_LNO_BUF fnm_buf;
794 off_t end;
795
796 /* find the hash chain for the new entry */
797 e = find_white(emsg, wf, type, sum, &bin);
798 if (e == FIND_WHITE_BROKEN)
799 return -1;
800
801 /* ignore duplicates on this, the first pass */
802 if (e)
803 return 1;
804
805 memset(&new, 0, sizeof(new));
806 new.type = type;
807 memcpy(new.sum, sum, sizeof(DCC_SUM));
808 new.tgts = tgts;
809 new.fwd = wf->wtbl->bins[bin];
810 new.lno = wf->lno;
811 new.fno = wf->fno;
812
813 /* Use a new entry at the end of the file
814 * It will be beyond the memory mapped window into the file */
815 if (wf->wtbl->hdr.entries >= MAX_WHITE_ENTRIES) {
816 dcc_pemsg(EX_DATAERR, emsg, "more than maximum %d entries%s",
817 wf->wtbl->hdr.entries, wf_fnm_lno(&fnm_buf, wf));
818 return 0;
819 }
820 end = ENTRIES2SIZE(wf->wtbl->hdr.entries);
821 wf->wtbl->bins[bin] = ++wf->wtbl->hdr.entries;
822 return write_white(emsg, wf, &new, sizeof(new), end) ? 1 : -1;
823 }
824
825
826
827 #define MAX_CIDR_BITS 7
828
829 static int /* 1=ok, 0=bad entry, -1=fatal */
830 add_white_cidr(DCC_EMSG emsg, DCC_WF *wf,
831 int bits,
832 const struct in6_addr *addrp, const struct in6_addr *maskp,
833 DCC_TGTS tgts)
834 {
835 DCC_WHITE_CIDR_ENTRY *e;
836 struct in6_addr addr;
837 DCC_SUM sum;
838 DCC_FNM_LNO_BUF fnm_buf;
839 int result, i, j;
840
841 /* use individual entries for MAX_CIDR_BITS and smaller blocks */
842 if (128-bits <= MAX_CIDR_BITS) {
843 addr = *addrp;
844 result = 1;
845 for (i = 1 << (128-bits); i > 0; --i) {
846 dcc_ck_ipv6(sum, &addr);
847 j = add_white(emsg, wf, DCC_CK_IP, sum, tgts);
848 if (j <= 0) {
849 if (j < 0)
850 return j;
851 result = j;
852 }
853 addr.s6_addr32[3] = ntohl(addr.s6_addr32[3]);
854 ++addr.s6_addr32[3];
855 addr.s6_addr32[3] = htonl(addr.s6_addr32[3]);
856 }
857 return result;
858 }
859
860 i = wf->wtbl->hdr.cidr.len;
861 for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) {
862 /* ignore collisions on this, the first pass */
863 if (e->bits == bits
864 && !memcmp(addrp, &e->addr, sizeof(*addrp)))
865 return 1;
866 }
867
868 if (wf->wtbl->hdr.cidr.len >= DIM(wf->wtbl->hdr.cidr.e)) {
869 dcc_pemsg(EX_DATAERR, emsg, "too many CIDR blocks%s",
870 wf_fnm_lno(&fnm_buf, wf));
871 return 0;
872 }
873
874 e->bits = bits;
875 e->addr = *addrp;
876 e->mask = *maskp;
877 e->tgts = tgts;
878 e->lno = wf->lno;
879 e->fno = wf->fno;
880 ++wf->wtbl->hdr.cidr.len;
881
882 return 1;
883 }
884
885
886
887 static void
888 dup_msg(DCC_EMSG emsg, DCC_WF *wf, int e_fno, int e_lno,
889 DCC_TGTS e_tgts, DCC_TGTS tgts)
890 {
891 char tgts_buf[30], e_tgts_buf[30];
892 const char *fname1, *fname2;
893
894 fname1 = wf_fnm(wf, wf->fno);
895 fname2 = wf_fnm(wf, e_fno);
896 dcc_pemsg(EX_DATAERR, emsg,
897 "\"%s\" in line %d%s%s conflicts with \"%s\" in line"
898 " %d of %s",
899 dcc_tgts2str(tgts_buf, sizeof(tgts_buf), tgts, 0),
900 wf->lno,
901 fname1 != fname2 ? " of " : "",
902 fname1 != fname2 ? fname1 : "",
903 dcc_tgts2str(e_tgts_buf, sizeof(e_tgts_buf), e_tgts, 0),
904 e_lno,
905 fname2);
906 }
907
908
909
910 static DCC_TGTS
911 combine_white_tgts(DCC_TGTS new, DCC_TGTS old)
912 {
913 if (new < DCC_TGTS_TOO_MANY && old == DCC_TGTS_TOO_MANY)
914 return new;
915
916 if (new == DCC_TGTS_OK || old == DCC_TGTS_OK)
917 return DCC_TGTS_OK;
918 if (new == DCC_TGTS_OK2 || old == DCC_TGTS_OK2)
919 return DCC_TGTS_OK2;
920 if (new == DCC_TGTS_OK_MX || old == DCC_TGTS_OK_MX)
921 return DCC_TGTS_OK_MX;
922 if (new == DCC_TGTS_OK_MXDCC || old == DCC_TGTS_OK_MXDCC)
923 return DCC_TGTS_OK_MXDCC;
924 if (new == DCC_TGTS_TOO_MANY || old == DCC_TGTS_TOO_MANY)
925 return DCC_TGTS_TOO_MANY;
926 if (new == DCC_TGTS_SUBMIT_CLIENT || old == DCC_TGTS_SUBMIT_CLIENT)
927 return DCC_TGTS_SUBMIT_CLIENT;
928
929 return new;
930 }
931
932
933
934 static int /* 1=ok, 0=bad entry, -1=fatal */
935 ck_dup_white(DCC_EMSG emsg, DCC_WF *wf,
936 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts, u_char complain)
937 {
938 DCC_WHITE_ENTRY *e;
939 DCC_FNM_LNO_BUF buf;
940 const char *from;
941
942 /* find the hash table entry */
943 e = find_white(emsg, wf, type, sum, 0);
944 if (e == FIND_WHITE_BROKEN)
945 return -1;
946 if (!e) {
947 /* We failed to find the hash table entry. Either the
948 * hash file is corrupt, the ASCII file is changing beneath
949 * our feet, or a host name that failed to resolve the first
950 * time we parsed the ASCII file is now resolving during
951 * this second parsing. */
952 from = wf_fnm_lno(&buf, wf);
953 if (type == DCC_CK_IP) {
954 if (dcc_clnt_debug > 2)
955 dcc_trace_msg("%s entry (dis)appeared in %s%s",
956 dcc_type2str_err(type, 0, 0, 0),
957 wf->ht_nm,
958 *from == '\0' ? "in ?" : from);
959 return 0;
960 }
961 dcc_pemsg(EX_DATAERR, emsg, "%s entry (dis)appeared in %s%s",
962 dcc_type2str_err(type, 0, 0, 0),
963 wf->ht_nm,
964 *from == '\0' ? "in ?" : from);
965 return -1;
966 }
967
968 /* ignore perfect duplicates */
969 if (e->tgts == tgts)
970 return 1;
971
972 if (complain)
973 dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts);
974
975 if (e->tgts != combine_white_tgts(tgts, e->tgts)) {
976 e->tgts = tgts;
977 e->lno = wf->lno;
978 e->fno = wf->fno;
979 }
980 return 0;
981 }
982
983
984
985 static int /* 1=ok, 0=bad entry, -1=fatal */
986 ck_dup_white_complain(DCC_EMSG emsg, DCC_WF *wf,
987 DCC_CK_TYPES type, DCC_SUM sum, DCC_TGTS tgts)
988 {
989 return ck_dup_white(emsg, wf, type, sum, tgts, 1);
990 }
991
992
993
994 /* Without brute force checks that would take too long, it is impossible to
995 * check that a new CIDR block does not collide with individual entries that
996 * are already in the hash table. Without expanding a big CIDR block into
997 * IP addresses and looking for each in the hash table, how can you check
998 * whether it covers an entry for an individual address? One way is to
999 * parse the file twice and so check each individual IP address against
1000 * the CIDR blocks. */
1001 static int /* 1=ok, 0=bad entry, -1=fatal */
1002 ck_dup_white_cidr(DCC_EMSG emsg UATTRIB, DCC_WF *wf,
1003 int bits,
1004 const struct in6_addr *addrp, const struct in6_addr *maskp,
1005 DCC_TGTS tgts)
1006 {
1007 DCC_WHITE_CIDR_ENTRY *e;
1008 struct in6_addr addr;
1009 DCC_SUM sum;
1010 int result, i, j;
1011
1012 result = 1;
1013
1014 /* Check for collisions between CIDR blocks and either other
1015 * CIDR blocks or individual addresses. Individual addresses
1016 * are sent here as /128 blocks. */
1017 i = wf->wtbl->hdr.cidr.len;
1018 for (e = wf->wtbl->hdr.cidr.e; i > 0; ++e, --i) {
1019 if (!DCC_IN_BLOCK(e->addr, *addrp, *maskp)
1020 && !DCC_IN_BLOCK(*addrp, e->addr, e->mask))
1021 continue;
1022
1023 /* ignore simple duplicates */
1024 if (e->tgts == tgts)
1025 continue;
1026
1027 dup_msg(emsg, wf, e->fno, e->lno, e->tgts, tgts);
1028 result = 0;
1029
1030 /* fix direct collisions */
1031 if (e->bits == bits
1032 && !memcmp(addrp, &e->addr, sizeof(*addrp))
1033 && e->tgts != combine_white_tgts(tgts, e->tgts)) {
1034 e->tgts = tgts;
1035 e->lno = wf->lno;
1036 e->fno = wf->fno;
1037 }
1038 }
1039
1040 /* check and fix collisions among individual entries */
1041 if (128-bits <= MAX_CIDR_BITS) {
1042 addr = *addrp;
1043 for (i = 1 << (128-bits); i > 0; --i) {
1044 dcc_ck_ipv6(sum, &addr);
1045 j = ck_dup_white(emsg, wf, DCC_CK_IP, sum, tgts,
1046 result > 0);
1047 if (j < 0)
1048 return j;
1049 if (j == 0)
1050 result = j;
1051 addr.s6_addr32[3] = htonl(1+ntohl(addr.s6_addr32[3]));
1052 }
1053 }
1054
1055 return result;
1056 }
1057
1058
1059
1060 /* bail out on creating a whiteclnt hash table file */
1061 static DCC_WHITE_RESULT
1062 make_white_hash_bail(DCC_WHITE_RESULT result, DCC_WF *wf, DCC_WF *tmp_wf)
1063 {
1064 if (tmp_wf->ht_nm[0] != '\0') {
1065 unlink_white_ht(tmp_wf, 0);
1066 tmp_wf->ht_nm[0] = '\0';
1067 }
1068
1069 /* we have a usable file open */
1070 if (wf->wtbl)
1071 return DCC_WHITE_CONTINUE;
1072 return result;
1073 }
1074
1075
1076
1077 /* (re)create the hash table file */
1078 static DCC_WHITE_RESULT
1079 make_white_hash(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf)
1080 {
1081 static const u_char zero = 0;
1082 int ascii_fd;
1083 struct stat ascii_sb;
1084 DCC_PATH path;
1085 DCC_WHITE_RESULT result;
1086 DCC_CK_TYPES type;
1087 int part_result, i;
1088
1089 if (dcc_clnt_debug > 2)
1090 dcc_trace_msg("start parsing %s", wf->ascii_nm);
1091
1092 /* Do not wait to create a new, locked hash table file if we have
1093 * a usable file. Assume some other process is re-parsing
1094 * the ASCII file.
1095 * If we should wait to create a new file, then we don't have a
1096 * usable hash table and so there is no reason to unlock the
1097 * DCC_WF structure. */
1098
1099 #ifdef DCC_DEBUG_CLNT_LOCK
1100 if (tmp_wf == &cmn_tmp_wf)
1101 assert_cwf_locked();
1102 #endif
1103
1104 dcc_wf_init(tmp_wf, wf->wf_flags);
1105
1106 if (0 > stat(wf->ascii_nm, &ascii_sb)) {
1107 result = ((errno == ENOENT || errno == ENOTDIR)
1108 ? DCC_WHITE_NOFILE
1109 : DCC_WHITE_COMPLAIN);
1110
1111 dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
1112 wf->ascii_nm, ERROR_STR());
1113
1114 /* Delete the hash table if the ASCII file has disappeared.
1115 * stat_white_nms() delays forcing a re-build if the ASCII
1116 * file disappears only temporarily */
1117 if (wf->ht_nm[0] != '\0') {
1118 close_white_ht(0, wf);
1119 i = unlink(wf->ht_nm);
1120 if (i < 0) {
1121 if (errno != ENOENT && errno != ENOTDIR)
1122 dcc_trace_msg("%s missing,:unlink(%s):"
1123 " %s",
1124 wf->ascii_nm,
1125 fnm2abs_err(path,
1126 wf->ht_nm),
1127 ERROR_STR());
1128 } else if (dcc_clnt_debug > 1) {
1129 dcc_trace_msg("delete %s after %s missing",
1130 fnm2abs_err(path, wf->ht_nm),
1131 wf->ascii_nm);
1132 }
1133 }
1134 return make_white_hash_bail(result, wf, tmp_wf);
1135 }
1136
1137 part_result = create_white_ht(emsg, tmp_wf, wf);
1138 if (part_result == 0) {
1139 /* The new hash table file is busy.
1140 * Do not complain if we have a usable open hash table file */
1141 if (!dcc_clnt_debug && wf->wtbl)
1142 return DCC_WHITE_OK;
1143 /* at least ignore the need to reparse */
1144 return DCC_WHITE_CONTINUE;
1145 }
1146 if (part_result < 0)
1147 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1148
1149 /* clear the new hash table */
1150 if (!write_white(emsg, tmp_wf, white_magic, sizeof(white_magic), 0)
1151 || !write_white(emsg, tmp_wf, &zero, 1, EMPTY_WHITE_SIZE-1)
1152 || !map_white_ht(emsg, tmp_wf, 0))
1153 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1154 if (tmp_wf->wf_flags & DCC_WF_PER_USER)
1155 tmp_wf->wtbl->hdr.flags = DCC_WHITE_FG_PER_USER;
1156 tmp_wf->wtbl_flags = tmp_wf->wtbl->hdr.flags;
1157 for (type = 0; type <= DCC_CK_TYPE_LAST; ++type)
1158 tmp_wf->wtbl->hdr.tholds_rej[type] = DCC_THOLD_UNSET;
1159
1160 ascii_fd = dcc_lock_open(emsg, tmp_wf->ascii_nm, O_RDONLY,
1161 DCC_LOCK_OPEN_NOWAIT | DCC_LOCK_OPEN_SHARE,
1162 DCC_LOCK_ALL_FILE, 0);
1163 if (ascii_fd == -1)
1164 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1165
1166 tmp_wf->wtbl->hdr.ascii_mtime = ascii_sb.st_mtime;
1167 part_result = dcc_parse_whitefile(emsg, tmp_wf, ascii_fd,
1168 add_white, add_white_cidr);
1169 if (part_result > 0) {
1170 /* parse again to detect colliding definitions among
1171 * host names and CIDR blocks */
1172 if (0 != lseek(ascii_fd, 0, SEEK_SET)) {
1173 dcc_pemsg(EX_IOERR, emsg, "lseek(%s, 0, SEEK_SET): %s",
1174 wf->ascii_nm, ERROR_STR());
1175 part_result = -1;
1176 } else {
1177 part_result = dcc_parse_whitefile(emsg, tmp_wf,
1178 ascii_fd,
1179 ck_dup_white_complain,
1180 ck_dup_white_cidr);
1181 }
1182 }
1183
1184 /* if the hash table is tolerable, compute a checksum of it
1185 * to detect differing per-user hash tables */
1186 if (part_result >= 0
1187 && map_white_ht(emsg, tmp_wf, tmp_wf->wtbl->hdr.entries)) {
1188 MD5_CTX ctx;
1189
1190 MD5Init(&ctx);
1191 MD5Update(&ctx, (u_char *)&tmp_wf->wtbl->hdr.cidr,
1192 sizeof(tmp_wf->wtbl->hdr.cidr));
1193 i = tmp_wf->wtbl->hdr.entries;
1194 while (--i >= 0) {
1195 MD5Update(&ctx, &tmp_wf->wtbl->tbl[i].type,
1196 sizeof(tmp_wf->wtbl->tbl[i].type));
1197 MD5Update(&ctx, tmp_wf->wtbl->tbl[i].sum,
1198 sizeof(tmp_wf->wtbl->tbl[i].sum));
1199 }
1200 MD5Final(tmp_wf->wtbl->hdr.ck_sum, &ctx);
1201 }
1202 #ifdef DCC_WIN32
1203 /* unlock the file before closing it to keep Win95 happy */
1204 dcc_unlock_fd(0, ascii_fd, DCC_LOCK_ALL_FILE,
1205 "whitelist ", tmp_wf->ascii_nm);
1206 #endif
1207 if (close(ascii_fd) < 0)
1208 dcc_trace_msg("close(%s): %s", wf->ascii_nm, ERROR_STR());
1209 if (part_result < 0)
1210 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1211 result = (part_result > 0) ? DCC_WHITE_OK : DCC_WHITE_CONTINUE;
1212
1213 /* ensure continued complaints about errors */
1214 if (result == DCC_WHITE_CONTINUE)
1215 tmp_wf->wtbl->hdr.reparse = time(0) + DCC_WHITE_REPARSE_DELAY;
1216 sync_white(tmp_wf);
1217
1218 #ifdef DCC_WIN32
1219 /* WIN32 prohibits renaming open files
1220 * and there is little or no concurrency on WIN32 DCC clients
1221 * So lock the original file and copy to it. */
1222 close_white_ht(0, wf);
1223 wf->ht_fd = dcc_lock_open(emsg, wf->ht_nm, O_RDWR | O_CREAT,
1224 0, DCC_LOCK_ALL_FILE, 0);
1225 if (wf->ht_fd < 0)
1226 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1227 if (!map_white_ht(emsg, wf, tmp_wf->wtbl_entries))
1228 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1229 memcpy(wf->wtbl, tmp_wf->wtbl, tmp_wf->wtbl_size);
1230 close_white_ht(emsg, tmp_wf);
1231 unlink(tmp_wf->ht_nm);
1232 #else
1233 part_result = rename(tmp_wf->ht_nm, wf->ht_nm);
1234 if (0 > part_result
1235 && dcc_get_priv_home(wf->ht_nm)) {
1236 part_result = rename(tmp_wf->ht_nm, wf->ht_nm);
1237 dcc_rel_priv();
1238 }
1239 if (0 > part_result) {
1240 dcc_pemsg(EX_IOERR, emsg, "rename(%s, %s): %s",
1241 tmp_wf->ht_nm, wf->ht_nm, ERROR_STR());
1242 return make_white_hash_bail(DCC_WHITE_COMPLAIN, wf, tmp_wf);
1243 }
1244 #endif
1245
1246 close_white_ht(0, tmp_wf);
1247 if (dcc_clnt_debug > 1)
1248 dcc_trace_msg("finished parsing %s", wf->ascii_nm);
1249 part_result = open_white_ht(emsg, wf);
1250 if (part_result < 0)
1251 result = DCC_WHITE_COMPLAIN;
1252 else if (part_result == 0 && result == DCC_WHITE_OK)
1253 result = DCC_WHITE_CONTINUE;
1254
1255 wf->next_stat_time = time(0) + DCC_WHITE_STAT_DELAY;
1256
1257 return result;
1258 }
1259
1260
1261
1262 /* see that a local whitelist is ready
1263 * on failure the file is not locked
1264 * The caller must lock the DCC_WF if necessary */
1265 DCC_WHITE_RESULT
1266 dcc_rdy_white(DCC_EMSG emsg, DCC_WF *wf, DCC_WF *tmp_wf)
1267 {
1268 time_t now;
1269 int i;
1270
1271 if (wf->need_reopen) {
1272 /* The resolver thread has change the hash table in the file
1273 * and possibly renamed it.
1274 * We need to re-open the hash table */
1275 wf->broken = 0;
1276 wf->reparse = 0;
1277 close_white_ht(0, wf);
1278 }
1279
1280 now = time(0);
1281 if (emsg)
1282 *emsg = '\0';
1283
1284 if (wf->ht_fd >= 0) {
1285 /* The hash table is open.
1286 * If we have checked recently, assume everything is good. */
1287 if (!DCC_IS_TIME(now, wf->next_stat_time, DCC_WHITE_STAT_DELAY))
1288 return DCC_WHITE_OK;
1289 i = stat_white_nms(emsg, wf);
1290 if (i > 0)
1291 return DCC_WHITE_OK;
1292 if (i < 0) {
1293 /* Things are broken or not open, so try to open the
1294 * hash table.
1295 * We may be racing here. Be happy if another process
1296 * fixes the hash table while we stall trying to open
1297 * it locked. */
1298 i = open_white_ht(emsg, wf);
1299 if (i < 0)
1300 return DCC_WHITE_COMPLAIN;
1301 if (i > 0)
1302 i = stat_white_nms(emsg, wf);
1303 }
1304
1305 } else {
1306 if (wf->ascii_nm[0] == '\0') {
1307 dcc_pemsg(EX_NOINPUT, emsg, "no whitelist");
1308 return DCC_WHITE_NOFILE;
1309 }
1310
1311 if (STILL_BROKE(wf, now)) {
1312 /* only check for a missing file occassionally */
1313 if (wf->wf_flags & DCC_WF_NOFILE) {
1314 dcc_pemsg(EX_NOINPUT, emsg, "%s does not exist",
1315 wf->ascii_nm);
1316 return DCC_WHITE_NOFILE;
1317 }
1318
1319 /* If things are broken, and it has not been a while,
1320 * then assume things are still broken. */
1321 dcc_pemsg(EX_DATAERR, emsg,
1322 "%s still broken", wf->ascii_nm);
1323 return (dcc_clnt_debug > 2
1324 ? DCC_WHITE_COMPLAIN
1325 : DCC_WHITE_SILENT);
1326 }
1327
1328 i = open_white_ht(emsg, wf);
1329 if (i < 0)
1330 return DCC_WHITE_COMPLAIN;
1331 if (i > 0)
1332 i = stat_white_nms(emsg, wf);
1333 }
1334
1335 if (i > 0)
1336 return DCC_WHITE_OK;
1337
1338 /* Try to let the resolver thread wait for the DNS chitchat
1339 * for host names that might now be in the ASCII file.
1340 * To avoid racing with the resolver thread to delete the main
1341 * hash files, fail if there is no hash table and this is not
1342 * the resolver thread. */
1343 if (i == 0
1344 && !(wf->wf_flags & DCC_WF_PER_USER)
1345 && dcc_clnt_wake_resolve()) {
1346 if (wf->wtbl)
1347 return DCC_WHITE_OK;
1348 return (dcc_clnt_debug > 2
1349 ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT);
1350 }
1351
1352 if (STILL_BROKE(wf, now)) {
1353 dcc_pemsg(EX_DATAERR, emsg, "%s still broken", wf->ascii_nm);
1354 if (i == 0 && wf->wtbl)
1355 return (dcc_clnt_debug > 2
1356 ? DCC_WHITE_CONTINUE : DCC_WHITE_SILENT);
1357 return (dcc_clnt_debug > 2
1358 ? DCC_WHITE_COMPLAIN : DCC_WHITE_SILENT);
1359 }
1360
1361 if (emsg && *emsg != '\0') {
1362 if (i < 0) {
1363 dcc_error_msg("%s", emsg);
1364 } else if (dcc_clnt_debug > 2) {
1365 dcc_trace_msg("%s", emsg);
1366 }
1367 *emsg = '\0';
1368 }
1369
1370 switch (make_white_hash(emsg, wf, tmp_wf)) {
1371 case DCC_WHITE_OK:
1372 wf->wf_flags &= ~DCC_WF_NOFILE;
1373 return DCC_WHITE_OK; /* all is good */
1374 case DCC_WHITE_CONTINUE: /* minor syntax error or bad hostname */
1375 wf->wf_flags &= ~DCC_WF_NOFILE;
1376 return DCC_WHITE_CONTINUE;
1377 case DCC_WHITE_SILENT: /* no new message */
1378 wf->wf_flags &= ~DCC_WF_NOFILE;
1379 return DCC_WHITE_CONTINUE;
1380 case DCC_WHITE_NOFILE:
1381 wf->wf_flags |= DCC_WF_NOFILE;
1382 return DCC_WHITE_NOFILE;
1383 case DCC_WHITE_COMPLAIN:
1384 default:
1385 is_broken(wf);
1386 wf->wf_flags &= ~DCC_WF_NOFILE;
1387 return DCC_WHITE_COMPLAIN;
1388 }
1389 }
1390
1391
1392
1393 static u_char
1394 lookup_white(DCC_EMSG emsg,
1395 DCC_TGTS *tgtsp, /* value if hit else DCC_TGTS_INVALID */
1396 DCC_WF *wf,
1397 const DCC_GOT_CKS *cks, const DCC_GOT_SUM *g)
1398 {
1399 const DCC_WHITE_ENTRY *e;
1400 const DCC_WHITE_CIDR_ENTRY *cidrp;
1401 int bits;
1402
1403 e = find_white(emsg, wf, g->type, g->sum, 0);
1404 if (e == FIND_WHITE_BROKEN) {
1405 *tgtsp = DCC_TGTS_OK;
1406 return 0;
1407 }
1408
1409 if (!e) {
1410 if (g->type != DCC_CK_IP) {
1411 *tgtsp = DCC_TGTS_INVALID;
1412 return 1;
1413 }
1414
1415 /* if we had no hit and it is an IP address,
1416 * check the CIDR blocks */
1417 bits = 0;
1418 cidrp = &wf->wtbl->hdr.cidr.e[wf->wtbl->hdr.cidr.len];
1419 while (cidrp != wf->wtbl->hdr.cidr.e) {
1420 --cidrp;
1421 /* look for the longest match */
1422 if (cidrp->bits <= bits)
1423 continue;
1424 if (DCC_IN_BLOCK(cks->ip_addr,
1425 cidrp->addr, cidrp->mask)) {
1426 *tgtsp = cidrp->tgts;
1427 bits = cidrp->bits;
1428 }
1429 }
1430 if (bits == 0)
1431 *tgtsp = DCC_TGTS_INVALID;
1432 return 1;
1433 }
1434
1435 *tgtsp = e->tgts;
1436 return 1;
1437 }
1438
1439
1440
1441 /* check a local whitelist for a single checksum
1442 * on exit the file is locked except after an error */
1443 DCC_WHITE_RESULT
1444 dcc_white_sum(DCC_EMSG emsg,
1445 DCC_WF *wf, /* in this whitelist */
1446 DCC_CK_TYPES type, const DCC_SUM sum, /* look for this checksum */
1447 DCC_TGTS *tgtsp, /* set only if we find the checksum */
1448 DCC_WHITE_LISTING *listingp)
1449 {
1450 DCC_WHITE_ENTRY *e;
1451 DCC_WHITE_RESULT result;
1452
1453 result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf);
1454 switch (result) {
1455 case DCC_WHITE_OK:
1456 case DCC_WHITE_CONTINUE:
1457 break;
1458 case DCC_WHITE_SILENT:
1459 case DCC_WHITE_NOFILE:
1460 case DCC_WHITE_COMPLAIN:
1461 *listingp = DCC_WHITE_RESULT_FAILURE;
1462 return result;
1463 }
1464
1465 e = find_white(emsg, wf, type, sum, 0);
1466 if (e == FIND_WHITE_BROKEN) {
1467 *listingp = DCC_WHITE_RESULT_FAILURE;
1468 return DCC_WHITE_COMPLAIN;
1469 }
1470
1471 if (!e) {
1472 *listingp = DCC_WHITE_UNLISTED;
1473 } else if (e->tgts == DCC_TGTS_OK2
1474 && type == DCC_CK_ENV_TO) {
1475 /* deprecated mechanism for turn DCC checks on and off for
1476 * individual targets */
1477 *tgtsp = e->tgts;
1478 *listingp = DCC_WHITE_USE_DCC;
1479 } else if (e->tgts == DCC_TGTS_OK) {
1480 *tgtsp = e->tgts;
1481 *listingp = DCC_WHITE_LISTED;
1482 } else if (e->tgts == DCC_TGTS_TOO_MANY) {
1483 *tgtsp = e->tgts;
1484 *listingp = DCC_WHITE_BLACK;
1485 } else {
1486 *listingp = DCC_WHITE_UNLISTED;
1487 }
1488
1489 return result;
1490 }
1491
1492
1493
1494 /* see if an IP addess is that of one of our MX servers
1495 * the caller must lock cmn_wf if necessray */
1496 u_char /* 0=problems */
1497 dcc_white_mx(DCC_EMSG emsg,
1498 DCC_TGTS *tgtsp, /* !=0 if listed */
1499 const DCC_GOT_CKS *cks) /* this IP address checksum */
1500 {
1501 u_char result;
1502
1503 result = 1;
1504 switch (dcc_rdy_white(emsg, &cmn_wf, &cmn_tmp_wf)) {
1505 case DCC_WHITE_OK:
1506 break;
1507 case DCC_WHITE_CONTINUE:
1508 result = 0;
1509 break;
1510 case DCC_WHITE_NOFILE:
1511 *tgtsp = 0;
1512 return 1;
1513 case DCC_WHITE_SILENT:
1514 case DCC_WHITE_COMPLAIN:
1515 *tgtsp = 0;
1516 return 0;
1517 }
1518
1519 if (cks->sums[DCC_CK_IP].type != DCC_CK_IP) {
1520 *tgtsp = 0;
1521 return result;
1522 }
1523
1524 if (!lookup_white(emsg, tgtsp, &cmn_wf, cks, &cks->sums[DCC_CK_IP]))
1525 return 0;
1526
1527 return result;
1528 }
1529
1530
1531
1532 /* See what a local whitelist file says about the checksums for a message.
1533 * The message is whitelisted if at least one checksum is in the local
1534 * whitelist or if there are two or more OK2 values.
1535 * Otherwise it is blacklisted if at least one checksum is.
1536 * The caller must lock the DCC_WF if necessary. */
1537 DCC_WHITE_RESULT /* whether the lookup worked */
1538 dcc_white_cks(DCC_EMSG emsg, DCC_WF *wf,
1539 DCC_GOT_CKS *cks, /* these checksums */
1540 DCC_CKS_WTGTS wtgts, /* whitelist targets, each cks->sums */
1541 DCC_WHITE_LISTING *listingp) /* the answer found */
1542 {
1543 const DCC_GOT_SUM *g;
1544 int inx;
1545 DCC_TGTS tgts, prev_tgts;
1546 DCC_WHITE_RESULT result;
1547
1548 result = dcc_rdy_white(emsg, wf, &cmn_tmp_wf);
1549 switch (result) {
1550 case DCC_WHITE_OK:
1551 case DCC_WHITE_CONTINUE:
1552 break;
1553 case DCC_WHITE_NOFILE:
1554 case DCC_WHITE_COMPLAIN:
1555 case DCC_WHITE_SILENT:
1556 *listingp = DCC_WHITE_RESULT_FAILURE;
1557 return result;
1558 }
1559
1560 /* look for each checksum in the hash file */
1561 *listingp = DCC_WHITE_UNLISTED;
1562 prev_tgts = DCC_TGTS_INVALID;
1563 for (g = &cks->sums[inx = DCC_CK_TYPE_FIRST];
1564 g <= LAST(cks->sums);
1565 ++g, ++inx) {
1566 /* ignore checksums we don't have */
1567 if (g->type == DCC_CK_INVALID)
1568 continue;
1569
1570 if (!lookup_white(emsg, &tgts, wf, cks, g)) {
1571 *listingp = DCC_WHITE_RESULT_FAILURE;
1572 return DCC_WHITE_COMPLAIN;
1573 }
1574 if (tgts == DCC_TGTS_INVALID) {
1575 /* report any body checksums as spam for a spam trap */
1576 if ((wf->wtbl_flags & DCC_WHITE_FG_TRAPS)
1577 && DCC_CK_IS_BODY(g->type))
1578 tgts = DCC_TGTS_TOO_MANY;
1579 else
1580 continue;
1581 }
1582
1583 if (wtgts)
1584 wtgts[inx] = tgts;
1585
1586 if (tgts == DCC_TGTS_OK) {
1587 /* found the checksum in our whitelist,
1588 * so we have the answer */
1589 *listingp = DCC_WHITE_LISTED;
1590
1591 } else if (tgts == DCC_TGTS_OK2) {
1592 if (prev_tgts == DCC_TGTS_OK2) {
1593 /* two half-white checksums count the same
1594 * as a single pure white checksum
1595 * and gives the answer */
1596 *listingp = DCC_WHITE_LISTED;
1597 continue;
1598 }
1599 prev_tgts = DCC_TGTS_OK2;
1600
1601 } else if (tgts == DCC_TGTS_TOO_MANY) {
1602 if (*listingp == DCC_WHITE_UNLISTED)
1603 *listingp = DCC_WHITE_BLACK;
1604 }
1605 }
1606
1607 return result;
1608 }