GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/spamlogd/../spamd/sync.c Lines: 0 306 0.0 %
Date: 2017-11-13 Branches: 0 117 0.0 %

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