1 /* $OpenBSD: sync.c,v 1.13 2019/06/28 13:32:53 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/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)) == -1) { 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 } 575