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