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