Bug Summary

File:src/libexec/spamd/grey.c
Warning:line 558, column 3
Null pointer passed as 1st argument to memory copy function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name grey.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/libexec/spamd/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/libexec/spamd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/libexec/spamd/grey.c
1/* $OpenBSD: grey.c,v 1.66 2018/10/25 06:42:35 mestre Exp $ */
2
3/*
4 * Copyright (c) 2004-2006 Bob Beck. All rights reserved.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/ioctl.h>
22#include <sys/wait.h>
23#include <net/if.h>
24#include <netinet/in.h>
25#include <net/pfvar.h>
26#include <ctype.h>
27#include <db.h>
28#include <errno(*__errno()).h>
29#include <fcntl.h>
30#include <pwd.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <syslog.h>
36#include <time.h>
37#include <unistd.h>
38#include <netdb.h>
39
40#include "grey.h"
41#include "sync.h"
42
43extern time_t passtime, greyexp, whiteexp, trapexp;
44extern struct syslog_data sdata;
45extern struct passwd *pw;
46extern u_short cfg_port;
47extern pid_t jail_pid;
48extern FILE *trapcfg;
49extern FILE *grey;
50extern int debug;
51extern int syncsend;
52extern int greyback[2];
53
54/* From netinet/in.h, but only _KERNEL_ gets them. */
55#define satosin(sa)((struct sockaddr_in *)(sa)) ((struct sockaddr_in *)(sa))
56#define satosin6(sa)((struct sockaddr_in6 *)(sa)) ((struct sockaddr_in6 *)(sa))
57
58void configure_spamd(char **, u_int, FILE *);
59int configure_pf(char **, int);
60char *dequotetolower(const char *);
61void readsuffixlists(void);
62void freeaddrlists(void);
63int addwhiteaddr(char *);
64int addtrapaddr(char *);
65int db_addrstate(DB *, char *);
66int greyscan(char *);
67int trapcheck(DB *, char *);
68int twupdate(char *, char *, char *, char *, char *);
69int twread(char *);
70int greyreader(void);
71void greyscanner(void);
72
73
74u_int whitecount, whitealloc;
75u_int trapcount, trapalloc;
76char **whitelist;
77char **traplist;
78
79char *traplist_name = "spamd-greytrap";
80char *traplist_msg = "\"Your address %A has mailed to spamtraps here\\n\"";
81
82pid_t db_pid = -1;
83int pfdev;
84
85struct db_change {
86 SLIST_ENTRY(db_change)struct { struct db_change *sle_next; } entry;
87 char * key;
88 void * data;
89 size_t dsiz;
90 int act;
91};
92
93#define DBC_ADD1 1
94#define DBC_DEL2 2
95
96/* db pending changes list */
97SLIST_HEAD(, db_change)struct { struct db_change *slh_first; } db_changes = SLIST_HEAD_INITIALIZER(db_changes){ ((void *)0) };
98
99struct mail_addr {
100 SLIST_ENTRY(mail_addr)struct { struct mail_addr *sle_next; } entry;
101 char addr[MAX_MAIL1024];
102};
103
104/* list of suffixes that must match TO: */
105SLIST_HEAD(, mail_addr)struct { struct mail_addr *slh_first; } match_suffix = SLIST_HEAD_INITIALIZER(match_suffix){ ((void *)0) };
106char *alloweddomains_file = PATH_SPAMD_ALLOWEDDOMAINS"/etc/mail/spamd.alloweddomains";
107
108char *low_prio_mx_ip;
109time_t startup;
110
111static char *pargv[11]= {
112 "pfctl", "-p", "/dev/pf", "-q", "-t",
113 "spamd-white", "-T", "replace", "-f", "-", NULL((void *)0)
114};
115
116/* If the parent gets a signal, kill off the children and exit */
117/* ARGSUSED */
118static void
119sig_term_chld(int sig)
120{
121 if (db_pid != -1)
122 kill(db_pid, SIGTERM15);
123 if (jail_pid != -1)
124 kill(jail_pid, SIGTERM15);
125 _exit(1);
126}
127
128/*
129 * Greatly simplified version from spamd_setup.c - only
130 * sends one blacklist to an already open stream. Has no need
131 * to collapse cidr ranges since these are only ever single
132 * host hits.
133 */
134void
135configure_spamd(char **addrs, u_int count, FILE *sdc)
136{
137 u_int i;
138
139 /* XXX - doesn't support IPV6 yet */
140 fprintf(sdc, "%s;", traplist_name);
141 if (count != 0) {
142 fprintf(sdc, "%s;inet;%u", traplist_msg, count);
143 for (i = 0; i < count; i++)
144 fprintf(sdc, ";%s/32", addrs[i]);
145 }
146 fputc('\n', sdc);
147 if (fflush(sdc) == EOF(-1))
148 syslog_r(LOG_DEBUG7, &sdata, "configure_spamd: fflush failed (%m)");
149}
150
151int
152configure_pf(char **addrs, int count)
153{
154 FILE *pf = NULL((void *)0);
155 int i, pdes[2], status;
156 pid_t pid;
157 char *fdpath;
158 struct sigaction sa;
159
160 sigfillset(&sa.sa_mask);
161 sa.sa_flags = SA_RESTART0x0002;
162 sa.sa_handler__sigaction_u.__sa_handler = sig_term_chld;
163
164 if (debug)
165 fprintf(stderr(&__sF[2]), "configure_pf - device on fd %d\n", pfdev);
166
167 /* Because /dev/fd/ only contains device nodes for 0-63 */
168 if (pfdev < 1 || pfdev > 63)
169 return(-1);
170
171 if (asprintf(&fdpath, "/dev/fd/%d", pfdev) == -1)
172 return(-1);
173 pargv[2] = fdpath;
174 if (pipe(pdes) != 0) {
175 syslog_r(LOG_INFO6, &sdata, "pipe failed (%m)");
176 free(fdpath);
177 fdpath = NULL((void *)0);
178 return(-1);
179 }
180 signal(SIGCHLD20, SIG_DFL(void (*)(int))0);
181 switch (pid = fork()) {
182 case -1:
183 syslog_r(LOG_INFO6, &sdata, "fork failed (%m)");
184 free(fdpath);
185 fdpath = NULL((void *)0);
186 close(pdes[0]);
187 close(pdes[1]);
188 sigaction(SIGCHLD20, &sa, NULL((void *)0));
189 return(-1);
190 case 0:
191 /* child */
192 close(pdes[1]);
193 if (pdes[0] != STDIN_FILENO0) {
194 dup2(pdes[0], STDIN_FILENO0);
195 close(pdes[0]);
196 }
197 execvp(PATH_PFCTL"/sbin/pfctl", pargv);
198 syslog_r(LOG_ERR3, &sdata, "can't exec %s:%m", PATH_PFCTL"/sbin/pfctl");
199 _exit(1);
200 }
201
202 /* parent */
203 free(fdpath);
204 fdpath = NULL((void *)0);
205 close(pdes[0]);
206 pf = fdopen(pdes[1], "w");
207 if (pf == NULL((void *)0)) {
208 syslog_r(LOG_INFO6, &sdata, "fdopen failed (%m)");
209 close(pdes[1]);
210 sigaction(SIGCHLD20, &sa, NULL((void *)0));
211 return(-1);
212 }
213 for (i = 0; i < count; i++)
214 if (addrs[i] != NULL((void *)0))
215 fprintf(pf, "%s/32\n", addrs[i]);
216 fclose(pf);
217
218 waitpid(pid, &status, 0);
219 if (WIFEXITED(status)(((status) & 0177) == 0) && WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) != 0)
220 syslog_r(LOG_ERR3, &sdata, "%s returned status %d", PATH_PFCTL"/sbin/pfctl",
221 WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
222 else if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177
) != 0)
)
223 syslog_r(LOG_ERR3, &sdata, "%s died on signal %d", PATH_PFCTL"/sbin/pfctl",
224 WTERMSIG(status)(((status) & 0177)));
225
226 sigaction(SIGCHLD20, &sa, NULL((void *)0));
227 return(0);
228}
229
230char *
231dequotetolower(const char *addr)
232{
233 static char buf[MAX_MAIL1024];
234 char *cp;
235
236 if (*addr == '<')
237 addr++;
238 (void) strlcpy(buf, addr, sizeof(buf));
239 cp = strrchr(buf, '>');
240 if (cp != NULL((void *)0) && cp[1] == '\0')
241 *cp = '\0';
242 cp = buf;
243 while (*cp != '\0') {
244 *cp = tolower((unsigned char)*cp);
245 cp++;
246 }
247 return(buf);
248}
249
250void
251readsuffixlists(void)
252{
253 FILE *fp;
254 char *buf;
255 size_t len;
256 struct mail_addr *m;
257
258 while (!SLIST_EMPTY(&match_suffix)(((&match_suffix)->slh_first) == ((void *)0))) {
259 m = SLIST_FIRST(&match_suffix)((&match_suffix)->slh_first);
260 SLIST_REMOVE_HEAD(&match_suffix, entry)do { (&match_suffix)->slh_first = (&match_suffix)->
slh_first->entry.sle_next; } while (0)
;
261 free(m);
262 }
263 if ((fp = fopen(alloweddomains_file, "r")) != NULL((void *)0)) {
264 while ((buf = fgetln(fp, &len))) {
265 /* strip white space-characters */
266 while (len > 0 && isspace((unsigned char)buf[len-1]))
267 len--;
268 while (len > 0 && isspace((unsigned char)*buf)) {
269 buf++;
270 len--;
271 }
272 if (len == 0)
273 continue;
274 /* jump over comments and blank lines */
275 if (*buf == '#' || *buf == '\n')
276 continue;
277 if (buf[len-1] == '\n')
278 len--;
279 if ((len + 1) > sizeof(m->addr)) {
280 syslog_r(LOG_ERR3, &sdata,
281 "line too long in %s - file ignored",
282 alloweddomains_file);
283 goto bad;
284 }
285 if ((m = malloc(sizeof(struct mail_addr))) == NULL((void *)0))
286 goto bad;
287 memcpy(m->addr, buf, len);
288 m->addr[len]='\0';
289 syslog_r(LOG_ERR3, &sdata, "got suffix %s", m->addr);
290 SLIST_INSERT_HEAD(&match_suffix, m, entry)do { (m)->entry.sle_next = (&match_suffix)->slh_first
; (&match_suffix)->slh_first = (m); } while (0)
;
291 }
292 }
293 return;
294bad:
295 while (!SLIST_EMPTY(&match_suffix)(((&match_suffix)->slh_first) == ((void *)0))) {
296 m = SLIST_FIRST(&match_suffix)((&match_suffix)->slh_first);
297 SLIST_REMOVE_HEAD(&match_suffix, entry)do { (&match_suffix)->slh_first = (&match_suffix)->
slh_first->entry.sle_next; } while (0)
;
298 free(m);
299 }
300}
301
302void
303freeaddrlists(void)
304{
305 int i;
306
307 if (whitelist != NULL((void *)0))
308 for (i = 0; i < whitecount; i++) {
309 free(whitelist[i]);
310 whitelist[i] = NULL((void *)0);
311 }
312 whitecount = 0;
313 if (traplist != NULL((void *)0)) {
314 for (i = 0; i < trapcount; i++) {
315 free(traplist[i]);
316 traplist[i] = NULL((void *)0);
317 }
318 }
319 trapcount = 0;
320}
321
322/* validate, then add to list of addrs to whitelist */
323int
324addwhiteaddr(char *addr)
325{
326 struct addrinfo hints, *res;
327 char ch;
328
329 memset(&hints, 0, sizeof(hints));
330 hints.ai_family = AF_INET2; /*for now*/
331 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
332 hints.ai_protocol = IPPROTO_UDP17; /*dummy*/
333 hints.ai_flags = AI_NUMERICHOST4;
334
335 if (getaddrinfo(addr, NULL((void *)0), &hints, &res) != 0)
336 return(-1);
337
338 /* Check spamd blacklists in main process. */
339 if (send(greyback[0], res->ai_addr, res->ai_addr->sa_len, 0) == -1) {
340 syslog_r(LOG_ERR3, &sdata, "%s: send: %m", __func__);
341 } else {
342 if (recv(greyback[0], &ch, sizeof(ch), 0) == 1) {
343 if (ch == '1') {
344 syslog_r(LOG_DEBUG7, &sdata,
345 "%s blacklisted, removing from whitelist",
346 addr);
347 freeaddrinfo(res);
348 return(-1);
349 }
350 }
351 }
352
353 if (whitecount == whitealloc) {
354 char **tmp;
355
356 tmp = reallocarray(whitelist,
357 whitealloc + 1024, sizeof(char *));
358 if (tmp == NULL((void *)0)) {
359 freeaddrinfo(res);
360 return(-1);
361 }
362 whitelist = tmp;
363 whitealloc += 1024;
364 }
365 whitelist[whitecount] = strdup(addr);
366 if (whitelist[whitecount] == NULL((void *)0)) {
367 freeaddrinfo(res);
368 return(-1);
369 }
370 whitecount++;
371 freeaddrinfo(res);
372 return(0);
373}
374
375/* validate, then add to list of addrs to traplist */
376int
377addtrapaddr(char *addr)
378{
379 struct addrinfo hints, *res;
380
381 memset(&hints, 0, sizeof(hints));
382 hints.ai_family = AF_INET2; /*for now*/
383 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
384 hints.ai_protocol = IPPROTO_UDP17; /*dummy*/
385 hints.ai_flags = AI_NUMERICHOST4;
386
387 if (getaddrinfo(addr, NULL((void *)0), &hints, &res) == 0) {
388 if (trapcount == trapalloc) {
389 char **tmp;
390
391 tmp = reallocarray(traplist,
392 trapalloc + 1024, sizeof(char *));
393 if (tmp == NULL((void *)0)) {
394 freeaddrinfo(res);
395 return(-1);
396 }
397 traplist = tmp;
398 trapalloc += 1024;
399 }
400 traplist[trapcount] = strdup(addr);
401 if (traplist[trapcount] == NULL((void *)0)) {
402 freeaddrinfo(res);
403 return(-1);
404 }
405 trapcount++;
406 freeaddrinfo(res);
407 } else
408 return(-1);
409 return(0);
410}
411
412static int
413queue_change(char *key, char *data, size_t dsiz, int act)
414{
415 struct db_change *dbc;
416
417 if ((dbc = malloc(sizeof(*dbc))) == NULL((void *)0)) {
418 syslog_r(LOG_DEBUG7, &sdata, "malloc failed (queue change)");
419 return(-1);
420 }
421 if ((dbc->key = strdup(key)) == NULL((void *)0)) {
422 syslog_r(LOG_DEBUG7, &sdata, "malloc failed (queue change)");
423 free(dbc);
424 return(-1);
425 }
426 if ((dbc->data = malloc(dsiz)) == NULL((void *)0)) {
427 syslog_r(LOG_DEBUG7, &sdata, "malloc failed (queue change)");
428 free(dbc->key);
429 free(dbc);
430 return(-1);
431 }
432 memcpy(dbc->data, data, dsiz);
433 dbc->dsiz = dsiz;
434 dbc->act = act;
435 syslog_r(LOG_DEBUG7, &sdata,
436 "queueing %s of %s", ((act == DBC_ADD1) ? "add" : "deletion"),
437 dbc->key);
438 SLIST_INSERT_HEAD(&db_changes, dbc, entry)do { (dbc)->entry.sle_next = (&db_changes)->slh_first
; (&db_changes)->slh_first = (dbc); } while (0)
;
439 return(0);
440}
441
442static int
443do_changes(DB *db)
444{
445 DBT dbk, dbd;
446 struct db_change *dbc;
447 int ret = 0;
448
449 while (!SLIST_EMPTY(&db_changes)(((&db_changes)->slh_first) == ((void *)0))) {
450 dbc = SLIST_FIRST(&db_changes)((&db_changes)->slh_first);
451 switch (dbc->act) {
452 case DBC_ADD1:
453 memset(&dbk, 0, sizeof(dbk));
454 dbk.size = strlen(dbc->key);
455 dbk.data = dbc->key;
456 memset(&dbd, 0, sizeof(dbd));
457 dbd.size = dbc->dsiz;
458 dbd.data = dbc->data;
459 if (db->put(db, &dbk, &dbd, 0)) {
460 db->sync(db, 0);
461 syslog_r(LOG_ERR3, &sdata,
462 "can't add %s to spamd db (%m)", dbc->key);
463 ret = -1;
464 }
465 db->sync(db, 0);
466 break;
467 case DBC_DEL2:
468 memset(&dbk, 0, sizeof(dbk));
469 dbk.size = strlen(dbc->key);
470 dbk.data = dbc->key;
471 if (db->del(db, &dbk, 0)) {
472 syslog_r(LOG_ERR3, &sdata,
473 "can't delete %s from spamd db (%m)",
474 dbc->key);
475 ret = -1;
476 }
477 break;
478 default:
479 syslog_r(LOG_ERR3, &sdata, "Unrecognized db change");
480 ret = -1;
481 }
482 free(dbc->key);
483 dbc->key = NULL((void *)0);
484 free(dbc->data);
485 dbc->data = NULL((void *)0);
486 dbc->act = 0;
487 dbc->dsiz = 0;
488 SLIST_REMOVE_HEAD(&db_changes, entry)do { (&db_changes)->slh_first = (&db_changes)->
slh_first->entry.sle_next; } while (0)
;
489 free(dbc);
490
491 }
492 return(ret);
493}
494
495/* -1=error, 0=notfound, 1=TRAPPED, 2=WHITE */
496int
497db_addrstate(DB *db, char *key)
498{
499 DBT dbk, dbd;
500 struct gdata gd;
501
502 memset(&dbk, 0, sizeof(dbk));
503 dbk.size = strlen(key);
504 dbk.data = key;
505 memset(&dbd, 0, sizeof(dbd));
506 switch (db->get(db, &dbk, &dbd, 0)) {
507 case 1:
508 /* not found */
509 return (0);
510 case 0:
511 if (gdcopyin(&dbd, &gd) != -1)
512 return (gd.pcount == -1 ? 1 : 2);
513 /* FALLTHROUGH */
514 default:
515 /* error */
516 return (-1);
517 }
518}
519
520
521int
522greyscan(char *dbname)
523{
524 HASHINFO hashinfo;
525 DBT dbk, dbd;
526 DB *db;
527 struct gdata gd;
528 int r;
529 char *a = NULL((void *)0);
13
'a' initialized to a null pointer value
530 size_t asiz = 0;
531 time_t now = time(NULL((void *)0));
532
533 /* walk db, expire, and whitelist */
534 memset(&hashinfo, 0, sizeof(hashinfo));
535 db = dbopen(dbname, O_EXLOCK0x0020|O_RDWR0x0002, 0600, DB_HASH, &hashinfo);
536 if (db == NULL((void *)0)) {
14
Assuming 'db' is not equal to NULL
15
Taking false branch
537 syslog_r(LOG_INFO6, &sdata, "dbopen failed (%m)");
538 return(-1);
539 }
540 memset(&dbk, 0, sizeof(dbk));
541 memset(&dbd, 0, sizeof(dbd));
542 for (r = db->seq(db, &dbk, &dbd, R_FIRST3); !r;
16
Assuming 'r' is 0
17
Loop condition is true. Entering loop body
543 r = db->seq(db, &dbk, &dbd, R_NEXT7)) {
544 if ((dbk.size < 1) || gdcopyin(&dbd, &gd) == -1) {
18
Assuming field 'size' is >= 1
19
Assuming the condition is false
20
Taking false branch
545 syslog_r(LOG_ERR3, &sdata, "bogus entry in spamd database");
546 goto bad;
547 }
548 if (asiz < dbk.size + 1) {
21
Assuming the condition is false
22
Taking false branch
549 char *tmp;
550
551 tmp = reallocarray(a, dbk.size, 2);
552 if (tmp == NULL((void *)0))
553 goto bad;
554 a = tmp;
555 asiz = dbk.size * 2;
556 }
557 memset(a, 0, asiz);
558 memcpy(a, dbk.data, dbk.size);
23
Null pointer passed as 1st argument to memory copy function
559 if (gd.expire <= now && gd.pcount != -2) {
560 /* get rid of entry */
561 if (queue_change(a, NULL((void *)0), 0, DBC_DEL2) == -1)
562 goto bad;
563 } else if (gd.pcount == -1) {
564 /* this is a greytrap hit */
565 if ((addtrapaddr(a) == -1) &&
566 (queue_change(a, NULL((void *)0), 0, DBC_DEL2) == -1))
567 goto bad;
568 } else if (gd.pcount >= 0 && gd.pass <= now) {
569 int tuple = 0;
570 char *cp;
571 int state;
572
573 /*
574 * if not already TRAPPED,
575 * add address to whitelist
576 * add an address-keyed entry to db
577 */
578 cp = strchr(a, '\n');
579 if (cp != NULL((void *)0)) {
580 tuple = 1;
581 *cp = '\0';
582 }
583
584 state = db_addrstate(db, a);
585 if (state != 1 && addwhiteaddr(a) == -1) {
586 if (cp != NULL((void *)0))
587 *cp = '\n';
588 if (queue_change(a, NULL((void *)0), 0, DBC_DEL2) == -1)
589 goto bad;
590 }
591
592 if (tuple && state <= 0) {
593 if (cp != NULL((void *)0))
594 *cp = '\0';
595 /* re-add entry, keyed only by ip */
596 gd.expire = now + whiteexp;
597 dbd.size = sizeof(gd);
598 dbd.data = &gd;
599 if (queue_change(a, (void *) &gd, sizeof(gd),
600 DBC_ADD1) == -1)
601 goto bad;
602 syslog_r(LOG_DEBUG7, &sdata,
603 "whitelisting %s in %s", a, dbname);
604 }
605 if (debug)
606 fprintf(stderr(&__sF[2]), "whitelisted %s\n", a);
607 }
608 }
609 (void) do_changes(db);
610 db->close(db);
611 db = NULL((void *)0);
612 configure_pf(whitelist, whitecount);
613 configure_spamd(traplist, trapcount, trapcfg);
614
615 freeaddrlists();
616 free(a);
617 a = NULL((void *)0);
618 return(0);
619 bad:
620 (void) do_changes(db);
621 db->close(db);
622 db = NULL((void *)0);
623 freeaddrlists();
624 free(a);
625 a = NULL((void *)0);
626 return(-1);
627}
628
629int
630trapcheck(DB *db, char *to)
631{
632 int i, j, smatch = 0;
633 DBT dbk, dbd;
634 struct mail_addr *m;
635 char * trap;
636 size_t s;
637
638 trap = dequotetolower(to);
639 if (!SLIST_EMPTY(&match_suffix)(((&match_suffix)->slh_first) == ((void *)0))) {
640 s = strlen(trap);
641 SLIST_FOREACH(m, &match_suffix, entry)for((m) = ((&match_suffix)->slh_first); (m) != ((void *
)0); (m) = ((m)->entry.sle_next))
{
642 j = s - strlen(m->addr);
643 if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0))
644 smatch = 1;
645 }
646 if (!smatch)
647 /* no suffixes match, so trap it */
648 return (0);
649 }
650 memset(&dbk, 0, sizeof(dbk));
651 dbk.size = strlen(trap);
652 dbk.data = trap;
653 memset(&dbd, 0, sizeof(dbd));
654 i = db->get(db, &dbk, &dbd, 0);
655 if (i == -1)
656 return (-1);
657 if (i)
658 /* didn't exist - so this doesn't match a known spamtrap */
659 return (1);
660 else
661 /* To: address is a spamtrap, so add as a greytrap entry */
662 return (0);
663}
664
665int
666twupdate(char *dbname, char *what, char *ip, char *source, char *expires)
667{
668 /* we got a TRAP or WHITE update from someone else */
669 HASHINFO hashinfo;
670 DBT dbk, dbd;
671 DB *db;
672 struct gdata gd;
673 time_t now, expire;
674 int r, spamtrap;
675
676 now = time(NULL((void *)0));
677 /* expiry times have to be in the future */
678 expire = strtonum(expires, now,
679 sizeof(time_t) == sizeof(int) ? INT_MAX2147483647 : LLONG_MAX9223372036854775807LL, NULL((void *)0));
680 if (expire == 0)
681 return(-1);
682
683 if (strcmp(what, "TRAP") == 0)
684 spamtrap = 1;
685 else if (strcmp(what, "WHITE") == 0)
686 spamtrap = 0;
687 else
688 return(-1);
689
690 memset(&hashinfo, 0, sizeof(hashinfo));
691 db = dbopen(dbname, O_EXLOCK0x0020|O_RDWR0x0002, 0600, DB_HASH, &hashinfo);
692 if (db == NULL((void *)0))
693 return(-1);
694
695 memset(&dbk, 0, sizeof(dbk));
696 dbk.size = strlen(ip);
697 dbk.data = ip;
698 memset(&dbd, 0, sizeof(dbd));
699 r = db->get(db, &dbk, &dbd, 0);
700 if (r == -1)
701 goto bad;
702 if (r) {
703 /* new entry */
704 memset(&gd, 0, sizeof(gd));
705 gd.first = now;
706 gd.pcount = spamtrap ? -1 : 0;
707 gd.expire = expire;
708 memset(&dbk, 0, sizeof(dbk));
709 dbk.size = strlen(ip);
710 dbk.data = ip;
711 memset(&dbd, 0, sizeof(dbd));
712 dbd.size = sizeof(gd);
713 dbd.data = &gd;
714 r = db->put(db, &dbk, &dbd, 0);
715 db->sync(db, 0);
716 if (r)
717 goto bad;
718 if (debug)
719 fprintf(stderr(&__sF[2]), "added %s %s\n",
720 spamtrap ? "trap entry for" : "", ip);
721 syslog_r(LOG_DEBUG7, &sdata,
722 "new %s from %s for %s, expires %s", what, source, ip,
723 expires);
724 } else {
725 /* existing entry */
726 if (gdcopyin(&dbd, &gd) == -1) {
727 /* whatever this is, it doesn't belong */
728 db->del(db, &dbk, 0);
729 db->sync(db, 0);
730 goto bad;
731 }
732 if (spamtrap) {
733 gd.pcount = -1;
734 gd.bcount++;
735 } else
736 gd.pcount++;
737 memset(&dbk, 0, sizeof(dbk));
738 dbk.size = strlen(ip);
739 dbk.data = ip;
740 memset(&dbd, 0, sizeof(dbd));
741 dbd.size = sizeof(gd);
742 dbd.data = &gd;
743 r = db->put(db, &dbk, &dbd, 0);
744 db->sync(db, 0);
745 if (r)
746 goto bad;
747 if (debug)
748 fprintf(stderr(&__sF[2]), "updated %s\n", ip);
749 }
750 db->close(db);
751 return(0);
752 bad:
753 db->close(db);
754 return(-1);
755
756}
757
758int
759greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync,
760 char *cip)
761{
762 HASHINFO hashinfo;
763 DBT dbk, dbd;
764 DB *db;
765 char *key = NULL((void *)0);
766 char *lookup;
767 struct gdata gd;
768 time_t now, expire;
769 int r, spamtrap;
770
771 now = time(NULL((void *)0));
772
773 /* open with lock, find record, update, close, unlock */
774 memset(&hashinfo, 0, sizeof(hashinfo));
775 db = dbopen(dbname, O_EXLOCK0x0020|O_RDWR0x0002, 0600, DB_HASH, &hashinfo);
776 if (db == NULL((void *)0))
777 return(-1);
778 if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1)
779 goto bad;
780 r = trapcheck(db, to);
781 switch (r) {
782 case 1:
783 /* do not trap */
784 spamtrap = 0;
785 lookup = key;
786 expire = greyexp;
787 break;
788 case 0:
789 /* trap */
790 spamtrap = 1;
791 lookup = ip;
792 expire = trapexp;
793 syslog_r(LOG_DEBUG7, &sdata, "Trapping %s for tuple %s", ip,
794 key);
795 break;
796 default:
797 goto bad;
798 break;
799 }
800 memset(&dbk, 0, sizeof(dbk));
801 dbk.size = strlen(lookup);
802 dbk.data = lookup;
803 memset(&dbd, 0, sizeof(dbd));
804 r = db->get(db, &dbk, &dbd, 0);
805 if (r == -1)
806 goto bad;
807 if (r) {
808 /* new entry */
809 if (sync && low_prio_mx_ip &&
810 (strcmp(cip, low_prio_mx_ip) == 0) &&
811 ((startup + 60) < now)) {
812 /* we haven't seen a greylist entry for this tuple,
813 * and yet the connection was to a low priority MX
814 * which we know can't be hit first if the client
815 * is adhering to the RFC's - soo.. kill it!
816 */
817 spamtrap = 1;
818 lookup = ip;
819 expire = trapexp;
820 syslog_r(LOG_DEBUG7, &sdata,
821 "Trapping %s for trying %s first for tuple %s",
822 ip, low_prio_mx_ip, key);
823 }
824 memset(&gd, 0, sizeof(gd));
825 gd.first = now;
826 gd.bcount = 1;
827 gd.pcount = spamtrap ? -1 : 0;
828 gd.pass = now + expire;
829 gd.expire = now + expire;
830 memset(&dbk, 0, sizeof(dbk));
831 dbk.size = strlen(lookup);
832 dbk.data = lookup;
833 memset(&dbd, 0, sizeof(dbd));
834 dbd.size = sizeof(gd);
835 dbd.data = &gd;
836 r = db->put(db, &dbk, &dbd, 0);
837 db->sync(db, 0);
838 if (r)
839 goto bad;
840 if (debug)
841 fprintf(stderr(&__sF[2]), "added %s %s\n",
842 spamtrap ? "greytrap entry for" : "", lookup);
843 syslog_r(LOG_DEBUG7, &sdata,
844 "new %sentry %s from %s to %s, helo %s",
845 spamtrap ? "greytrap " : "", ip, from, to, helo);
846 } else {
847 /* existing entry */
848 if (gdcopyin(&dbd, &gd) == -1) {
849 /* whatever this is, it doesn't belong */
850 db->del(db, &dbk, 0);
851 db->sync(db, 0);
852 goto bad;
853 }
854 gd.bcount++;
855 gd.pcount = spamtrap ? -1 : 0;
856 if (gd.first + passtime < now)
857 gd.pass = now;
858 memset(&dbk, 0, sizeof(dbk));
859 dbk.size = strlen(lookup);
860 dbk.data = lookup;
861 memset(&dbd, 0, sizeof(dbd));
862 dbd.size = sizeof(gd);
863 dbd.data = &gd;
864 r = db->put(db, &dbk, &dbd, 0);
865 db->sync(db, 0);
866 if (r)
867 goto bad;
868 if (debug)
869 fprintf(stderr(&__sF[2]), "updated %s\n", lookup);
870 }
871 free(key);
872 key = NULL((void *)0);
873 db->close(db);
874 db = NULL((void *)0);
875
876 /* Entry successfully update, sent out sync message */
877 if (syncsend && sync) {
878 if (spamtrap) {
879 syslog_r(LOG_DEBUG7, &sdata,
880 "sync_trap %s", ip);
881 sync_trapped(now, now + expire, ip);
882 }
883 else
884 sync_update(now, helo, ip, from, to);
885 }
886 return(0);
887 bad:
888 free(key);
889 key = NULL((void *)0);
890 db->close(db);
891 db = NULL((void *)0);
892 return(-1);
893}
894
895int
896twread(char *buf)
897{
898 if ((strncmp(buf, "WHITE:", 6) == 0) ||
899 (strncmp(buf, "TRAP:", 5) == 0)) {
900 char **ap, *argv[5];
901 int argc = 0;
902
903 for (ap = argv;
904 ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL((void *)0);) {
905 if (**ap != '\0')
906 ap++;
907 argc++;
908 }
909 *ap = NULL((void *)0);
910 if (argc != 4)
911 return (-1);
912 twupdate(PATH_SPAMD_DB"/var/db/spamd", argv[0], argv[1], argv[2], argv[3]);
913 return (0);
914 } else
915 return (-1);
916}
917
918int
919greyreader(void)
920{
921 char cip[32], ip[32], helo[MAX_MAIL1024], from[MAX_MAIL1024], to[MAX_MAIL1024];
922 char *buf;
923 size_t len;
924 int state, sync;
925 struct addrinfo hints, *res;
926
927 memset(&hints, 0, sizeof(hints));
928 hints.ai_family = AF_INET2; /*for now*/
929 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
930 hints.ai_protocol = IPPROTO_UDP17; /*dummy*/
931 hints.ai_flags = AI_NUMERICHOST4;
932
933 state = 0;
934 sync = 1;
935 if (grey == NULL((void *)0)) {
936 syslog_r(LOG_ERR3, &sdata, "No greylist pipe stream!\n");
937 return (-1);
938 }
939
940 /* grab trap suffixes */
941 readsuffixlists();
942
943 while ((buf = fgetln(grey, &len))) {
944 if (buf[len - 1] == '\n')
945 buf[len - 1] = '\0';
946 else
947 /* all valid lines end in \n */
948 continue;
949 if (strlen(buf) < 4)
950 continue;
951
952 if (strcmp(buf, "SYNC") == 0) {
953 sync = 0;
954 continue;
955 }
956
957 switch (state) {
958 case 0:
959 if (twread(buf) == 0) {
960 state = 0;
961 break;
962 }
963 if (strncmp(buf, "HE:", 3) != 0) {
964 if (strncmp(buf, "CO:", 3) == 0)
965 strlcpy(cip, buf+3, sizeof(cip));
966 state = 0;
967 break;
968 }
969 strlcpy(helo, buf+3, sizeof(helo));
970 state = 1;
971 break;
972 case 1:
973 if (strncmp(buf, "IP:", 3) != 0)
974 break;
975 strlcpy(ip, buf+3, sizeof(ip));
976 if (getaddrinfo(ip, NULL((void *)0), &hints, &res) == 0) {
977 freeaddrinfo(res);
978 state = 2;
979 } else
980 state = 0;
981 break;
982 case 2:
983 if (strncmp(buf, "FR:", 3) != 0) {
984 state = 0;
985 break;
986 }
987 strlcpy(from, buf+3, sizeof(from));
988 state = 3;
989 break;
990 case 3:
991 if (strncmp(buf, "TO:", 3) != 0) {
992 state = 0;
993 break;
994 }
995 strlcpy(to, buf+3, sizeof(to));
996 if (debug)
997 fprintf(stderr(&__sF[2]),
998 "Got Grey HELO %s, IP %s from %s to %s\n",
999 helo, ip, from, to);
1000 greyupdate(PATH_SPAMD_DB"/var/db/spamd", helo, ip, from, to, sync, cip);
1001 sync = 1;
1002 state = 0;
1003 break;
1004 }
1005 }
1006 return (0);
1007}
1008
1009void
1010greyscanner(void)
1011{
1012 for (;;) {
11
Loop condition is true. Entering loop body
1013 if (greyscan(PATH_SPAMD_DB"/var/db/spamd") == -1)
12
Calling 'greyscan'
1014 syslog_r(LOG_NOTICE5, &sdata, "scan of %s failed",
1015 PATH_SPAMD_DB"/var/db/spamd");
1016 sleep(DB_SCAN_INTERVAL60);
1017 }
1018}
1019
1020static void
1021drop_privs(void)
1022{
1023 /*
1024 * lose root, continue as non-root user
1025 */
1026 if (setgroups(1, &pw->pw_gid) ||
1027 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1028 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
1029 syslog_r(LOG_ERR3, &sdata, "failed to drop privs (%m)");
1030 exit(1);
1031 }
1032}
1033
1034void
1035check_spamd_db(void)
1036{
1037 HASHINFO hashinfo;
1038 int i = -1;
1039 DB *db;
1040
1041 /* check to see if /var/db/spamd exists, if not, create it */
1042 memset(&hashinfo, 0, sizeof(hashinfo));
1043 db = dbopen(PATH_SPAMD_DB"/var/db/spamd", O_EXLOCK0x0020|O_RDWR0x0002, 0600, DB_HASH, &hashinfo);
1044
1045 if (db == NULL((void *)0)) {
1046 switch (errno(*__errno())) {
1047 case ENOENT2:
1048 i = open(PATH_SPAMD_DB"/var/db/spamd", O_RDWR0x0002|O_CREAT0x0200, 0644);
1049 if (i == -1) {
1050 syslog_r(LOG_ERR3, &sdata,
1051 "create %s failed (%m)", PATH_SPAMD_DB"/var/db/spamd");
1052 exit(1);
1053 }
1054 /* if we are dropping privs, chown to that user */
1055 if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) {
1056 syslog_r(LOG_ERR3, &sdata,
1057 "chown %s failed (%m)", PATH_SPAMD_DB"/var/db/spamd");
1058 exit(1);
1059 }
1060 close(i);
1061 return;
1062 break;
1063 default:
1064 syslog_r(LOG_ERR3, &sdata, "open of %s failed (%m)",
1065 PATH_SPAMD_DB"/var/db/spamd");
1066 exit(1);
1067 }
1068 }
1069 db->sync(db, 0);
1070 db->close(db);
1071}
1072
1073
1074int
1075greywatcher(void)
1076{
1077 struct sigaction sa;
1078
1079 drop_privs();
1080
1081 if (unveil(PATH_SPAMD_DB"/var/db/spamd", "rw") == -1) {
1
Assuming the condition is false
2
Taking false branch
1082 syslog_r(LOG_ERR3, &sdata, "unveil failed (%m)");
1083 exit(1);
1084 }
1085 if (unveil(alloweddomains_file, "r") == -1) {
3
Assuming the condition is false
4
Taking false branch
1086 syslog_r(LOG_ERR3, &sdata, "unveil failed (%m)");
1087 exit(1);
1088 }
1089 if (unveil(PATH_PFCTL"/sbin/pfctl", "x") == -1) {
5
Assuming the condition is false
6
Taking false branch
1090 syslog_r(LOG_ERR3, &sdata, "unveil failed (%m)");
1091 exit(1);
1092 }
1093 if (pledge("stdio rpath wpath inet flock proc exec", NULL((void *)0)) == -1) {
7
Assuming the condition is false
8
Taking false branch
1094 syslog_r(LOG_ERR3, &sdata, "pledge failed (%m)");
1095 exit(1);
1096 }
1097
1098 startup = time(NULL((void *)0));
1099 db_pid = fork();
1100 switch (db_pid) {
9
'Default' branch taken. Execution continues on line 1119
1101 case -1:
1102 syslog_r(LOG_ERR3, &sdata, "fork failed (%m)");
1103 exit(1);
1104 case 0:
1105 /*
1106 * child, talks to jailed spamd over greypipe,
1107 * updates db. has no access to pf.
1108 */
1109 close(pfdev);
1110 setproctitle("(%s update)", PATH_SPAMD_DB"/var/db/spamd");
1111 if (greyreader() == -1) {
1112 syslog_r(LOG_ERR3, &sdata, "greyreader failed (%m)");
1113 _exit(1);
1114 }
1115 _exit(0);
1116 }
1117
1118
1119 fclose(grey);
1120 /*
1121 * parent, scans db periodically for changes and updates
1122 * pf whitelist table accordingly.
1123 */
1124
1125 sigfillset(&sa.sa_mask);
1126 sa.sa_flags = SA_RESTART0x0002;
1127 sa.sa_handler__sigaction_u.__sa_handler = sig_term_chld;
1128 sigaction(SIGTERM15, &sa, NULL((void *)0));
1129 sigaction(SIGHUP1, &sa, NULL((void *)0));
1130 sigaction(SIGCHLD20, &sa, NULL((void *)0));
1131 sigaction(SIGINT2, &sa, NULL((void *)0));
1132
1133 setproctitle("(pf <spamd-white> update)");
1134 greyscanner();
10
Calling 'greyscanner'
1135 exit(1);
1136}