1 /* $NetBSD: connect.c,v 1.5 2023/06/19 21:41:41 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "kdc_locl.h" 37 38 /* 39 * a tuple describing on what to listen 40 */ 41 42 struct port_desc{ 43 int family; 44 int type; 45 int port; 46 }; 47 48 /* the current ones */ 49 50 static struct port_desc *ports; 51 static size_t num_ports; 52 static pid_t bonjour_pid = -1; 53 54 /* 55 * add `family, port, protocol' to the list with duplicate suppresion. 56 */ 57 58 static void 59 add_port(krb5_context context, 60 int family, int port, const char *protocol) 61 { 62 int type; 63 size_t i; 64 65 if(strcmp(protocol, "udp") == 0) 66 type = SOCK_DGRAM; 67 else if(strcmp(protocol, "tcp") == 0) 68 type = SOCK_STREAM; 69 else 70 return; 71 for(i = 0; i < num_ports; i++){ 72 if(ports[i].type == type 73 && ports[i].port == port 74 && ports[i].family == family) 75 return; 76 } 77 ports = realloc(ports, (num_ports + 1) * sizeof(*ports)); 78 if (ports == NULL) 79 krb5_err (context, 1, errno, "realloc"); 80 ports[num_ports].family = family; 81 ports[num_ports].type = type; 82 ports[num_ports].port = port; 83 num_ports++; 84 } 85 86 /* 87 * add a triple but with service -> port lookup 88 * (this prints warnings for stuff that does not exist) 89 */ 90 91 static void 92 add_port_service(krb5_context context, 93 int family, const char *service, int port, 94 const char *protocol) 95 { 96 port = krb5_getportbyname (context, service, protocol, port); 97 add_port (context, family, port, protocol); 98 } 99 100 /* 101 * add the port with service -> port lookup or string -> number 102 * (no warning is printed) 103 */ 104 105 static void 106 add_port_string (krb5_context context, 107 int family, const char *str, const char *protocol) 108 { 109 struct servent *sp; 110 int port; 111 112 sp = roken_getservbyname (str, protocol); 113 if (sp != NULL) { 114 port = sp->s_port; 115 } else { 116 char *end; 117 118 port = htons(strtol(str, &end, 0)); 119 if (end == str) 120 return; 121 } 122 add_port (context, family, port, protocol); 123 } 124 125 /* 126 * add the standard collection of ports for `family' 127 */ 128 129 static void 130 add_standard_ports (krb5_context context, 131 krb5_kdc_configuration *config, 132 int family) 133 { 134 add_port_service(context, family, "kerberos", 88, "udp"); 135 add_port_service(context, family, "kerberos", 88, "tcp"); 136 add_port_service(context, family, "kerberos-sec", 88, "udp"); 137 add_port_service(context, family, "kerberos-sec", 88, "tcp"); 138 if(enable_http) 139 add_port_service(context, family, "http", 80, "tcp"); 140 if(config->enable_kx509) { 141 add_port_service(context, family, "kca_service", 9878, "udp"); 142 add_port_service(context, family, "kca_service", 9878, "tcp"); 143 } 144 145 } 146 147 /* 148 * parse the set of space-delimited ports in `str' and add them. 149 * "+" => all the standard ones 150 * otherwise it's port|service[/protocol] 151 */ 152 153 static void 154 parse_ports(krb5_context context, 155 krb5_kdc_configuration *config, 156 const char *str) 157 { 158 char *pos = NULL; 159 char *p; 160 char *str_copy = strdup (str); 161 162 p = strtok_r(str_copy, " \t", &pos); 163 while(p != NULL) { 164 if(strcmp(p, "+") == 0) { 165 #ifdef HAVE_IPV6 166 add_standard_ports(context, config, AF_INET6); 167 #endif 168 add_standard_ports(context, config, AF_INET); 169 } else { 170 char *q = strchr(p, '/'); 171 if(q){ 172 *q++ = 0; 173 #ifdef HAVE_IPV6 174 add_port_string(context, AF_INET6, p, q); 175 #endif 176 add_port_string(context, AF_INET, p, q); 177 }else { 178 #ifdef HAVE_IPV6 179 add_port_string(context, AF_INET6, p, "udp"); 180 add_port_string(context, AF_INET6, p, "tcp"); 181 #endif 182 add_port_string(context, AF_INET, p, "udp"); 183 add_port_string(context, AF_INET, p, "tcp"); 184 } 185 } 186 187 p = strtok_r(NULL, " \t", &pos); 188 } 189 free (str_copy); 190 } 191 192 /* 193 * every socket we listen on 194 */ 195 196 struct descr { 197 krb5_socket_t s; 198 int type; 199 int port; 200 unsigned char *buf; 201 size_t size; 202 size_t len; 203 time_t timeout; 204 struct sockaddr_storage __ss; 205 struct sockaddr *sa; 206 socklen_t sock_len; 207 char addr_string[128]; 208 }; 209 210 static void 211 init_descr(struct descr *d) 212 { 213 memset(d, 0, sizeof(*d)); 214 d->sa = (struct sockaddr *)&d->__ss; 215 d->s = rk_INVALID_SOCKET; 216 } 217 218 /* 219 * re-initialize all `n' ->sa in `d'. 220 */ 221 222 static void 223 reinit_descrs (struct descr *d, int n) 224 { 225 int i; 226 227 for (i = 0; i < n; ++i) 228 d[i].sa = (struct sockaddr *)&d[i].__ss; 229 } 230 231 /* 232 * Create the socket (family, type, port) in `d' 233 */ 234 235 static void 236 init_socket(krb5_context context, 237 krb5_kdc_configuration *config, 238 struct descr *d, krb5_address *a, int family, int type, int port) 239 { 240 krb5_error_code ret; 241 struct sockaddr_storage __ss; 242 struct sockaddr *sa = (struct sockaddr *)&__ss; 243 krb5_socklen_t sa_size = sizeof(__ss); 244 245 init_descr (d); 246 247 ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port); 248 if (ret) { 249 krb5_warn(context, ret, "krb5_addr2sockaddr"); 250 rk_closesocket(d->s); 251 d->s = rk_INVALID_SOCKET; 252 return; 253 } 254 255 if (sa->sa_family != family) 256 return; 257 258 d->s = socket(family, type, 0); 259 if(rk_IS_BAD_SOCKET(d->s)){ 260 krb5_warn(context, errno, "socket(%d, %d, 0)", family, type); 261 d->s = rk_INVALID_SOCKET; 262 return; 263 } 264 rk_cloexec(d->s); 265 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR) 266 { 267 int one = 1; 268 setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 269 } 270 #endif 271 d->type = type; 272 d->port = port; 273 274 socket_set_nonblocking(d->s, 1); 275 276 if(rk_IS_SOCKET_ERROR(bind(d->s, sa, sa_size))){ 277 char a_str[256]; 278 size_t len; 279 280 krb5_print_address (a, a_str, sizeof(a_str), &len); 281 krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port)); 282 rk_closesocket(d->s); 283 d->s = rk_INVALID_SOCKET; 284 return; 285 } 286 if(type == SOCK_STREAM && rk_IS_SOCKET_ERROR(listen(d->s, SOMAXCONN))){ 287 char a_str[256]; 288 size_t len; 289 290 krb5_print_address (a, a_str, sizeof(a_str), &len); 291 krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port)); 292 rk_closesocket(d->s); 293 d->s = rk_INVALID_SOCKET; 294 return; 295 } 296 } 297 298 /* 299 * Allocate descriptors for all the sockets that we should listen on 300 * and return the number of them. 301 */ 302 303 static int 304 init_sockets(krb5_context context, 305 krb5_kdc_configuration *config, 306 struct descr **desc) 307 { 308 krb5_error_code ret; 309 size_t i, j; 310 struct descr *d; 311 int num = 0; 312 krb5_addresses addresses; 313 314 if (explicit_addresses.len) { 315 addresses = explicit_addresses; 316 } else { 317 ret = krb5_get_all_server_addrs (context, &addresses); 318 if (ret) 319 krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); 320 } 321 parse_ports(context, config, port_str); 322 d = malloc(addresses.len * num_ports * sizeof(*d)); 323 if (d == NULL) 324 krb5_errx(context, 1, "malloc(%lu) failed", 325 (unsigned long)num_ports * sizeof(*d)); 326 327 for (i = 0; i < num_ports; i++){ 328 for (j = 0; j < addresses.len; ++j) { 329 init_socket(context, config, &d[num], &addresses.val[j], 330 ports[i].family, ports[i].type, ports[i].port); 331 if(d[num].s != rk_INVALID_SOCKET){ 332 char a_str[80]; 333 size_t len; 334 335 krb5_print_address (&addresses.val[j], a_str, 336 sizeof(a_str), &len); 337 338 kdc_log(context, config, 5, "listening on %s port %u/%s", 339 a_str, 340 ntohs(ports[i].port), 341 (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); 342 /* XXX */ 343 num++; 344 } 345 } 346 } 347 krb5_free_addresses (context, &addresses); 348 d = realloc(d, num * sizeof(*d)); 349 if (d == NULL && num != 0) 350 krb5_errx(context, 1, "realloc(%lu) failed", 351 (unsigned long)num * sizeof(*d)); 352 reinit_descrs (d, num); 353 *desc = d; 354 return num; 355 } 356 357 /* 358 * 359 */ 360 361 static const char * 362 descr_type(struct descr *d) 363 { 364 if (d->type == SOCK_DGRAM) 365 return "udp"; 366 else if (d->type == SOCK_STREAM) 367 return "tcp"; 368 return "unknown"; 369 } 370 371 static void 372 addr_to_string(krb5_context context, 373 struct sockaddr *addr, size_t addr_len, char *str, size_t len) 374 { 375 krb5_address a; 376 if(krb5_sockaddr2address(context, addr, &a) == 0) { 377 if(krb5_print_address(&a, str, len, &len) == 0) { 378 krb5_free_address(context, &a); 379 return; 380 } 381 krb5_free_address(context, &a); 382 } 383 snprintf(str, len, "<family=%d>", addr->sa_family); 384 } 385 386 /* 387 * 388 */ 389 390 static void 391 send_reply(krb5_context context, 392 krb5_kdc_configuration *config, 393 krb5_boolean prependlength, 394 struct descr *d, 395 krb5_data *reply) 396 { 397 kdc_log(context, config, 5, 398 "sending %lu bytes to %s", (unsigned long)reply->length, 399 d->addr_string); 400 if(prependlength){ 401 unsigned char l[4]; 402 l[0] = (reply->length >> 24) & 0xff; 403 l[1] = (reply->length >> 16) & 0xff; 404 l[2] = (reply->length >> 8) & 0xff; 405 l[3] = reply->length & 0xff; 406 if(rk_IS_SOCKET_ERROR(sendto(d->s, l, sizeof(l), 0, d->sa, d->sock_len))) { 407 kdc_log (context, config, 408 0, "sendto(%s): %s", d->addr_string, 409 strerror(rk_SOCK_ERRNO)); 410 return; 411 } 412 } 413 if(rk_IS_SOCKET_ERROR(sendto(d->s, reply->data, reply->length, 0, d->sa, d->sock_len))) { 414 kdc_log (context, config, 0, "sendto(%s): %s", d->addr_string, 415 strerror(rk_SOCK_ERRNO)); 416 return; 417 } 418 } 419 420 /* 421 * Handle the request in `buf, len' to socket `d' 422 */ 423 424 static void 425 do_request(krb5_context context, 426 krb5_kdc_configuration *config, 427 void *buf, size_t len, krb5_boolean prependlength, 428 struct descr *d) 429 { 430 krb5_error_code ret; 431 krb5_data reply; 432 int datagram_reply = (d->type == SOCK_DGRAM); 433 434 krb5_kdc_update_time(NULL); 435 436 krb5_data_zero(&reply); 437 ret = krb5_kdc_process_request(context, config, 438 buf, len, &reply, &prependlength, 439 d->addr_string, d->sa, 440 datagram_reply); 441 if(request_log) 442 krb5_kdc_save_request(context, request_log, buf, len, &reply, d->sa); 443 if(reply.length){ 444 send_reply(context, config, prependlength, d, &reply); 445 krb5_data_free(&reply); 446 } 447 if(ret) 448 kdc_log(context, config, 0, 449 "Failed processing %lu byte request from %s", 450 (unsigned long)len, d->addr_string); 451 } 452 453 /* 454 * Handle incoming data to the UDP socket in `d' 455 */ 456 457 static void 458 handle_udp(krb5_context context, 459 krb5_kdc_configuration *config, 460 struct descr *d) 461 { 462 unsigned char *buf; 463 ssize_t n; 464 465 buf = malloc(max_request_udp); 466 if (buf == NULL){ 467 kdc_log(context, config, 0, "Failed to allocate %lu bytes", 468 (unsigned long)max_request_udp); 469 return; 470 } 471 472 d->sock_len = sizeof(d->__ss); 473 n = recvfrom(d->s, buf, max_request_udp, 0, d->sa, &d->sock_len); 474 if (rk_IS_SOCKET_ERROR(n)) { 475 if (rk_SOCK_ERRNO != EAGAIN && rk_SOCK_ERRNO != EINTR) 476 krb5_warn(context, rk_SOCK_ERRNO, "recvfrom"); 477 } else { 478 addr_to_string (context, d->sa, d->sock_len, 479 d->addr_string, sizeof(d->addr_string)); 480 if ((size_t)n == max_request_udp) { 481 krb5_data data; 482 krb5_warn(context, errno, 483 "recvfrom: truncated packet from %s, asking for TCP", 484 d->addr_string); 485 krb5_mk_error(context, 486 KRB5KRB_ERR_RESPONSE_TOO_BIG, 487 NULL, 488 NULL, 489 NULL, 490 NULL, 491 NULL, 492 NULL, 493 &data); 494 send_reply(context, config, FALSE, d, &data); 495 krb5_data_free(&data); 496 } else { 497 do_request(context, config, buf, n, FALSE, d); 498 } 499 } 500 free (buf); 501 } 502 503 static void 504 clear_descr(struct descr *d) 505 { 506 if(d->buf) 507 memset(d->buf, 0, d->size); 508 d->len = 0; 509 if(d->s != rk_INVALID_SOCKET) 510 rk_closesocket(d->s); 511 d->s = rk_INVALID_SOCKET; 512 } 513 514 515 /* remove HTTP %-quoting from buf */ 516 static int 517 de_http(char *buf) 518 { 519 unsigned char *p, *q; 520 unsigned int x; 521 522 for (p = q = (unsigned char *)buf; *p; p++, q++) { 523 if (*p == '%') { 524 if (!(isxdigit(p[1]) && isxdigit(p[2]))) 525 return -1; 526 527 if (sscanf((char *)p + 1, "%2x", &x) != 1) 528 return -1; 529 530 *q = x; 531 p += 2; 532 } else { 533 *q = *p; 534 } 535 } 536 *q = '\0'; 537 return 0; 538 } 539 540 #define TCP_TIMEOUT 4 541 542 /* 543 * accept a new TCP connection on `d[parent]' and store it in `d[child]' 544 */ 545 546 static void 547 add_new_tcp (krb5_context context, 548 krb5_kdc_configuration *config, 549 struct descr *d, int parent, int child) 550 { 551 krb5_socket_t s; 552 553 if (child == -1) 554 return; 555 556 d[child].sock_len = sizeof(d[child].__ss); 557 s = accept(d[parent].s, d[child].sa, &d[child].sock_len); 558 if(rk_IS_BAD_SOCKET(s)) { 559 if (rk_SOCK_ERRNO != EAGAIN && rk_SOCK_ERRNO != EINTR) 560 krb5_warn(context, rk_SOCK_ERRNO, "accept"); 561 return; 562 } 563 564 #ifdef FD_SETSIZE 565 if (s >= FD_SETSIZE) { 566 krb5_warnx(context, "socket FD too large"); 567 rk_closesocket (s); 568 return; 569 } 570 #endif 571 572 d[child].s = s; 573 d[child].timeout = time(NULL) + TCP_TIMEOUT; 574 d[child].type = SOCK_STREAM; 575 addr_to_string (context, 576 d[child].sa, d[child].sock_len, 577 d[child].addr_string, sizeof(d[child].addr_string)); 578 } 579 580 /* 581 * Grow `d' to handle at least `n'. 582 * Return != 0 if fails 583 */ 584 585 static int 586 grow_descr (krb5_context context, 587 krb5_kdc_configuration *config, 588 struct descr *d, size_t n) 589 { 590 if (d->size - d->len < n) { 591 unsigned char *tmp; 592 size_t grow; 593 594 grow = max(1024, d->len + n); 595 if (d->size + grow > max_request_tcp) { 596 kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).", 597 (unsigned long)d->size + grow); 598 clear_descr(d); 599 return -1; 600 } 601 tmp = realloc (d->buf, d->size + grow); 602 if (tmp == NULL) { 603 kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.", 604 (unsigned long)d->size + grow); 605 clear_descr(d); 606 return -1; 607 } 608 d->size += grow; 609 d->buf = tmp; 610 } 611 return 0; 612 } 613 614 /* 615 * Try to handle the TCP data at `d->buf, d->len'. 616 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 617 */ 618 619 static int 620 handle_vanilla_tcp (krb5_context context, 621 krb5_kdc_configuration *config, 622 struct descr *d) 623 { 624 krb5_storage *sp; 625 uint32_t len; 626 627 sp = krb5_storage_from_mem(d->buf, d->len); 628 if (sp == NULL) { 629 kdc_log (context, config, 0, "krb5_storage_from_mem failed"); 630 return -1; 631 } 632 krb5_ret_uint32(sp, &len); 633 krb5_storage_free(sp); 634 if(d->len - 4 >= len) { 635 memmove(d->buf, d->buf + 4, d->len - 4); 636 d->len -= 4; 637 return 1; 638 } 639 return 0; 640 } 641 642 /* 643 * Try to handle the TCP/HTTP data at `d->buf, d->len'. 644 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 645 */ 646 647 static int 648 handle_http_tcp (krb5_context context, 649 krb5_kdc_configuration *config, 650 struct descr *d) 651 { 652 char *s, *p, *t; 653 void *data; 654 char *proto; 655 int len; 656 657 s = (char *)d->buf; 658 659 /* If its a multi line query, truncate off the first line */ 660 p = strstr(s, "\r\n"); 661 if (p) 662 *p = 0; 663 664 p = NULL; 665 t = strtok_r(s, " \t", &p); 666 if (t == NULL) { 667 kdc_log(context, config, 0, 668 "Missing HTTP operand (GET) request from %s", d->addr_string); 669 return -1; 670 } 671 672 t = strtok_r(NULL, " \t", &p); 673 if(t == NULL) { 674 kdc_log(context, config, 0, 675 "Missing HTTP GET data in request from %s", d->addr_string); 676 return -1; 677 } 678 679 data = malloc(strlen(t)); 680 if (data == NULL) { 681 kdc_log(context, config, 0, "Failed to allocate %lu bytes", 682 (unsigned long)strlen(t)); 683 return -1; 684 } 685 if(*t == '/') 686 t++; 687 if(de_http(t) != 0) { 688 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 689 kdc_log(context, config, 5, "HTTP request: %s", t); 690 free(data); 691 return -1; 692 } 693 proto = strtok_r(NULL, " \t", &p); 694 if (proto == NULL) { 695 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 696 free(data); 697 return -1; 698 } 699 len = rk_base64_decode(t, data); 700 if(len <= 0){ 701 const char *msg = 702 " 404 Not found\r\n" 703 "Server: Heimdal/" VERSION "\r\n" 704 "Cache-Control: no-cache\r\n" 705 "Pragma: no-cache\r\n" 706 "Content-type: text/html\r\n" 707 "Content-transfer-encoding: 8bit\r\n\r\n" 708 "<TITLE>404 Not found</TITLE>\r\n" 709 "<H1>404 Not found</H1>\r\n" 710 "That page doesn't exist, maybe you are looking for " 711 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n"; 712 kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string); 713 kdc_log(context, config, 5, "HTTP request: %s", t); 714 free(data); 715 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) { 716 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 717 d->addr_string, strerror(rk_SOCK_ERRNO)); 718 return -1; 719 } 720 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) { 721 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 722 d->addr_string, strerror(rk_SOCK_ERRNO)); 723 return -1; 724 } 725 return -1; 726 } 727 { 728 const char *msg = 729 " 200 OK\r\n" 730 "Server: Heimdal/" VERSION "\r\n" 731 "Cache-Control: no-cache\r\n" 732 "Pragma: no-cache\r\n" 733 "Content-type: application/octet-stream\r\n" 734 "Content-transfer-encoding: binary\r\n\r\n"; 735 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) { 736 free(data); 737 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 738 d->addr_string, strerror(rk_SOCK_ERRNO)); 739 return -1; 740 } 741 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) { 742 free(data); 743 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 744 d->addr_string, strerror(rk_SOCK_ERRNO)); 745 return -1; 746 } 747 } 748 if ((size_t)len > d->len) 749 len = d->len; 750 memcpy(d->buf, data, len); 751 d->len = len; 752 free(data); 753 return 1; 754 } 755 756 /* 757 * Handle incoming data to the TCP socket in `d[index]' 758 */ 759 760 static void 761 handle_tcp(krb5_context context, 762 krb5_kdc_configuration *config, 763 struct descr *d, int idx, int min_free) 764 { 765 unsigned char buf[1024]; 766 int n; 767 int ret = 0; 768 769 if (d[idx].timeout == 0) { 770 add_new_tcp (context, config, d, idx, min_free); 771 return; 772 } 773 774 n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL); 775 if(rk_IS_SOCKET_ERROR(n)){ 776 krb5_warn(context, rk_SOCK_ERRNO, "recvfrom failed from %s to %s/%d", 777 d[idx].addr_string, descr_type(d + idx), 778 ntohs(d[idx].port)); 779 return; 780 } else if (n == 0) { 781 krb5_warnx(context, "connection closed before end of data after %lu " 782 "bytes from %s to %s/%d", (unsigned long)d[idx].len, 783 d[idx].addr_string, descr_type(d + idx), 784 ntohs(d[idx].port)); 785 clear_descr (d + idx); 786 return; 787 } 788 if (grow_descr (context, config, &d[idx], n)) 789 return; 790 memcpy(d[idx].buf + d[idx].len, buf, n); 791 d[idx].len += n; 792 if(d[idx].len > 4 && d[idx].buf[0] == 0) { 793 ret = handle_vanilla_tcp (context, config, &d[idx]); 794 } else if(enable_http && 795 d[idx].len >= 4 && 796 strncmp((char *)d[idx].buf, "GET ", 4) == 0 && 797 strncmp((char *)d[idx].buf + d[idx].len - 4, 798 "\r\n\r\n", 4) == 0) { 799 800 /* remove the trailing \r\n\r\n so the string is NUL terminated */ 801 d[idx].buf[d[idx].len - 4] = '\0'; 802 803 ret = handle_http_tcp (context, config, &d[idx]); 804 if (ret < 0) 805 clear_descr (d + idx); 806 } else if (d[idx].len > 4) { 807 kdc_log (context, config, 808 0, "TCP data of strange type from %s to %s/%d", 809 d[idx].addr_string, descr_type(d + idx), 810 ntohs(d[idx].port)); 811 if (d[idx].buf[0] & 0x80) { 812 krb5_data reply; 813 814 kdc_log (context, config, 0, "TCP extension not supported"); 815 816 ret = krb5_mk_error(context, 817 KRB5KRB_ERR_FIELD_TOOLONG, 818 NULL, 819 NULL, 820 NULL, 821 NULL, 822 NULL, 823 NULL, 824 &reply); 825 if (ret == 0) { 826 send_reply(context, config, TRUE, d + idx, &reply); 827 krb5_data_free(&reply); 828 } 829 } 830 clear_descr(d + idx); 831 return; 832 } 833 if (ret < 0) 834 return; 835 else if (ret == 1) { 836 do_request(context, config, 837 d[idx].buf, d[idx].len, TRUE, &d[idx]); 838 clear_descr(d + idx); 839 } 840 } 841 842 #ifdef HAVE_FORK 843 static void 844 handle_islive(int fd) 845 { 846 char buf; 847 int ret; 848 849 ret = read(fd, &buf, 1); 850 if (ret != 1) 851 exit_flag = -1; 852 } 853 #endif 854 855 krb5_boolean 856 realloc_descrs(struct descr **d, unsigned int *ndescr) 857 { 858 struct descr *tmp; 859 size_t i; 860 861 tmp = realloc(*d, (*ndescr + 4) * sizeof(**d)); 862 if(tmp == NULL) 863 return FALSE; 864 865 *d = tmp; 866 reinit_descrs (*d, *ndescr); 867 memset(*d + *ndescr, 0, 4 * sizeof(**d)); 868 for(i = *ndescr; i < *ndescr + 4; i++) 869 init_descr (*d + i); 870 871 *ndescr += 4; 872 873 return TRUE; 874 } 875 876 int 877 next_min_free(krb5_context context, struct descr **d, unsigned int *ndescr) 878 { 879 size_t i; 880 int min_free; 881 882 for(i = 0; i < *ndescr; i++) { 883 int s = (*d + i)->s; 884 if(rk_IS_BAD_SOCKET(s)) 885 return i; 886 } 887 888 min_free = *ndescr; 889 if(!realloc_descrs(d, ndescr)) { 890 min_free = -1; 891 krb5_warnx(context, "No memory"); 892 } 893 894 return min_free; 895 } 896 897 static void 898 loop(krb5_context context, krb5_kdc_configuration *config, 899 struct descr *d, unsigned int ndescr, int islive) 900 { 901 902 while (exit_flag == 0) { 903 struct timeval tmout; 904 fd_set fds; 905 int min_free = -1; 906 int max_fd = 0; 907 size_t i; 908 909 FD_ZERO(&fds); 910 if (islive > -1) { 911 FD_SET(islive, &fds); 912 max_fd = islive; 913 } 914 for (i = 0; i < ndescr; i++) { 915 if (!rk_IS_BAD_SOCKET(d[i].s)) { 916 if (d[i].type == SOCK_STREAM && 917 d[i].timeout && d[i].timeout < time(NULL)) { 918 kdc_log(context, config, 1, 919 "TCP-connection from %s expired after %lu bytes", 920 d[i].addr_string, (unsigned long)d[i].len); 921 clear_descr(&d[i]); 922 continue; 923 } 924 #ifndef NO_LIMIT_FD_SETSIZE 925 if (max_fd < d[i].s) 926 max_fd = d[i].s; 927 #ifdef FD_SETSIZE 928 if (max_fd >= FD_SETSIZE) 929 krb5_errx(context, 1, "fd too large"); 930 #endif 931 #endif 932 FD_SET(d[i].s, &fds); 933 } 934 } 935 936 tmout.tv_sec = TCP_TIMEOUT; 937 tmout.tv_usec = 0; 938 switch(select(max_fd + 1, &fds, 0, 0, &tmout)){ 939 case 0: 940 break; 941 case -1: 942 if (errno != EINTR) 943 krb5_warn(context, rk_SOCK_ERRNO, "select"); 944 break; 945 default: 946 #ifdef HAVE_FORK 947 if (islive > -1 && FD_ISSET(islive, &fds)) 948 handle_islive(islive); 949 #endif 950 for (i = 0; i < ndescr; i++) 951 if (!rk_IS_BAD_SOCKET(d[i].s) && FD_ISSET(d[i].s, &fds)) { 952 min_free = next_min_free(context, &d, &ndescr); 953 954 if (d[i].type == SOCK_DGRAM) 955 handle_udp(context, config, &d[i]); 956 else if (d[i].type == SOCK_STREAM) 957 handle_tcp(context, config, d, i, min_free); 958 } 959 } 960 } 961 962 switch (exit_flag) { 963 case -1: 964 kdc_log(context, config, 0, 965 "KDC worker process exiting because KDC master exited."); 966 break; 967 #ifdef SIGXCPU 968 case SIGXCPU: 969 kdc_log(context, config, 0, "CPU time limit exceeded"); 970 break; 971 #endif 972 case SIGINT: 973 case SIGTERM: 974 kdc_log(context, config, 0, "Terminated"); 975 break; 976 default: 977 kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag); 978 break; 979 } 980 } 981 982 #ifdef __APPLE__ 983 static void 984 bonjour_kid(krb5_context context, krb5_kdc_configuration *config, const char *argv0, int *islive) 985 { 986 char buf; 987 988 if (do_bonjour > 0) { 989 bonjour_announce(context, config); 990 991 while (read(0, &buf, 1) == 1) 992 continue; 993 _exit(0); 994 } 995 996 if ((bonjour_pid = fork()) != 0) 997 return; 998 999 close(islive[0]); 1000 if (dup2(islive[1], 0) == -1) 1001 err(1, "failed to announce with bonjour (dup)"); 1002 if (islive[1] != 0) 1003 close(islive[1]); 1004 execlp(argv0, "kdc", "--bonjour", NULL); 1005 err(1, "failed to announce with bonjour (exec)"); 1006 } 1007 #endif 1008 1009 #ifdef HAVE_FORK 1010 static void 1011 kill_kids(pid_t *pids, int max_kids, int sig) 1012 { 1013 int i; 1014 1015 for (i=0; i < max_kids; i++) 1016 if (pids[i] > 0) 1017 kill(sig, pids[i]); 1018 if (bonjour_pid > 0) 1019 kill(sig, bonjour_pid); 1020 } 1021 1022 static int 1023 reap_kid(krb5_context context, krb5_kdc_configuration *config, 1024 pid_t *pids, int max_kids, int options) 1025 { 1026 pid_t pid; 1027 char *what; 1028 int status; 1029 int i = 0; /* quiet warnings */ 1030 1031 pid = waitpid(-1, &status, options); 1032 if (pid < 1) 1033 return 0; 1034 1035 if (pid != bonjour_pid) { 1036 for (i=0; i < max_kids; i++) { 1037 if (pids[i] == pid) 1038 break; 1039 } 1040 1041 if (i == max_kids) { 1042 /* XXXrcd: this should not happen, have to do something, though */ 1043 return 0; 1044 } 1045 } 1046 1047 if (pid == bonjour_pid) 1048 what = "bonjour"; 1049 else 1050 what = "worker"; 1051 if (WIFEXITED(status)) 1052 kdc_log(context, config, 0, "KDC reaped %s process: %d, exit status: %d", 1053 what, (int)pid, WEXITSTATUS(status)); 1054 else if (WIFSIGNALED(status)) 1055 kdc_log(context, config, 0, "KDC reaped %s process: %d, term signal %d%s", 1056 what, (int)pid, WTERMSIG(status), 1057 WCOREDUMP(status) ? " (core dumped)" : ""); 1058 else 1059 kdc_log(context, config, 0, "KDC reaped %s process: %d", 1060 what, (int)pid); 1061 if (pid == bonjour_pid) { 1062 bonjour_pid = (pid_t)-1; 1063 return 0; 1064 } else { 1065 pids[i] = (pid_t)0; 1066 return 1; 1067 } 1068 } 1069 1070 static int 1071 reap_kids(krb5_context context, krb5_kdc_configuration *config, 1072 pid_t *pids, int max_kids) 1073 { 1074 int reaped = 0; 1075 1076 for (;;) { 1077 if (reap_kid(context, config, pids, max_kids, WNOHANG) == 0) 1078 break; 1079 reaped++; 1080 } 1081 1082 return reaped; 1083 } 1084 1085 static void 1086 select_sleep(int microseconds) 1087 { 1088 struct timeval tv; 1089 1090 tv.tv_sec = microseconds / 1000000; 1091 tv.tv_usec = microseconds % 1000000; 1092 select(0, NULL, NULL, NULL, &tv); 1093 } 1094 #endif 1095 1096 void 1097 start_kdc(krb5_context context, 1098 krb5_kdc_configuration *config, const char *argv0) 1099 { 1100 struct timeval tv1; 1101 struct timeval tv2; 1102 struct descr *d; 1103 unsigned int ndescr; 1104 pid_t pid = -1; 1105 #ifdef HAVE_FORK 1106 pid_t *pids; 1107 int max_kdcs = config->num_kdc_processes; 1108 int num_kdcs = 0; 1109 int i; 1110 int islive[2]; 1111 #endif 1112 1113 #ifdef __APPLE__ 1114 if (do_bonjour > 0) 1115 bonjour_kid(context, config, argv0, NULL); 1116 #endif 1117 1118 #ifdef HAVE_FORK 1119 #ifdef _SC_NPROCESSORS_ONLN 1120 if (max_kdcs < 1) 1121 max_kdcs = sysconf(_SC_NPROCESSORS_ONLN); 1122 #endif 1123 1124 if (max_kdcs < 1) 1125 max_kdcs = 1; 1126 1127 pids = calloc(max_kdcs, sizeof(*pids)); 1128 if (!pids) 1129 krb5_err(context, 1, errno, "malloc"); 1130 1131 /* 1132 * We open a socketpair of which we hand one end to each of our kids. 1133 * When we exit, for whatever reason, the children will notice an EOF 1134 * on their end and be able to cleanly exit. 1135 */ 1136 1137 if (socketpair(PF_UNIX, SOCK_STREAM, 0, islive) == -1) 1138 krb5_errx(context, 1, "socketpair"); 1139 socket_set_nonblocking(islive[1], 1); 1140 #endif 1141 1142 ndescr = init_sockets(context, config, &d); 1143 if(ndescr <= 0) 1144 krb5_errx(context, 1, "No sockets!"); 1145 1146 #ifdef HAVE_FORK 1147 1148 # ifdef __APPLE__ 1149 if (do_bonjour < 0) 1150 bonjour_kid(context, config, argv0, islive); 1151 # endif 1152 1153 kdc_log(context, config, 0, "KDC started master process pid=%d", getpid()); 1154 #else 1155 kdc_log(context, config, 0, "KDC started pid=%d", getpid()); 1156 #endif 1157 1158 roken_detach_finish(NULL, daemon_child); 1159 1160 tv1.tv_sec = 0; 1161 tv1.tv_usec = 0; 1162 1163 #ifdef HAVE_FORK 1164 if (!testing_flag) { 1165 /* Note that we might never execute the body of this loop */ 1166 while (exit_flag == 0) { 1167 1168 /* Slow down the creation of KDCs... */ 1169 1170 gettimeofday(&tv2, NULL); 1171 if (tv1.tv_sec == tv2.tv_sec && tv2.tv_usec - tv1.tv_usec < 25000) { 1172 #if 0 /* XXXrcd: should print a message... */ 1173 kdc_log(context, config, 0, "Spawning KDCs too quickly, " 1174 "pausing for 50ms"); 1175 #endif 1176 select_sleep(12500); 1177 continue; 1178 } 1179 1180 if (num_kdcs >= max_kdcs) { 1181 num_kdcs -= reap_kid(context, config, pids, max_kdcs, 0); 1182 continue; 1183 } 1184 1185 if (num_kdcs > 0) 1186 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1187 1188 pid = fork(); 1189 switch (pid) { 1190 case 0: 1191 close(islive[0]); 1192 loop(context, config, d, ndescr, islive[1]); 1193 exit(0); 1194 case -1: 1195 /* XXXrcd: hmmm, do something useful?? */ 1196 kdc_log(context, config, 0, 1197 "KDC master process could not fork worker process"); 1198 sleep(10); 1199 break; 1200 default: 1201 for (i=0; i < max_kdcs; i++) { 1202 if (pids[i] < 1) { 1203 pids[i] = pid; 1204 break; 1205 } 1206 } 1207 kdc_log(context, config, 0, "KDC worker process started: %d", 1208 pid); 1209 num_kdcs++; 1210 gettimeofday(&tv1, NULL); 1211 break; 1212 } 1213 } 1214 1215 /* Closing these sockets should cause the kids to die... */ 1216 1217 close(islive[0]); 1218 close(islive[1]); 1219 1220 /* Close our listener sockets before terminating workers */ 1221 for (i = 0; i < ndescr; ++i) 1222 clear_descr(&d[i]); 1223 1224 gettimeofday(&tv1, NULL); 1225 tv2 = tv1; 1226 1227 /* Reap every 10ms, terminate stragglers once a second, give up after 10 */ 1228 for (;;) { 1229 struct timeval tv3; 1230 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1231 if (num_kdcs == 0 && bonjour_pid <= 0) 1232 goto end; 1233 /* 1234 * Using select to sleep will fail with EINTR if we receive a 1235 * SIGCHLD. This is desirable. 1236 */ 1237 select_sleep(10000); 1238 gettimeofday(&tv3, NULL); 1239 if (tv3.tv_sec - tv1.tv_sec > 10 || 1240 (tv3.tv_sec - tv1.tv_sec == 10 && tv3.tv_usec >= tv1.tv_usec)) 1241 break; 1242 if (tv3.tv_sec - tv2.tv_sec > 1 || 1243 (tv3.tv_sec - tv2.tv_sec == 1 && tv3.tv_usec >= tv2.tv_usec)) { 1244 kill_kids(pids, max_kdcs, SIGTERM); 1245 tv2 = tv3; 1246 } 1247 } 1248 1249 /* Kill stragglers and reap every 200ms, give up after 15s */ 1250 for (;;) { 1251 kill_kids(pids, max_kdcs, SIGKILL); 1252 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1253 if (num_kdcs == 0 && bonjour_pid <= 0) 1254 break; 1255 select_sleep(200000); 1256 gettimeofday(&tv2, NULL); 1257 if (tv2.tv_sec - tv1.tv_sec > 15 || 1258 (tv2.tv_sec - tv1.tv_sec == 15 && tv2.tv_usec >= tv1.tv_usec)) 1259 break; 1260 } 1261 1262 end: 1263 kdc_log(context, config, 0, "KDC master process exiting", pid); 1264 free(pids); 1265 } else { 1266 loop(context, config, d, ndescr, -1); 1267 kdc_log(context, config, 0, "KDC exiting", pid); 1268 } 1269 #else 1270 loop(context, config, d, ndescr, -1); 1271 kdc_log(context, config, 0, "KDC exiting", pid); 1272 #endif 1273 } 1274