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