1 /* $NetBSD: connect.c,v 1.3 2018/04/29 05:36:04 spz 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 for(p = q = (unsigned char *)buf; *p; p++, q++) { 521 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) { 522 unsigned int x; 523 if(sscanf((char *)p + 1, "%2x", &x) != 1) 524 return -1; 525 *q = x; 526 p += 2; 527 } else 528 *q = *p; 529 } 530 *q = '\0'; 531 return 0; 532 } 533 534 #define TCP_TIMEOUT 4 535 536 /* 537 * accept a new TCP connection on `d[parent]' and store it in `d[child]' 538 */ 539 540 static void 541 add_new_tcp (krb5_context context, 542 krb5_kdc_configuration *config, 543 struct descr *d, int parent, int child) 544 { 545 krb5_socket_t s; 546 547 if (child == -1) 548 return; 549 550 d[child].sock_len = sizeof(d[child].__ss); 551 s = accept(d[parent].s, d[child].sa, &d[child].sock_len); 552 if(rk_IS_BAD_SOCKET(s)) { 553 if (rk_SOCK_ERRNO != EAGAIN && rk_SOCK_ERRNO != EINTR) 554 krb5_warn(context, rk_SOCK_ERRNO, "accept"); 555 return; 556 } 557 558 #ifdef FD_SETSIZE 559 if (s >= FD_SETSIZE) { 560 krb5_warnx(context, "socket FD too large"); 561 rk_closesocket (s); 562 return; 563 } 564 #endif 565 566 d[child].s = s; 567 d[child].timeout = time(NULL) + TCP_TIMEOUT; 568 d[child].type = SOCK_STREAM; 569 addr_to_string (context, 570 d[child].sa, d[child].sock_len, 571 d[child].addr_string, sizeof(d[child].addr_string)); 572 } 573 574 /* 575 * Grow `d' to handle at least `n'. 576 * Return != 0 if fails 577 */ 578 579 static int 580 grow_descr (krb5_context context, 581 krb5_kdc_configuration *config, 582 struct descr *d, size_t n) 583 { 584 if (d->size - d->len < n) { 585 unsigned char *tmp; 586 size_t grow; 587 588 grow = max(1024, d->len + n); 589 if (d->size + grow > max_request_tcp) { 590 kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).", 591 (unsigned long)d->size + grow); 592 clear_descr(d); 593 return -1; 594 } 595 tmp = realloc (d->buf, d->size + grow); 596 if (tmp == NULL) { 597 kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.", 598 (unsigned long)d->size + grow); 599 clear_descr(d); 600 return -1; 601 } 602 d->size += grow; 603 d->buf = tmp; 604 } 605 return 0; 606 } 607 608 /* 609 * Try to handle the TCP data at `d->buf, d->len'. 610 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 611 */ 612 613 static int 614 handle_vanilla_tcp (krb5_context context, 615 krb5_kdc_configuration *config, 616 struct descr *d) 617 { 618 krb5_storage *sp; 619 uint32_t len; 620 621 sp = krb5_storage_from_mem(d->buf, d->len); 622 if (sp == NULL) { 623 kdc_log (context, config, 0, "krb5_storage_from_mem failed"); 624 return -1; 625 } 626 krb5_ret_uint32(sp, &len); 627 krb5_storage_free(sp); 628 if(d->len - 4 >= len) { 629 memmove(d->buf, d->buf + 4, d->len - 4); 630 d->len -= 4; 631 return 1; 632 } 633 return 0; 634 } 635 636 /* 637 * Try to handle the TCP/HTTP data at `d->buf, d->len'. 638 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 639 */ 640 641 static int 642 handle_http_tcp (krb5_context context, 643 krb5_kdc_configuration *config, 644 struct descr *d) 645 { 646 char *s, *p, *t; 647 void *data; 648 char *proto; 649 int len; 650 651 s = (char *)d->buf; 652 653 /* If its a multi line query, truncate off the first line */ 654 p = strstr(s, "\r\n"); 655 if (p) 656 *p = 0; 657 658 p = NULL; 659 t = strtok_r(s, " \t", &p); 660 if (t == NULL) { 661 kdc_log(context, config, 0, 662 "Missing HTTP operand (GET) request from %s", d->addr_string); 663 return -1; 664 } 665 666 t = strtok_r(NULL, " \t", &p); 667 if(t == NULL) { 668 kdc_log(context, config, 0, 669 "Missing HTTP GET data in request from %s", d->addr_string); 670 return -1; 671 } 672 673 data = malloc(strlen(t)); 674 if (data == NULL) { 675 kdc_log(context, config, 0, "Failed to allocate %lu bytes", 676 (unsigned long)strlen(t)); 677 return -1; 678 } 679 if(*t == '/') 680 t++; 681 if(de_http(t) != 0) { 682 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 683 kdc_log(context, config, 5, "HTTP request: %s", t); 684 free(data); 685 return -1; 686 } 687 proto = strtok_r(NULL, " \t", &p); 688 if (proto == NULL) { 689 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 690 free(data); 691 return -1; 692 } 693 len = rk_base64_decode(t, data); 694 if(len <= 0){ 695 const char *msg = 696 " 404 Not found\r\n" 697 "Server: Heimdal/" VERSION "\r\n" 698 "Cache-Control: no-cache\r\n" 699 "Pragma: no-cache\r\n" 700 "Content-type: text/html\r\n" 701 "Content-transfer-encoding: 8bit\r\n\r\n" 702 "<TITLE>404 Not found</TITLE>\r\n" 703 "<H1>404 Not found</H1>\r\n" 704 "That page doesn't exist, maybe you are looking for " 705 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n"; 706 kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string); 707 kdc_log(context, config, 5, "HTTP request: %s", t); 708 free(data); 709 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) { 710 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 711 d->addr_string, strerror(rk_SOCK_ERRNO)); 712 return -1; 713 } 714 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) { 715 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 716 d->addr_string, strerror(rk_SOCK_ERRNO)); 717 return -1; 718 } 719 return -1; 720 } 721 { 722 const char *msg = 723 " 200 OK\r\n" 724 "Server: Heimdal/" VERSION "\r\n" 725 "Cache-Control: no-cache\r\n" 726 "Pragma: no-cache\r\n" 727 "Content-type: application/octet-stream\r\n" 728 "Content-transfer-encoding: binary\r\n\r\n"; 729 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) { 730 free(data); 731 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 732 d->addr_string, strerror(rk_SOCK_ERRNO)); 733 return -1; 734 } 735 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 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 } 742 if ((size_t)len > d->len) 743 len = d->len; 744 memcpy(d->buf, data, len); 745 d->len = len; 746 free(data); 747 return 1; 748 } 749 750 /* 751 * Handle incoming data to the TCP socket in `d[index]' 752 */ 753 754 static void 755 handle_tcp(krb5_context context, 756 krb5_kdc_configuration *config, 757 struct descr *d, int idx, int min_free) 758 { 759 unsigned char buf[1024]; 760 int n; 761 int ret = 0; 762 763 if (d[idx].timeout == 0) { 764 add_new_tcp (context, config, d, idx, min_free); 765 return; 766 } 767 768 n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL); 769 if(rk_IS_SOCKET_ERROR(n)){ 770 krb5_warn(context, rk_SOCK_ERRNO, "recvfrom failed from %s to %s/%d", 771 d[idx].addr_string, descr_type(d + idx), 772 ntohs(d[idx].port)); 773 return; 774 } else if (n == 0) { 775 krb5_warnx(context, "connection closed before end of data after %lu " 776 "bytes from %s to %s/%d", (unsigned long)d[idx].len, 777 d[idx].addr_string, descr_type(d + idx), 778 ntohs(d[idx].port)); 779 clear_descr (d + idx); 780 return; 781 } 782 if (grow_descr (context, config, &d[idx], n)) 783 return; 784 memcpy(d[idx].buf + d[idx].len, buf, n); 785 d[idx].len += n; 786 if(d[idx].len > 4 && d[idx].buf[0] == 0) { 787 ret = handle_vanilla_tcp (context, config, &d[idx]); 788 } else if(enable_http && 789 d[idx].len >= 4 && 790 strncmp((char *)d[idx].buf, "GET ", 4) == 0 && 791 strncmp((char *)d[idx].buf + d[idx].len - 4, 792 "\r\n\r\n", 4) == 0) { 793 794 /* remove the trailing \r\n\r\n so the string is NUL terminated */ 795 d[idx].buf[d[idx].len - 4] = '\0'; 796 797 ret = handle_http_tcp (context, config, &d[idx]); 798 if (ret < 0) 799 clear_descr (d + idx); 800 } else if (d[idx].len > 4) { 801 kdc_log (context, config, 802 0, "TCP data of strange type from %s to %s/%d", 803 d[idx].addr_string, descr_type(d + idx), 804 ntohs(d[idx].port)); 805 if (d[idx].buf[0] & 0x80) { 806 krb5_data reply; 807 808 kdc_log (context, config, 0, "TCP extension not supported"); 809 810 ret = krb5_mk_error(context, 811 KRB5KRB_ERR_FIELD_TOOLONG, 812 NULL, 813 NULL, 814 NULL, 815 NULL, 816 NULL, 817 NULL, 818 &reply); 819 if (ret == 0) { 820 send_reply(context, config, TRUE, d + idx, &reply); 821 krb5_data_free(&reply); 822 } 823 } 824 clear_descr(d + idx); 825 return; 826 } 827 if (ret < 0) 828 return; 829 else if (ret == 1) { 830 do_request(context, config, 831 d[idx].buf, d[idx].len, TRUE, &d[idx]); 832 clear_descr(d + idx); 833 } 834 } 835 836 #ifdef HAVE_FORK 837 static void 838 handle_islive(int fd) 839 { 840 char buf; 841 int ret; 842 843 ret = read(fd, &buf, 1); 844 if (ret != 1) 845 exit_flag = -1; 846 } 847 #endif 848 849 krb5_boolean 850 realloc_descrs(struct descr **d, unsigned int *ndescr) 851 { 852 struct descr *tmp; 853 size_t i; 854 855 tmp = realloc(*d, (*ndescr + 4) * sizeof(**d)); 856 if(tmp == NULL) 857 return FALSE; 858 859 *d = tmp; 860 reinit_descrs (*d, *ndescr); 861 memset(*d + *ndescr, 0, 4 * sizeof(**d)); 862 for(i = *ndescr; i < *ndescr + 4; i++) 863 init_descr (*d + i); 864 865 *ndescr += 4; 866 867 return TRUE; 868 } 869 870 int 871 next_min_free(krb5_context context, struct descr **d, unsigned int *ndescr) 872 { 873 size_t i; 874 int min_free; 875 876 for(i = 0; i < *ndescr; i++) { 877 int s = (*d + i)->s; 878 if(rk_IS_BAD_SOCKET(s)) 879 return i; 880 } 881 882 min_free = *ndescr; 883 if(!realloc_descrs(d, ndescr)) { 884 min_free = -1; 885 krb5_warnx(context, "No memory"); 886 } 887 888 return min_free; 889 } 890 891 static void 892 loop(krb5_context context, krb5_kdc_configuration *config, 893 struct descr *d, unsigned int ndescr, int islive) 894 { 895 896 while (exit_flag == 0) { 897 struct timeval tmout; 898 fd_set fds; 899 int min_free = -1; 900 int max_fd = 0; 901 size_t i; 902 903 FD_ZERO(&fds); 904 if (islive > -1) { 905 FD_SET(islive, &fds); 906 max_fd = islive; 907 } 908 for (i = 0; i < ndescr; i++) { 909 if (!rk_IS_BAD_SOCKET(d[i].s)) { 910 if (d[i].type == SOCK_STREAM && 911 d[i].timeout && d[i].timeout < time(NULL)) { 912 kdc_log(context, config, 1, 913 "TCP-connection from %s expired after %lu bytes", 914 d[i].addr_string, (unsigned long)d[i].len); 915 clear_descr(&d[i]); 916 continue; 917 } 918 #ifndef NO_LIMIT_FD_SETSIZE 919 if (max_fd < d[i].s) 920 max_fd = d[i].s; 921 #ifdef FD_SETSIZE 922 if (max_fd >= FD_SETSIZE) 923 krb5_errx(context, 1, "fd too large"); 924 #endif 925 #endif 926 FD_SET(d[i].s, &fds); 927 } 928 } 929 930 tmout.tv_sec = TCP_TIMEOUT; 931 tmout.tv_usec = 0; 932 switch(select(max_fd + 1, &fds, 0, 0, &tmout)){ 933 case 0: 934 break; 935 case -1: 936 if (errno != EINTR) 937 krb5_warn(context, rk_SOCK_ERRNO, "select"); 938 break; 939 default: 940 #ifdef HAVE_FORK 941 if (islive > -1 && FD_ISSET(islive, &fds)) 942 handle_islive(islive); 943 #endif 944 for (i = 0; i < ndescr; i++) 945 if (!rk_IS_BAD_SOCKET(d[i].s) && FD_ISSET(d[i].s, &fds)) { 946 min_free = next_min_free(context, &d, &ndescr); 947 948 if (d[i].type == SOCK_DGRAM) 949 handle_udp(context, config, &d[i]); 950 else if (d[i].type == SOCK_STREAM) 951 handle_tcp(context, config, d, i, min_free); 952 } 953 } 954 } 955 956 switch (exit_flag) { 957 case -1: 958 kdc_log(context, config, 0, 959 "KDC worker process exiting because KDC master exited."); 960 break; 961 #ifdef SIGXCPU 962 case SIGXCPU: 963 kdc_log(context, config, 0, "CPU time limit exceeded"); 964 break; 965 #endif 966 case SIGINT: 967 case SIGTERM: 968 kdc_log(context, config, 0, "Terminated"); 969 break; 970 default: 971 kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag); 972 break; 973 } 974 } 975 976 #ifdef __APPLE__ 977 static void 978 bonjour_kid(krb5_context context, krb5_kdc_configuration *config, const char *argv0, int *islive) 979 { 980 char buf; 981 982 if (do_bonjour > 0) { 983 bonjour_announce(context, config); 984 985 while (read(0, &buf, 1) == 1) 986 continue; 987 _exit(0); 988 } 989 990 if ((bonjour_pid = fork()) != 0) 991 return; 992 993 close(islive[0]); 994 if (dup2(islive[1], 0) == -1) 995 err(1, "failed to announce with bonjour (dup)"); 996 if (islive[1] != 0) 997 close(islive[1]); 998 execlp(argv0, "kdc", "--bonjour", NULL); 999 err(1, "failed to announce with bonjour (exec)"); 1000 } 1001 #endif 1002 1003 #ifdef HAVE_FORK 1004 static void 1005 kill_kids(pid_t *pids, int max_kids, int sig) 1006 { 1007 int i; 1008 1009 for (i=0; i < max_kids; i++) 1010 if (pids[i] > 0) 1011 kill(sig, pids[i]); 1012 if (bonjour_pid > 0) 1013 kill(sig, bonjour_pid); 1014 } 1015 1016 static int 1017 reap_kid(krb5_context context, krb5_kdc_configuration *config, 1018 pid_t *pids, int max_kids, int options) 1019 { 1020 pid_t pid; 1021 char *what; 1022 int status; 1023 int i = 0; /* quiet warnings */ 1024 1025 pid = waitpid(-1, &status, options); 1026 if (pid < 1) 1027 return 0; 1028 1029 if (pid != bonjour_pid) { 1030 for (i=0; i < max_kids; i++) { 1031 if (pids[i] == pid) 1032 break; 1033 } 1034 1035 if (i == max_kids) { 1036 /* XXXrcd: this should not happen, have to do something, though */ 1037 return 0; 1038 } 1039 } 1040 1041 if (pid == bonjour_pid) 1042 what = "bonjour"; 1043 else 1044 what = "worker"; 1045 if (WIFEXITED(status)) 1046 kdc_log(context, config, 0, "KDC reaped %s process: %d, exit status: %d", 1047 what, (int)pid, WEXITSTATUS(status)); 1048 else if (WIFSIGNALED(status)) 1049 kdc_log(context, config, 0, "KDC reaped %s process: %d, term signal %d%s", 1050 what, (int)pid, WTERMSIG(status), 1051 WCOREDUMP(status) ? " (core dumped)" : ""); 1052 else 1053 kdc_log(context, config, 0, "KDC reaped %s process: %d", 1054 what, (int)pid); 1055 if (pid == bonjour_pid) { 1056 bonjour_pid = (pid_t)-1; 1057 return 0; 1058 } else { 1059 pids[i] = (pid_t)-1; 1060 return 1; 1061 } 1062 } 1063 1064 static int 1065 reap_kids(krb5_context context, krb5_kdc_configuration *config, 1066 pid_t *pids, int max_kids) 1067 { 1068 int reaped = 0; 1069 1070 for (;;) { 1071 if (reap_kid(context, config, pids, max_kids, WNOHANG) == 0) 1072 break; 1073 reaped++; 1074 } 1075 1076 return reaped; 1077 } 1078 1079 static void 1080 select_sleep(int microseconds) 1081 { 1082 struct timeval tv; 1083 1084 tv.tv_sec = microseconds / 1000000; 1085 tv.tv_usec = microseconds % 1000000; 1086 select(0, NULL, NULL, NULL, &tv); 1087 } 1088 #endif 1089 1090 void 1091 start_kdc(krb5_context context, 1092 krb5_kdc_configuration *config, const char *argv0) 1093 { 1094 struct timeval tv1; 1095 struct timeval tv2; 1096 struct descr *d; 1097 unsigned int ndescr; 1098 pid_t pid = -1; 1099 #ifdef HAVE_FORK 1100 pid_t *pids; 1101 int max_kdcs = config->num_kdc_processes; 1102 int num_kdcs = 0; 1103 int i; 1104 int islive[2]; 1105 #endif 1106 1107 #ifdef __APPLE__ 1108 if (do_bonjour > 0) 1109 bonjour_kid(context, config, argv0, NULL); 1110 #endif 1111 1112 #ifdef HAVE_FORK 1113 #ifdef _SC_NPROCESSORS_ONLN 1114 if (max_kdcs < 1) 1115 max_kdcs = sysconf(_SC_NPROCESSORS_ONLN); 1116 #endif 1117 1118 if (max_kdcs < 1) 1119 max_kdcs = 1; 1120 1121 pids = calloc(max_kdcs, sizeof(*pids)); 1122 if (!pids) 1123 krb5_err(context, 1, errno, "malloc"); 1124 1125 /* 1126 * We open a socketpair of which we hand one end to each of our kids. 1127 * When we exit, for whatever reason, the children will notice an EOF 1128 * on their end and be able to cleanly exit. 1129 */ 1130 1131 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, islive) == -1) 1132 krb5_errx(context, 1, "socketpair"); 1133 socket_set_nonblocking(islive[1], 1); 1134 #endif 1135 1136 ndescr = init_sockets(context, config, &d); 1137 if(ndescr <= 0) 1138 krb5_errx(context, 1, "No sockets!"); 1139 1140 #ifdef HAVE_FORK 1141 1142 # ifdef __APPLE__ 1143 if (do_bonjour < 0) 1144 bonjour_kid(context, config, argv0, islive); 1145 # endif 1146 1147 kdc_log(context, config, 0, "KDC started master process pid=%d", getpid()); 1148 #else 1149 kdc_log(context, config, 0, "KDC started pid=%d", getpid()); 1150 #endif 1151 1152 roken_detach_finish(NULL, daemon_child); 1153 1154 tv1.tv_sec = 0; 1155 tv1.tv_usec = 0; 1156 1157 #ifdef HAVE_FORK 1158 if (!testing_flag) { 1159 /* Note that we might never execute the body of this loop */ 1160 while (exit_flag == 0) { 1161 1162 /* Slow down the creation of KDCs... */ 1163 1164 gettimeofday(&tv2, NULL); 1165 if (tv1.tv_sec == tv2.tv_sec && tv2.tv_usec - tv1.tv_usec < 25000) { 1166 #if 0 /* XXXrcd: should print a message... */ 1167 kdc_log(context, config, 0, "Spawning KDCs too quickly, " 1168 "pausing for 50ms"); 1169 #endif 1170 select_sleep(12500); 1171 continue; 1172 } 1173 1174 if (num_kdcs >= max_kdcs) { 1175 num_kdcs -= reap_kid(context, config, pids, max_kdcs, 0); 1176 continue; 1177 } 1178 1179 if (num_kdcs > 0) 1180 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1181 1182 pid = fork(); 1183 switch (pid) { 1184 case 0: 1185 close(islive[0]); 1186 loop(context, config, d, ndescr, islive[1]); 1187 exit(0); 1188 case -1: 1189 /* XXXrcd: hmmm, do something useful?? */ 1190 kdc_log(context, config, 0, 1191 "KDC master process could not fork worker process"); 1192 sleep(10); 1193 break; 1194 default: 1195 for (i=0; i < max_kdcs; i++) { 1196 if (pids[i] < 1) { 1197 pids[i] = pid; 1198 break; 1199 } 1200 } 1201 kdc_log(context, config, 0, "KDC worker process started: %d", 1202 pid); 1203 num_kdcs++; 1204 gettimeofday(&tv1, NULL); 1205 break; 1206 } 1207 } 1208 1209 /* Closing these sockets should cause the kids to die... */ 1210 1211 close(islive[0]); 1212 close(islive[1]); 1213 1214 /* Close our listener sockets before terminating workers */ 1215 for (i = 0; i < ndescr; ++i) 1216 clear_descr(&d[i]); 1217 1218 gettimeofday(&tv1, NULL); 1219 tv2 = tv1; 1220 1221 /* Reap every 10ms, terminate stragglers once a second, give up after 10 */ 1222 for (;;) { 1223 struct timeval tv3; 1224 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1225 if (num_kdcs == 0 && bonjour_pid <= 0) 1226 goto end; 1227 /* 1228 * Using select to sleep will fail with EINTR if we receive a 1229 * SIGCHLD. This is desirable. 1230 */ 1231 select_sleep(10000); 1232 gettimeofday(&tv3, NULL); 1233 if (tv3.tv_sec - tv1.tv_sec > 10 || 1234 (tv3.tv_sec - tv1.tv_sec == 10 && tv3.tv_usec >= tv1.tv_usec)) 1235 break; 1236 if (tv3.tv_sec - tv2.tv_sec > 1 || 1237 (tv3.tv_sec - tv2.tv_sec == 1 && tv3.tv_usec >= tv2.tv_usec)) { 1238 kill_kids(pids, max_kdcs, SIGTERM); 1239 tv2 = tv3; 1240 } 1241 } 1242 1243 /* Kill stragglers and reap every 200ms, give up after 15s */ 1244 for (;;) { 1245 kill_kids(pids, max_kdcs, SIGKILL); 1246 num_kdcs -= reap_kids(context, config, pids, max_kdcs); 1247 if (num_kdcs == 0 && bonjour_pid <= 0) 1248 break; 1249 select_sleep(200000); 1250 gettimeofday(&tv2, NULL); 1251 if (tv2.tv_sec - tv1.tv_sec > 15 || 1252 (tv2.tv_sec - tv1.tv_sec == 15 && tv2.tv_usec >= tv1.tv_usec)) 1253 break; 1254 } 1255 1256 end: 1257 kdc_log(context, config, 0, "KDC master process exiting", pid); 1258 free(pids); 1259 } else { 1260 loop(context, config, d, ndescr, -1); 1261 kdc_log(context, config, 0, "KDC exiting", pid); 1262 } 1263 #else 1264 loop(context, config, d, ndescr, -1); 1265 kdc_log(context, config, 0, "KDC exiting", pid); 1266 #endif 1267 1268 free(d); 1269 } 1270