Bug Summary

File:src/libexec/spamd-setup/spamd-setup.c
Warning:line 601, column 3
Value stored to 'laststate' is never read

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 spamd-setup.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-setup/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-setup/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-setup/spamd-setup.c
1/* $OpenBSD: spamd-setup.c,v 1.50 2017/07/07 00:10:15 djm Exp $ */
2
3/*
4 * Copyright (c) 2003 Bob Beck. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <arpa/inet.h>
28#include <sys/socket.h>
29#include <sys/types.h>
30
31#include <err.h>
32#include <errno(*__errno()).h>
33#include <fcntl.h>
34#include <netdb.h>
35#include <pwd.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <zlib.h>
41
42#define PATH_FTP"/usr/bin/ftp" "/usr/bin/ftp"
43#define PATH_PFCTL"/sbin/pfctl" "/sbin/pfctl"
44#define PATH_SPAMD_CONF"/etc/mail/spamd.conf" "/etc/mail/spamd.conf"
45#define SPAMD_ARG_MAX256 256 /* max # of args to an exec */
46#define SPAMD_USER"_spamd" "_spamd"
47
48struct cidr {
49 u_int32_t addr;
50 u_int8_t bits;
51};
52
53struct bl {
54 u_int32_t addr;
55 int8_t b;
56 int8_t w;
57};
58
59struct blacklist {
60 char *name;
61 char *message;
62 struct bl *bl;
63 size_t blc, bls;
64 u_int8_t black;
65};
66
67u_int32_t imask(u_int8_t);
68u_int8_t maxblock(u_int32_t, u_int8_t);
69u_int8_t maxdiff(u_int32_t, u_int32_t);
70struct cidr *range2cidrlist(struct cidr *, u_int *, u_int *, u_int32_t,
71 u_int32_t);
72void cidr2range(struct cidr, u_int32_t *, u_int32_t *);
73char *atop(u_int32_t);
74int parse_netblock(char *, struct bl *, struct bl *, int);
75int open_child(char *, char **, int);
76int fileget(char *);
77int open_file(char *, char *);
78char *fix_quoted_colons(char *);
79void do_message(FILE *, char *);
80struct bl *add_blacklist(struct bl *, size_t *, size_t *, gzFile, int);
81int cmpbl(const void *, const void *);
82struct cidr *collapse_blacklist(struct bl *, size_t, u_int *);
83int configure_spamd(u_short, char *, char *, struct cidr *, u_int);
84int configure_pf(struct cidr *);
85int getlist(char **, char *, struct blacklist *, struct blacklist *);
86__dead__attribute__((__noreturn__)) void usage(void);
87
88uid_t spamd_uid;
89gid_t spamd_gid;
90int debug;
91int dryrun;
92int greyonly = 1;
93
94extern char *__progname;
95
96#define MAXIMUM(a,b)(((a)>(b))?(a):(b)) (((a)>(b))?(a):(b))
97
98u_int32_t
99imask(u_int8_t b)
100{
101 if (b == 0)
102 return (0);
103 return (0xffffffffU << (32 - b));
104}
105
106u_int8_t
107maxblock(u_int32_t addr, u_int8_t bits)
108{
109 u_int32_t m;
110
111 while (bits > 0) {
112 m = imask(bits - 1);
113
114 if ((addr & m) != addr)
115 return (bits);
116 bits--;
117 }
118 return (bits);
119}
120
121u_int8_t
122maxdiff(u_int32_t a, u_int32_t b)
123{
124 u_int8_t bits = 0;
125 u_int32_t m;
126
127 b++;
128 while (bits < 32) {
129 m = imask(bits);
130
131 if ((a & m) != (b & m))
132 return (bits);
133 bits++;
134 }
135 return (bits);
136}
137
138struct cidr *
139range2cidrlist(struct cidr *list, u_int *cli, u_int *cls, u_int32_t start,
140 u_int32_t end)
141{
142 u_int8_t maxsize, diff;
143 struct cidr *tmp;
144
145 while (end >= start) {
146 maxsize = maxblock(start, 32);
147 diff = maxdiff(start, end);
148
149 maxsize = MAXIMUM(maxsize, diff)(((maxsize)>(diff))?(maxsize):(diff));
150 if (*cls <= *cli + 1) { /* one extra for terminator */
151 tmp = reallocarray(list, *cls + 32,
152 sizeof(struct cidr));
153 if (tmp == NULL((void*)0))
154 err(1, NULL((void*)0));
155 list = tmp;
156 *cls += 32;
157 }
158 list[*cli].addr = start;
159 list[*cli].bits = maxsize;
160 (*cli)++;
161 start = start + (1 << (32 - maxsize));
162 }
163 return (list);
164}
165
166void
167cidr2range(struct cidr cidr, u_int32_t *start, u_int32_t *end)
168{
169 *start = cidr.addr;
170 *end = cidr.addr + (1 << (32 - cidr.bits)) - 1;
171}
172
173char *
174atop(u_int32_t addr)
175{
176 struct in_addr in;
177
178 memset(&in, 0, sizeof(in));
179 in.s_addr = htonl(addr)(__uint32_t)(__builtin_constant_p(addr) ? (__uint32_t)(((__uint32_t
)(addr) & 0xff) << 24 | ((__uint32_t)(addr) & 0xff00
) << 8 | ((__uint32_t)(addr) & 0xff0000) >> 8
| ((__uint32_t)(addr) & 0xff000000) >> 24) : __swap32md
(addr))
;
180 return (inet_ntoa(in));
181}
182
183int
184parse_netblock(char *buf, struct bl *start, struct bl *end, int white)
185{
186 char astring[16], astring2[16];
187 unsigned maskbits;
188 struct cidr c;
189
190 /* skip leading spaces */
191 while (*buf == ' ')
192 buf++;
193 /* bail if it's a comment */
194 if (*buf == '#')
195 return (0);
196 /* otherwise, look for a netblock of some sort */
197 if (sscanf(buf, "%15[^/]/%u", astring, &maskbits) == 2) {
198 /* looks like a cidr */
199 memset(&c.addr, 0, sizeof(c.addr));
200 if (inet_net_pton(AF_INET2, astring, &c.addr, sizeof(c.addr))
201 == -1)
202 return (0);
203 c.addr = ntohl(c.addr)(__uint32_t)(__builtin_constant_p(c.addr) ? (__uint32_t)(((__uint32_t
)(c.addr) & 0xff) << 24 | ((__uint32_t)(c.addr) &
0xff00) << 8 | ((__uint32_t)(c.addr) & 0xff0000) >>
8 | ((__uint32_t)(c.addr) & 0xff000000) >> 24) : __swap32md
(c.addr))
;
204 if (maskbits > 32)
205 return (0);
206 c.bits = maskbits;
207 cidr2range(c, &start->addr, &end->addr);
208 end->addr += 1;
209 } else if (sscanf(buf, "%15[0123456789.]%*[ -]%15[0123456789.]",
210 astring, astring2) == 2) {
211 /* looks like start - end */
212 memset(&start->addr, 0, sizeof(start->addr));
213 memset(&end->addr, 0, sizeof(end->addr));
214 if (inet_net_pton(AF_INET2, astring, &start->addr,
215 sizeof(start->addr)) == -1)
216 return (0);
217 start->addr = ntohl(start->addr)(__uint32_t)(__builtin_constant_p(start->addr) ? (__uint32_t
)(((__uint32_t)(start->addr) & 0xff) << 24 | ((__uint32_t
)(start->addr) & 0xff00) << 8 | ((__uint32_t)(start
->addr) & 0xff0000) >> 8 | ((__uint32_t)(start->
addr) & 0xff000000) >> 24) : __swap32md(start->addr
))
;
218 if (inet_net_pton(AF_INET2, astring2, &end->addr,
219 sizeof(end->addr)) == -1)
220 return (0);
221 end->addr = ntohl(end->addr)(__uint32_t)(__builtin_constant_p(end->addr) ? (__uint32_t
)(((__uint32_t)(end->addr) & 0xff) << 24 | ((__uint32_t
)(end->addr) & 0xff00) << 8 | ((__uint32_t)(end->
addr) & 0xff0000) >> 8 | ((__uint32_t)(end->addr
) & 0xff000000) >> 24) : __swap32md(end->addr))
+ 1;
222 if (start > end)
223 return (0);
224 } else if (sscanf(buf, "%15[0123456789.]", astring) == 1) {
225 /* just a single address */
226 memset(&start->addr, 0, sizeof(start->addr));
227 if (inet_net_pton(AF_INET2, astring, &start->addr,
228 sizeof(start->addr)) == -1)
229 return (0);
230 start->addr = ntohl(start->addr)(__uint32_t)(__builtin_constant_p(start->addr) ? (__uint32_t
)(((__uint32_t)(start->addr) & 0xff) << 24 | ((__uint32_t
)(start->addr) & 0xff00) << 8 | ((__uint32_t)(start
->addr) & 0xff0000) >> 8 | ((__uint32_t)(start->
addr) & 0xff000000) >> 24) : __swap32md(start->addr
))
;
231 end->addr = start->addr + 1;
232 } else
233 return (0);
234
235 if (white) {
236 start->b = 0;
237 start->w = 1;
238 end->b = 0;
239 end->w = -1;
240 } else {
241 start->b = 1;
242 start->w = 0;
243 end->b = -1;
244 end->w = 0;
245 }
246 return (1);
247}
248
249void
250drop_privileges(void)
251{
252 if (setgroups(1, &spamd_gid) != 0)
253 err(1, "setgroups %ld", (long)spamd_gid);
254 if (setresgid(spamd_gid, spamd_gid, spamd_gid) != 0)
255 err(1, "setresgid %ld", (long)spamd_gid);
256 if (setresuid(spamd_uid, spamd_uid, spamd_uid) != 0)
257 err(1, "setresuid %ld", (long)spamd_uid);
258}
259
260int
261open_child(char *file, char **argv, int drop_privs)
262{
263 int pdes[2];
264
265 if (pipe(pdes) != 0)
266 return (-1);
267 switch (fork()) {
268 case -1:
269 close(pdes[0]);
270 close(pdes[1]);
271 return (-1);
272 case 0:
273 /* child */
274 close(pdes[0]);
275 if (pdes[1] != STDOUT_FILENO1) {
276 dup2(pdes[1], STDOUT_FILENO1);
277 close(pdes[1]);
278 }
279 if (drop_privs)
280 drop_privileges();
281 closefrom(STDERR_FILENO2 + 1);
282 execvp(file, argv);
283 _exit(1);
284 }
285
286 /* parent */
287 close(pdes[1]);
288 return (pdes[0]);
289}
290
291int
292fileget(char *url)
293{
294 char *argv[6];
295
296 argv[0] = "ftp";
297 argv[1] = "-V";
298 argv[2] = "-o";
299 argv[3] = "-";
300 argv[4] = url;
301 argv[5] = NULL((void*)0);
302
303 if (debug)
304 fprintf(stderr(&__sF[2]), "Getting %s\n", url);
305
306 return (open_child(PATH_FTP"/usr/bin/ftp", argv, 1));
307}
308
309int
310open_file(char *method, char *file)
311{
312 char *url;
313 char **ap, **argv;
314 int len, i, oerrno;
315
316 if ((method == NULL((void*)0)) || (strcmp(method, "file") == 0))
317 return (open(file, O_RDONLY0x0000));
318 if (strcmp(method, "http") == 0 || strcmp(method, "https") == 0 ||
319 strcmp(method, "ftp") == 0) {
320 if (asprintf(&url, "%s://%s", method, file) == -1)
321 return (-1);
322 i = fileget(url);
323 free(url);
324 return (i);
325 } else if (strcmp(method, "exec") == 0) {
326 len = strlen(file);
327 argv = calloc(len, sizeof(char *));
328 if (argv == NULL((void*)0))
329 return (-1);
330 for (ap = argv; ap < &argv[len - 1] &&
331 (*ap = strsep(&file, " \t")) != NULL((void*)0);) {
332 if (**ap != '\0')
333 ap++;
334 }
335 *ap = NULL((void*)0);
336 i = open_child(argv[0], argv, 0);
337 oerrno = errno(*__errno());
338 free(argv);
339 errno(*__errno()) = oerrno;
340 return (i);
341 }
342 errx(1, "Unknown method %s", method);
343 return (-1); /* NOTREACHED */
344}
345
346/*
347 * fix_quoted_colons walks through a buffer returned by cgetent. We
348 * look for quoted strings, to escape colons (:) in quoted strings for
349 * getcap by replacing them with \C so cgetstr() deals with it correctly
350 * without having to see the \C bletchery in a configuration file that
351 * needs to have urls in it. Frees the buffer passed to it, passes back
352 * another larger one, with can be used with cgetxxx(), like the original
353 * buffer, it must be freed by the caller.
354 * This should really be a temporary fix until there is a sanctioned
355 * way to make getcap(3) handle quoted strings like this in a nicer
356 * way.
357 */
358char *
359fix_quoted_colons(char *buf)
360{
361 int in = 0;
362 size_t i, j = 0;
363 char *newbuf, last;
364
365 /* Allocate enough space for a buf of all colons (impossible). */
366 newbuf = malloc(2 * strlen(buf) + 1);
367 if (newbuf == NULL((void*)0))
368 return (NULL((void*)0));
369 last = '\0';
370 for (i = 0; i < strlen(buf); i++) {
371 switch (buf[i]) {
372 case ':':
373 if (in) {
374 newbuf[j++] = '\\';
375 newbuf[j++] = 'C';
376 } else
377 newbuf[j++] = buf[i];
378 break;
379 case '"':
380 if (last != '\\')
381 in = !in;
382 newbuf[j++] = buf[i];
383 break;
384 default:
385 newbuf[j++] = buf[i];
386 }
387 last = buf[i];
388 }
389 free(buf);
390 newbuf[j] = '\0';
391 return (newbuf);
392}
393
394void
395do_message(FILE *sdc, char *msg)
396{
397 size_t i, bs = 0, bu = 0, len;
398 ssize_t n;
399 char *buf = NULL((void*)0), last, *tmp;
400 int fd;
401
402 len = strlen(msg);
403 if (msg[0] == '"' && msg[len - 1] == '"') {
404 /* quoted msg, escape newlines and send it out */
405 msg[len - 1] = '\0';
406 buf = msg + 1;
407 bu = len - 2;
408 goto sendit;
409 } else {
410 /*
411 * message isn't quoted - try to open a local
412 * file and read the message from it.
413 */
414 fd = open(msg, O_RDONLY0x0000);
415 if (fd == -1)
416 err(1, "Can't open message from %s", msg);
417 for (;;) {
418 if (bu == bs) {
419 tmp = realloc(buf, bs + 8192);
420 if (tmp == NULL((void*)0))
421 err(1, NULL((void*)0));
422 bs += 8192;
423 buf = tmp;
424 }
425
426 n = read(fd, buf + bu, bs - bu);
427 if (n == 0) {
428 goto sendit;
429 } else if (n == -1) {
430 err(1, "Can't read from %s", msg);
431 } else
432 bu += n;
433 }
434 buf[bu]='\0';
435 }
436 sendit:
437 fprintf(sdc, ";\"");
438 last = '\0';
439 for (i = 0; i < bu; i++) {
440 /* handle escaping the things spamd wants */
441 switch (buf[i]) {
442 case 'n':
443 if (last == '\\')
444 fprintf(sdc, "\\\\n");
445 else
446 fputc('n', sdc);
447 last = '\0';
448 break;
449 case '\n':
450 fprintf(sdc, "\\n");
451 last = '\0';
452 break;
453 case '"':
454 fputc('\\', sdc);
455 /* FALLTHROUGH */
456 default:
457 fputc(buf[i], sdc);
458 last = '\0';
459 }
460 }
461 fputc('"', sdc);
462 if (bs != 0)
463 free(buf);
464}
465
466/* retrieve a list from fd. add to blacklist bl */
467struct bl *
468add_blacklist(struct bl *bl, size_t *blc, size_t *bls, gzFile gzf, int white)
469{
470 int i, n, start, bu = 0, bs = 0, serrno = 0;
471 char *buf = NULL((void*)0), *tmp;
472 struct bl *blt;
473
474 for (;;) {
475 /* read in gzf, then parse */
476 if (bu == bs) {
477 tmp = realloc(buf, bs + (1024 * 1024) + 1);
478 if (tmp == NULL((void*)0)) {
479 serrno = errno(*__errno());
480 free(buf);
481 buf = NULL((void*)0);
482 bs = 0;
483 goto bldone;
484 }
485 bs += 1024 * 1024;
486 buf = tmp;
487 }
488
489 n = gzread(gzf, buf + bu, bs - bu);
490 if (n == 0)
491 goto parse;
492 else if (n == -1) {
493 serrno = errno(*__errno());
494 goto bldone;
495 } else
496 bu += n;
497 }
498 parse:
499 start = 0;
500 /* we assume that there is an IP for every 14 bytes */
501 if (*blc + bu / 7 >= *bls) {
502 *bls += bu / 7;
503 blt = reallocarray(bl, *bls, sizeof(struct bl));
504 if (blt == NULL((void*)0)) {
505 *bls -= bu / 7;
506 serrno = errno(*__errno());
507 goto bldone;
508 }
509 bl = blt;
510 }
511 for (i = 0; i <= bu; i++) {
512 if (*blc + 1 >= *bls) {
513 *bls += 1024;
514 blt = reallocarray(bl, *bls, sizeof(struct bl));
515 if (blt == NULL((void*)0)) {
516 *bls -= 1024;
517 serrno = errno(*__errno());
518 goto bldone;
519 }
520 bl = blt;
521 }
522 if (i == bu || buf[i] == '\n') {
523 buf[i] = '\0';
524 if (parse_netblock(buf + start,
525 bl + *blc, bl + *blc + 1, white))
526 *blc += 2;
527 start = i + 1;
528 }
529 }
530 if (bu == 0)
531 errno(*__errno()) = EIO5;
532 bldone:
533 free(buf);
534 if (serrno)
535 errno(*__errno()) = serrno;
536 return (bl);
537}
538
539int
540cmpbl(const void *a, const void *b)
541{
542 if (((struct bl *)a)->addr > ((struct bl *) b)->addr)
543 return (1);
544 if (((struct bl *)a)->addr < ((struct bl *) b)->addr)
545 return (-1);
546 return (0);
547}
548
549/*
550 * collapse_blacklist takes blacklist/whitelist entries sorts, removes
551 * overlaps and whitelist portions, and returns netblocks to blacklist
552 * as lists of nonoverlapping cidr blocks suitable for feeding in
553 * printable form to pfctl or spamd.
554 */
555struct cidr *
556collapse_blacklist(struct bl *bl, size_t blc, u_int *clc)
557{
558 int bs = 0, ws = 0, state=0;
559 u_int cli, cls, i;
560 u_int32_t bstart = 0;
561 struct cidr *cl;
562 int laststate;
563 u_int32_t addr;
564
565 if (blc == 0)
566 return (NULL((void*)0));
567
568 /*
569 * Overallocate by 10% to avoid excessive realloc due to white
570 * entries splitting up CIDR blocks.
571 */
572 cli = 0;
573 cls = (blc / 2) + (blc / 20) + 1;
574 cl = reallocarray(NULL((void*)0), cls, sizeof(struct cidr));
575 if (cl == NULL((void*)0))
576 return (NULL((void*)0));
577 qsort(bl, blc, sizeof(struct bl), cmpbl);
578 for (i = 0; i < blc;) {
579 laststate = state;
580 addr = bl[i].addr;
581
582 do {
583 bs += bl[i].b;
584 ws += bl[i].w;
585 i++;
586 } while (bl[i].addr == addr);
587 if (state == 1 && bs == 0)
588 state = 0;
589 else if (state == 0 && bs > 0)
590 state = 1;
591 if (ws > 0)
592 state = 0;
593 if (laststate == 0 && state == 1) {
594 /* start blacklist */
595 bstart = addr;
596 }
597 if (laststate == 1 && state == 0) {
598 /* end blacklist */
599 cl = range2cidrlist(cl, &cli, &cls, bstart, addr - 1);
600 }
601 laststate = state;
Value stored to 'laststate' is never read
602 }
603 cl[cli].addr = 0;
604 *clc = cli;
605 return (cl);
606}
607
608int
609configure_spamd(u_short dport, char *name, char *message,
610 struct cidr *blacklists, u_int count)
611{
612 int lport = IPPORT_RESERVED1024 - 1, s;
613 struct sockaddr_in sin;
614 FILE* sdc;
615
616 s = rresvport(&lport);
617 if (s == -1)
618 return (-1);
619 memset(&sin, 0, sizeof sin);
620 sin.sin_len = sizeof(sin);
621 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK)(__uint32_t)(__builtin_constant_p(((u_int32_t)(0x7f000001))) ?
(__uint32_t)(((__uint32_t)(((u_int32_t)(0x7f000001))) & 0xff
) << 24 | ((__uint32_t)(((u_int32_t)(0x7f000001))) &
0xff00) << 8 | ((__uint32_t)(((u_int32_t)(0x7f000001))
) & 0xff0000) >> 8 | ((__uint32_t)(((u_int32_t)(0x7f000001
))) & 0xff000000) >> 24) : __swap32md(((u_int32_t)(
0x7f000001))))
;
622 sin.sin_family = AF_INET2;
623 sin.sin_port = htons(dport)(__uint16_t)(__builtin_constant_p(dport) ? (__uint16_t)(((__uint16_t
)(dport) & 0xffU) << 8 | ((__uint16_t)(dport) &
0xff00U) >> 8) : __swap16md(dport))
;
624 if (connect(s, (struct sockaddr *)&sin, sizeof sin) == -1)
625 return (-1);
626 sdc = fdopen(s, "w");
627 if (sdc == NULL((void*)0)) {
628 close(s);
629 return (-1);
630 }
631 fputs(name, sdc);
632 do_message(sdc, message);
633 fprintf(sdc, ";inet;%u", count);
634 while (blacklists->addr != 0) {
635 fprintf(sdc, ";%s/%u", atop(blacklists->addr),
636 blacklists->bits);
637 blacklists++;
638 }
639 fputc('\n', sdc);
640 fclose(sdc);
641 close(s);
642 return (0);
643}
644
645
646int
647configure_pf(struct cidr *blacklists)
648{
649 char *argv[9]= {"pfctl", "-q", "-t", "spamd", "-T", "replace",
650 "-f" "-", NULL((void*)0)};
651 static FILE *pf = NULL((void*)0);
652 int pdes[2];
653
654 if (pf == NULL((void*)0)) {
655 if (pipe(pdes) != 0)
656 return (-1);
657 switch (fork()) {
658 case -1:
659 close(pdes[0]);
660 close(pdes[1]);
661 return (-1);
662 case 0:
663 /* child */
664 close(pdes[1]);
665 if (pdes[0] != STDIN_FILENO0) {
666 dup2(pdes[0], STDIN_FILENO0);
667 close(pdes[0]);
668 }
669 closefrom(STDERR_FILENO2 + 1);
670 execvp(PATH_PFCTL"/sbin/pfctl", argv);
671 _exit(1);
672 }
673
674 /* parent */
675 close(pdes[0]);
676 pf = fdopen(pdes[1], "w");
677 if (pf == NULL((void*)0)) {
678 close(pdes[1]);
679 return (-1);
680 }
681 }
682 while (blacklists->addr != 0) {
683 fprintf(pf, "%s/%u\n", atop(blacklists->addr),
684 blacklists->bits);
685 blacklists++;
686 }
687 return (0);
688}
689
690int
691getlist(char ** db_array, char *name, struct blacklist *blist,
692 struct blacklist *blistnew)
693{
694 char *buf, *method, *file, *message;
695 int fd, black = 0, serror;
696 size_t blc, bls;
697 struct bl *bl = NULL((void*)0);
698 gzFile gzf;
699
700 if (cgetent(&buf, db_array, name) != 0)
701 err(1, "Can't find \"%s\" in spamd config", name);
702 buf = fix_quoted_colons(buf);
703 if (cgetcap(buf, "black", ':') != NULL((void*)0)) {
704 /* use new list */
705 black = 1;
706 blc = blistnew->blc;
707 bls = blistnew->bls;
708 bl = blistnew->bl;
709 } else if (cgetcap(buf, "white", ':') != NULL((void*)0)) {
710 /* apply to most recent blacklist */
711 black = 0;
712 blc = blist->blc;
713 bls = blist->bls;
714 bl = blist->bl;
715 } else
716 errx(1, "Must have \"black\" or \"white\" in %s", name);
717
718 switch (cgetstr(buf, "msg", &message)) {
719 case -1:
720 if (black)
721 errx(1, "No msg for blacklist \"%s\"", name);
722 break;
723 case -2:
724 err(1, NULL((void*)0));
725 }
726
727 switch (cgetstr(buf, "method", &method)) {
728 case -1:
729 method = NULL((void*)0);
730 break;
731 case -2:
732 err(1, NULL((void*)0));
733 }
734
735 switch (cgetstr(buf, "file", &file)) {
736 case -1:
737 errx(1, "No file given for %slist %s",
738 black ? "black" : "white", name);
739 case -2:
740 err(1, NULL((void*)0));
741 default:
742 fd = open_file(method, file);
743 if (fd == -1)
744 err(1, "Can't open %s by %s method",
745 file, method ? method : "file");
746 free(method);
747 free(file);
748 gzf = gzdopen(fd, "r");
749 if (gzf == NULL((void*)0))
750 errx(1, "gzdopen");
751 }
752 free(buf);
753 bl = add_blacklist(bl, &blc, &bls, gzf, !black);
754 serror = errno(*__errno());
755 gzclose(gzf);
756 if (bl == NULL((void*)0)) {
757 errno(*__errno()) = serror;
758 warn("Could not add %slist %s", black ? "black" : "white",
759 name);
760 return (0);
761 }
762 if (black) {
763 if (debug)
764 fprintf(stderr(&__sF[2]), "blacklist %s %zu entries\n",
765 name, blc / 2);
766 blistnew->message = message;
767 blistnew->name = name;
768 blistnew->black = black;
769 blistnew->bl = bl;
770 blistnew->blc = blc;
771 blistnew->bls = bls;
772 } else {
773 /* whitelist applied to last active blacklist */
774 if (debug)
775 fprintf(stderr(&__sF[2]), "whitelist %s %zu entries\n",
776 name, (blc - blist->blc) / 2);
777 blist->bl = bl;
778 blist->blc = blc;
779 blist->bls = bls;
780 }
781 return (black);
782}
783
784void
785send_blacklist(struct blacklist *blist, in_port_t port)
786{
787 struct cidr *cidrs;
788 u_int clc;
789
790 if (blist->blc > 0) {
791 cidrs = collapse_blacklist(blist->bl, blist->blc, &clc);
792 if (cidrs == NULL((void*)0))
793 err(1, NULL((void*)0));
794 if (!dryrun) {
795 if (configure_spamd(port, blist->name,
796 blist->message, cidrs, clc) == -1)
797 err(1, "Can't connect to spamd on port %d",
798 port);
799 if (!greyonly && configure_pf(cidrs) == -1)
800 err(1, "pfctl failed");
801 }
802 free(cidrs);
803 free(blist->bl);
804 }
805}
806
807__dead__attribute__((__noreturn__)) void
808usage(void)
809{
810
811 fprintf(stderr(&__sF[2]), "usage: %s [-bDdn]\n", __progname);
812 exit(1);
813}
814
815int
816main(int argc, char *argv[])
817{
818 size_t blc, bls, black, white;
819 char *db_array[2], *buf, *name;
820 struct blacklist *blists;
821 struct servent *ent;
822 int daemonize = 0, ch;
823 struct passwd *pw;
824
825 while ((ch = getopt(argc, argv, "bdDn")) != -1) {
826 switch (ch) {
827 case 'n':
828 dryrun = 1;
829 break;
830 case 'd':
831 debug = 1;
832 break;
833 case 'b':
834 greyonly = 0;
835 break;
836 case 'D':
837 daemonize = 1;
838 break;
839 default:
840 usage();
841 break;
842 }
843 }
844 argc -= optind;
845 argv += optind;
846 if (argc != 0)
847 usage();
848
849 if ((pw = getpwnam(SPAMD_USER"_spamd")) == NULL((void*)0))
850 errx(1, "cannot find user %s", SPAMD_USER"_spamd");
851 spamd_uid = pw->pw_uid;
852 spamd_gid = pw->pw_gid;
853
854 if (pledge("stdio rpath inet proc exec id", NULL((void*)0)) == -1)
855 err(1, "pledge");
856
857 if (daemonize)
858 daemon(0, 0);
859 else if (chdir("/") != 0)
860 err(1, "chdir(\"/\")");
861
862 if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL((void*)0))
863 errx(1, "cannot find service \"spamd-cfg\" in /etc/services");
864 ent->s_port = ntohs(ent->s_port)(__uint16_t)(__builtin_constant_p(ent->s_port) ? (__uint16_t
)(((__uint16_t)(ent->s_port) & 0xffU) << 8 | ((__uint16_t
)(ent->s_port) & 0xff00U) >> 8) : __swap16md(ent
->s_port))
;
865
866 db_array[0] = PATH_SPAMD_CONF"/etc/mail/spamd.conf";
867 db_array[1] = NULL((void*)0);
868
869 if (cgetent(&buf, db_array, "all") != 0)
870 err(1, "Can't find \"all\" in spamd config");
871 name = strsep(&buf, ": \t"); /* skip "all" at start */
872 blists = NULL((void*)0);
873 blc = bls = 0;
874 while ((name = strsep(&buf, ": \t")) != NULL((void*)0)) {
875 if (*name) {
876 /* extract config in order specified in "all" tag */
877 if (blc == bls) {
878 struct blacklist *tmp;
879
880 bls += 32;
881 tmp = reallocarray(blists, bls,
882 sizeof(struct blacklist));
883 if (tmp == NULL((void*)0))
884 err(1, NULL((void*)0));
885 blists = tmp;
886 }
887 if (blc == 0)
888 black = white = 0;
889 else {
890 white = blc - 1;
891 black = blc;
892 }
893 memset(&blists[black], 0, sizeof(struct blacklist));
894 black = getlist(db_array, name, &blists[white],
895 &blists[black]);
896 if (black && blc > 0) {
897 /* collapse and free previous blacklist */
898 send_blacklist(&blists[blc - 1], ent->s_port);
899 }
900 blc += black;
901 }
902 }
903 /* collapse and free last blacklist */
904 if (blc > 0)
905 send_blacklist(&blists[blc - 1], ent->s_port);
906 return (0);
907}