xref: /openbsd-src/libexec/spamd/sync.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: sync.c,v 1.8 2009/04/20 17:42:21 beck Exp $	*/
2 
3 /*
4  * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
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/param.h>
20 #include <sys/stdint.h>
21 #include <sys/file.h>
22 #include <sys/wait.h>
23 #include <sys/socket.h>
24 #include <sys/resource.h>
25 #include <sys/uio.h>
26 #include <sys/ioctl.h>
27 #include <sys/queue.h>
28 
29 #include <net/if.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 
33 #include <err.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <sha1.h>
42 #include <syslog.h>
43 
44 #include <netdb.h>
45 
46 #include <openssl/hmac.h>
47 
48 #include "sdl.h"
49 #include "grey.h"
50 #include "sync.h"
51 
52 extern struct syslog_data sdata;
53 extern int debug;
54 extern FILE *grey;
55 extern int greylist;
56 
57 u_int32_t sync_counter;
58 int syncfd;
59 int sendmcast;
60 struct sockaddr_in sync_in;
61 struct sockaddr_in sync_out;
62 static char *sync_key;
63 
64 struct sync_host {
65 	LIST_ENTRY(sync_host)	h_entry;
66 
67 	char			*h_name;
68 	struct sockaddr_in	sh_addr;
69 };
70 LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts);
71 
72 void	 sync_send(struct iovec *, int);
73 void	 sync_addr(time_t, time_t, char *, u_int16_t);
74 
75 int
76 sync_addhost(const char *name, u_short port)
77 {
78 	struct addrinfo hints, *res, *res0;
79 	struct sync_host *shost;
80 	struct sockaddr_in *addr = NULL;
81 
82 	bzero(&hints, sizeof(hints));
83 	hints.ai_family = PF_UNSPEC;
84 	hints.ai_socktype = SOCK_STREAM;
85 	if (getaddrinfo(name, NULL, &hints, &res0) != 0)
86 		return (EINVAL);
87 	for (res = res0; res != NULL; res = res->ai_next) {
88 		if (addr == NULL && res->ai_family == AF_INET) {
89 			addr = (struct sockaddr_in *)res->ai_addr;
90 			break;
91 		}
92 	}
93 	if (addr == NULL) {
94 		freeaddrinfo(res0);
95 		return (EINVAL);
96 	}
97 	if ((shost = (struct sync_host *)
98 	    calloc(1, sizeof(struct sync_host))) == NULL) {
99 		freeaddrinfo(res0);
100 		return (ENOMEM);
101 	}
102 	if ((shost->h_name = strdup(name)) == NULL) {
103 		free(shost);
104 		freeaddrinfo(res0);
105 		return (ENOMEM);
106 	}
107 
108 	shost->sh_addr.sin_family = AF_INET;
109 	shost->sh_addr.sin_port = htons(port);
110 	shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
111 	freeaddrinfo(res0);
112 
113 	LIST_INSERT_HEAD(&sync_hosts, shost, h_entry);
114 
115 	if (debug)
116 		fprintf(stderr, "added spam sync host %s "
117 		    "(address %s, port %d)\n", shost->h_name,
118 		    inet_ntoa(shost->sh_addr.sin_addr), port);
119 
120 	return (0);
121 }
122 
123 int
124 sync_init(const char *iface, const char *baddr, u_short port)
125 {
126 	int one = 1;
127 	u_int8_t ttl;
128 	struct ifreq ifr;
129 	struct ip_mreq mreq;
130 	struct sockaddr_in *addr;
131 	char ifnam[IFNAMSIZ], *ttlstr;
132 	const char *errstr;
133 	struct in_addr ina;
134 
135 	if (iface != NULL)
136 		sendmcast++;
137 
138 	bzero(&ina, sizeof(ina));
139 	if (baddr != NULL) {
140 		if (inet_pton(AF_INET, baddr, &ina) != 1) {
141 			ina.s_addr = htonl(INADDR_ANY);
142 			if (iface == NULL)
143 				iface = baddr;
144 			else if (iface != NULL && strcmp(baddr, iface) != 0) {
145 				fprintf(stderr, "multicast interface does "
146 				    "not match");
147 				return (-1);
148 			}
149 		}
150 	}
151 
152 	sync_key = SHA1File(SPAM_SYNC_KEY, NULL);
153 	if (sync_key == NULL) {
154 		if (errno != ENOENT) {
155 			fprintf(stderr, "failed to open sync key: %s\n",
156 			    strerror(errno));
157 			return (-1);
158 		}
159 		/* Use empty key by default */
160 		sync_key = "";
161 	}
162 
163 	syncfd = socket(AF_INET, SOCK_DGRAM, 0);
164 	if (syncfd == -1)
165 		return (-1);
166 
167 	if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one,
168 	    sizeof(one)) == -1)
169 		goto fail;
170 
171 	bzero(&sync_out, sizeof(sync_out));
172 	sync_out.sin_family = AF_INET;
173 	sync_out.sin_len = sizeof(sync_out);
174 	sync_out.sin_addr.s_addr = ina.s_addr;
175 	if (baddr == NULL && iface == NULL)
176 		sync_out.sin_port = 0;
177 	else
178 		sync_out.sin_port = htons(port);
179 
180 	if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1)
181 		goto fail;
182 
183 	/* Don't use multicast messages */
184 	if (iface == NULL)
185 		return (syncfd);
186 
187 	strlcpy(ifnam, iface, sizeof(ifnam));
188 	ttl = SPAM_SYNC_MCASTTTL;
189 	if ((ttlstr = strchr(ifnam, ':')) != NULL) {
190 		*ttlstr++ = '\0';
191 		ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr);
192 		if (errstr) {
193 			fprintf(stderr, "invalid multicast ttl %s: %s",
194 			    ttlstr, errstr);
195 			goto fail;
196 		}
197 	}
198 
199 	bzero(&ifr, sizeof(ifr));
200 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
201 	if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1)
202 		goto fail;
203 
204 	bzero(&sync_in, sizeof(sync_in));
205 	addr = (struct sockaddr_in *)&ifr.ifr_addr;
206 	sync_in.sin_family = AF_INET;
207 	sync_in.sin_len = sizeof(sync_in);
208 	sync_in.sin_addr.s_addr = addr->sin_addr.s_addr;
209 	sync_in.sin_port = htons(port);
210 
211 	bzero(&mreq, sizeof(mreq));
212 	sync_out.sin_addr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
213 	mreq.imr_multiaddr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
214 	mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr;
215 
216 	if (setsockopt(syncfd, IPPROTO_IP,
217 	    IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
218 		fprintf(stderr, "failed to add multicast membership to %s: %s",
219 		    SPAM_SYNC_MCASTADDR, strerror(errno));
220 		goto fail;
221 	}
222 	if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
223 	    sizeof(ttl)) < 0) {
224 		fprintf(stderr, "failed to set multicast ttl to "
225 		    "%u: %s\n", ttl, strerror(errno));
226 		setsockopt(syncfd, IPPROTO_IP,
227 		    IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
228 		goto fail;
229 	}
230 
231 	if (debug)
232 		printf("using multicast spam sync %smode "
233 		    "(ttl %u, group %s, port %d)\n",
234 		    sendmcast ? "" : "receive ",
235 		    ttl, inet_ntoa(sync_out.sin_addr), port);
236 
237 	return (syncfd);
238 
239  fail:
240 	close(syncfd);
241 	return (-1);
242 }
243 
244 void
245 sync_recv(void)
246 {
247 	struct spam_synchdr *hdr;
248 	struct sockaddr_in addr;
249 	struct spam_synctlv_hdr *tlv;
250 	struct spam_synctlv_grey *sg;
251 	struct spam_synctlv_addr *sd;
252 	u_int8_t buf[SPAM_SYNC_MAXSIZE];
253 	u_int8_t hmac[2][SPAM_SYNC_HMAC_LEN];
254 	struct in_addr ip;
255 	char *from, *to, *helo;
256 	u_int8_t *p;
257 	socklen_t addr_len;
258 	ssize_t len;
259 	u_int hmac_len;
260 	time_t expire;
261 
262 	bzero(&addr, sizeof(addr));
263 	bzero(buf, sizeof(buf));
264 
265 	addr_len = sizeof(addr);
266 	if ((len = recvfrom(syncfd, buf, sizeof(buf), 0,
267 	    (struct sockaddr *)&addr, &addr_len)) < 1)
268 		return;
269 	if (addr.sin_addr.s_addr != htonl(INADDR_ANY) &&
270 	    bcmp(&sync_in.sin_addr, &addr.sin_addr,
271 	    sizeof(addr.sin_addr)) == 0)
272 		return;
273 
274 	/* Ignore invalid or truncated packets */
275 	hdr = (struct spam_synchdr *)buf;
276 	if (len < sizeof(struct spam_synchdr) ||
277 	    hdr->sh_version != SPAM_SYNC_VERSION ||
278 	    hdr->sh_af != AF_INET ||
279 	    len < ntohs(hdr->sh_length))
280 		goto trunc;
281 	len = ntohs(hdr->sh_length);
282 
283 	/* Compute and validate HMAC */
284 	bcopy(hdr->sh_hmac, hmac[0], SPAM_SYNC_HMAC_LEN);
285 	bzero(hdr->sh_hmac, SPAM_SYNC_HMAC_LEN);
286 	HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len,
287 	    hmac[1], &hmac_len);
288 	if (bcmp(hmac[0], hmac[1], SPAM_SYNC_HMAC_LEN) != 0)
289 		goto trunc;
290 
291 	if (debug)
292 		fprintf(stderr,
293 		    "%s(sync): received packet of %d bytes\n",
294 		    inet_ntoa(addr.sin_addr), (int)len);
295 
296 	p = (u_int8_t *)(hdr + 1);
297 	while (len) {
298 		tlv = (struct spam_synctlv_hdr *)p;
299 
300 		if (len < sizeof(struct spam_synctlv_hdr) ||
301 		    len < ntohs(tlv->st_length))
302 			goto trunc;
303 
304 		switch (ntohs(tlv->st_type)) {
305 		case SPAM_SYNC_GREY:
306 			sg = (struct spam_synctlv_grey *)tlv;
307 			if ((sizeof(*sg) +
308 			    ntohs(sg->sg_from_length) +
309 			    ntohs(sg->sg_to_length) +
310 			    ntohs(sg->sg_helo_length)) >
311 			    ntohs(tlv->st_length))
312 				goto trunc;
313 
314 			ip.s_addr = sg->sg_ip;
315 			from = (char *)(sg + 1);
316 			to = from + ntohs(sg->sg_from_length);
317 			helo = to + ntohs(sg->sg_to_length);
318 			if (debug) {
319 				fprintf(stderr, "%s(sync): "
320 				    "received grey entry ",
321 				    inet_ntoa(addr.sin_addr));
322 				fprintf(stderr, "helo %s ip %s "
323 				    "from %s to %s\n",
324 				    helo, inet_ntoa(ip), from, to);
325 			}
326 			if (greylist) {
327 				/* send this info to the greylister */
328 				fprintf(grey,
329 				    "SYNC\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
330 				    helo, inet_ntoa(ip), from, to);
331 				fflush(grey);
332 			}
333 			break;
334 		case SPAM_SYNC_WHITE:
335 			sd = (struct spam_synctlv_addr *)tlv;
336 			if (sizeof(*sd) != ntohs(tlv->st_length))
337 				goto trunc;
338 
339 			ip.s_addr = sd->sd_ip;
340 			expire = ntohl(sd->sd_expire);
341 			if (debug) {
342 				fprintf(stderr, "%s(sync): "
343 				    "received white entry ",
344 				    inet_ntoa(addr.sin_addr));
345 				fprintf(stderr, "ip %s ", inet_ntoa(ip));
346 			}
347 			if (greylist) {
348 				/* send this info to the greylister */
349 				fprintf(grey, "WHITE:%s:", inet_ntoa(ip));
350 				fprintf(grey, "%s:%u\n",
351 				    inet_ntoa(addr.sin_addr), expire);
352 				fflush(grey);
353 			}
354 			break;
355 		case SPAM_SYNC_TRAPPED:
356 			sd = (struct spam_synctlv_addr *)tlv;
357 			if (sizeof(*sd) != ntohs(tlv->st_length))
358 				goto trunc;
359 
360 			ip.s_addr = sd->sd_ip;
361 			expire = ntohl(sd->sd_expire);
362 			if (debug) {
363 				fprintf(stderr, "%s(sync): "
364 				    "received trapped entry ",
365 				    inet_ntoa(addr.sin_addr));
366 				fprintf(stderr, "ip %s ", inet_ntoa(ip));
367 			}
368 			if (greylist) {
369 				/* send this info to the greylister */
370 				fprintf(grey, "TRAP:%s:", inet_ntoa(ip));
371 				fprintf(grey, "%s:%u\n",
372 				    inet_ntoa(addr.sin_addr), expire);
373 				fflush(grey);
374 			}
375 			break;
376 		case SPAM_SYNC_END:
377 			goto done;
378 		default:
379 			printf("invalid type: %d\n", ntohs(tlv->st_type));
380 			goto trunc;
381 		}
382 		len -= ntohs(tlv->st_length);
383 		p = ((u_int8_t *)tlv) + ntohs(tlv->st_length);
384 	}
385 
386  done:
387 	return;
388 
389  trunc:
390 	if (debug)
391 		fprintf(stderr, "%s(sync): truncated or invalid packet\n",
392 		    inet_ntoa(addr.sin_addr));
393 }
394 
395 void
396 sync_send(struct iovec *iov, int iovlen)
397 {
398 	struct sync_host *shost;
399 	struct msghdr msg;
400 
401 	/* setup buffer */
402 	bzero(&msg, sizeof(msg));
403 	msg.msg_iov = iov;
404 	msg.msg_iovlen = iovlen;
405 
406 	if (sendmcast) {
407 		if (debug)
408 			fprintf(stderr, "sending multicast sync message\n");
409 		msg.msg_name = &sync_out;
410 		msg.msg_namelen = sizeof(sync_out);
411 		sendmsg(syncfd, &msg, 0);
412 	}
413 
414 	LIST_FOREACH(shost, &sync_hosts, h_entry) {
415 		if (debug)
416 			fprintf(stderr, "sending sync message to %s (%s)\n",
417 			    shost->h_name, inet_ntoa(shost->sh_addr.sin_addr));
418 		msg.msg_name = &shost->sh_addr;
419 		msg.msg_namelen = sizeof(shost->sh_addr);
420 		sendmsg(syncfd, &msg, 0);
421 	}
422 }
423 
424 void
425 sync_update(time_t now, char *helo, char *ip, char *from, char *to)
426 {
427 	struct iovec iov[7];
428 	struct spam_synchdr hdr;
429 	struct spam_synctlv_grey sg;
430 	struct spam_synctlv_hdr end;
431 	u_int16_t sglen, fromlen, tolen, helolen, padlen;
432 	char pad[SPAM_ALIGNBYTES];
433 	int i = 0;
434 	HMAC_CTX ctx;
435 	u_int hmac_len;
436 
437 	if (debug)
438 		fprintf(stderr,
439 		    "sync grey update helo %s ip %s from %s to %s\n",
440 		    helo, ip, from, to);
441 
442 	bzero(&hdr, sizeof(hdr));
443 	bzero(&sg, sizeof(sg));
444 	bzero(&pad, sizeof(pad));
445 
446 	fromlen = strlen(from) + 1;
447 	tolen = strlen(to) + 1;
448 	helolen = strlen(helo) + 1;
449 
450 	HMAC_CTX_init(&ctx);
451 	HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
452 
453 	sglen = sizeof(sg) + fromlen + tolen + helolen;
454 	padlen = SPAM_ALIGN(sglen) - sglen;
455 
456 	/* Add SPAM sync packet header */
457 	hdr.sh_version = SPAM_SYNC_VERSION;
458 	hdr.sh_af = AF_INET;
459 	hdr.sh_counter = htonl(sync_counter++);
460 	hdr.sh_length = htons(sizeof(hdr) + sglen + padlen + sizeof(end));
461 	iov[i].iov_base = &hdr;
462 	iov[i].iov_len = sizeof(hdr);
463 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
464 	i++;
465 
466 	/* Add single SPAM sync greylisting entry */
467 	sg.sg_type = htons(SPAM_SYNC_GREY);
468 	sg.sg_length = htons(sglen + padlen);
469 	sg.sg_timestamp = htonl(now);
470 	sg.sg_ip = inet_addr(ip);
471 	sg.sg_from_length = htons(fromlen);
472 	sg.sg_to_length = htons(tolen);
473 	sg.sg_helo_length = htons(helolen);
474 	iov[i].iov_base = &sg;
475 	iov[i].iov_len = sizeof(sg);
476 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
477 	i++;
478 
479 	iov[i].iov_base = from;
480 	iov[i].iov_len = fromlen;
481 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
482 	i++;
483 
484 	iov[i].iov_base = to;
485 	iov[i].iov_len = tolen;
486 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
487 	i++;
488 
489 	iov[i].iov_base = helo;
490 	iov[i].iov_len = helolen;
491 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
492 	i++;
493 
494 	iov[i].iov_base = pad;
495 	iov[i].iov_len = padlen;
496 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
497 	i++;
498 
499 	/* Add end marker */
500 	end.st_type = htons(SPAM_SYNC_END);
501 	end.st_length = htons(sizeof(end));
502 	iov[i].iov_base = &end;
503 	iov[i].iov_len = sizeof(end);
504 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
505 	i++;
506 
507 	HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
508 
509 	/* Send message to the target hosts */
510 	sync_send(iov, i);
511 	HMAC_CTX_cleanup(&ctx);
512 }
513 
514 void
515 sync_addr(time_t now, time_t expire, char *ip, u_int16_t type)
516 {
517 	struct iovec iov[3];
518 	struct spam_synchdr hdr;
519 	struct spam_synctlv_addr sd;
520 	struct spam_synctlv_hdr end;
521 	int i = 0;
522 	HMAC_CTX ctx;
523 	u_int hmac_len;
524 
525 	if (debug)
526 		fprintf(stderr, "sync %s %s\n",
527 			type == SPAM_SYNC_WHITE ? "white" : "trapped", ip);
528 
529 	bzero(&hdr, sizeof(hdr));
530 	bzero(&sd, sizeof(sd));
531 
532 	HMAC_CTX_init(&ctx);
533 	HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
534 
535 	/* Add SPAM sync packet header */
536 	hdr.sh_version = SPAM_SYNC_VERSION;
537 	hdr.sh_af = AF_INET;
538 	hdr.sh_counter = htonl(sync_counter++);
539 	hdr.sh_length = htons(sizeof(hdr) + sizeof(sd) + sizeof(end));
540 	iov[i].iov_base = &hdr;
541 	iov[i].iov_len = sizeof(hdr);
542 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
543 	i++;
544 
545 	/* Add single SPAM sync address entry */
546 	sd.sd_type = htons(type);
547 	sd.sd_length = htons(sizeof(sd));
548 	sd.sd_timestamp = htonl(now);
549 	sd.sd_expire = htonl(expire);
550 	sd.sd_ip = inet_addr(ip);
551 	iov[i].iov_base = &sd;
552 	iov[i].iov_len = sizeof(sd);
553 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
554 	i++;
555 
556 	/* Add end marker */
557 	end.st_type = htons(SPAM_SYNC_END);
558 	end.st_length = htons(sizeof(end));
559 	iov[i].iov_base = &end;
560 	iov[i].iov_len = sizeof(end);
561 	HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
562 	i++;
563 
564 	HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
565 
566 	/* Send message to the target hosts */
567 	sync_send(iov, i);
568 	HMAC_CTX_cleanup(&ctx);
569 }
570 
571 void
572 sync_white(time_t now, time_t expire, char *ip)
573 {
574 	sync_addr(now, expire, ip, SPAM_SYNC_WHITE);
575 }
576 
577 void
578 sync_trapped(time_t now, time_t expire, char *ip)
579 {
580 	sync_addr(now, expire, ip, SPAM_SYNC_TRAPPED);
581 }
582