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