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