File: | src/libexec/spamd/grey.c |
Warning: | line 558, 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.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 | ||||
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 | /* ARGSUSED */ | |||
118 | static void | |||
119 | sig_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 | */ | |||
134 | void | |||
135 | configure_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 | ||||
151 | int | |||
152 | configure_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 | ||||
230 | char * | |||
231 | dequotetolower(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 | ||||
250 | void | |||
251 | readsuffixlists(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; | |||
294 | bad: | |||
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 | ||||
302 | void | |||
303 | freeaddrlists(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 */ | |||
323 | int | |||
324 | addwhiteaddr(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 */ | |||
376 | int | |||
377 | addtrapaddr(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 | ||||
412 | static int | |||
413 | queue_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 | ||||
442 | static int | |||
443 | do_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 */ | |||
496 | int | |||
497 | db_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 | ||||
521 | int | |||
522 | greyscan(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); | |||
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)) { | |||
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; | |||
543 | r = db->seq(db, &dbk, &dbd, R_NEXT7)) { | |||
544 | if ((dbk.size < 1) || gdcopyin(&dbd, &gd) == -1) { | |||
545 | syslog_r(LOG_ERR3, &sdata, "bogus entry in spamd database"); | |||
546 | goto bad; | |||
547 | } | |||
548 | if (asiz < dbk.size + 1) { | |||
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); | |||
| ||||
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 | ||||
629 | int | |||
630 | trapcheck(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 | ||||
665 | int | |||
666 | twupdate(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 | ||||
758 | int | |||
759 | greyupdate(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 | ||||
895 | int | |||
896 | twread(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 | ||||
918 | int | |||
919 | greyreader(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 | ||||
1009 | void | |||
1010 | greyscanner(void) | |||
1011 | { | |||
1012 | for (;;) { | |||
1013 | if (greyscan(PATH_SPAMD_DB"/var/db/spamd") == -1) | |||
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 | ||||
1020 | static void | |||
1021 | drop_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 | ||||
1034 | void | |||
1035 | check_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 | ||||
1074 | int | |||
1075 | greywatcher(void) | |||
1076 | { | |||
1077 | struct sigaction sa; | |||
1078 | ||||
1079 | drop_privs(); | |||
1080 | ||||
1081 | if (unveil(PATH_SPAMD_DB"/var/db/spamd", "rw") == -1) { | |||
| ||||
1082 | syslog_r(LOG_ERR3, &sdata, "unveil failed (%m)"); | |||
1083 | exit(1); | |||
1084 | } | |||
1085 | if (unveil(alloweddomains_file, "r") == -1) { | |||
1086 | syslog_r(LOG_ERR3, &sdata, "unveil failed (%m)"); | |||
1087 | exit(1); | |||
1088 | } | |||
1089 | if (unveil(PATH_PFCTL"/sbin/pfctl", "x") == -1) { | |||
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) { | |||
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) { | |||
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(); | |||
1135 | exit(1); | |||
1136 | } |