1 /* 2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "kdc_locl.h" 35 36 /* Should we enable the HTTP hack? */ 37 int enable_http = -1; 38 39 /* Log over requests to the KDC */ 40 const char *request_log; 41 42 /* A string describing on what ports to listen */ 43 const char *port_str; 44 45 krb5_addresses explicit_addresses; 46 47 size_t max_request_udp; 48 size_t max_request_tcp; 49 50 /* 51 * a tuple describing on what to listen 52 */ 53 54 struct port_desc{ 55 int family; 56 int type; 57 int port; 58 }; 59 60 /* the current ones */ 61 62 static struct port_desc *ports; 63 static size_t num_ports; 64 65 /* 66 * add `family, port, protocol' to the list with duplicate suppresion. 67 */ 68 69 static void 70 add_port(krb5_context context, 71 int family, int port, const char *protocol) 72 { 73 int type; 74 size_t i; 75 76 if(strcmp(protocol, "udp") == 0) 77 type = SOCK_DGRAM; 78 else if(strcmp(protocol, "tcp") == 0) 79 type = SOCK_STREAM; 80 else 81 return; 82 for(i = 0; i < num_ports; i++){ 83 if(ports[i].type == type 84 && ports[i].port == port 85 && ports[i].family == family) 86 return; 87 } 88 ports = realloc(ports, (num_ports + 1) * sizeof(*ports)); 89 if (ports == NULL) 90 krb5_err (context, 1, errno, "realloc"); 91 ports[num_ports].family = family; 92 ports[num_ports].type = type; 93 ports[num_ports].port = port; 94 num_ports++; 95 } 96 97 /* 98 * add a triple but with service -> port lookup 99 * (this prints warnings for stuff that does not exist) 100 */ 101 102 static void 103 add_port_service(krb5_context context, 104 int family, const char *service, int port, 105 const char *protocol) 106 { 107 port = krb5_getportbyname (context, service, protocol, port); 108 add_port (context, family, port, protocol); 109 } 110 111 /* 112 * add the port with service -> port lookup or string -> number 113 * (no warning is printed) 114 */ 115 116 static void 117 add_port_string (krb5_context context, 118 int family, const char *str, const char *protocol) 119 { 120 struct servent *sp; 121 int port; 122 123 sp = roken_getservbyname (str, protocol); 124 if (sp != NULL) { 125 port = sp->s_port; 126 } else { 127 char *end; 128 129 port = htons(strtol(str, &end, 0)); 130 if (end == str) 131 return; 132 } 133 add_port (context, family, port, protocol); 134 } 135 136 /* 137 * add the standard collection of ports for `family' 138 */ 139 140 static void 141 add_standard_ports (krb5_context context, 142 krb5_kdc_configuration *config, 143 int family) 144 { 145 add_port_service(context, family, "kerberos", 88, "udp"); 146 add_port_service(context, family, "kerberos", 88, "tcp"); 147 add_port_service(context, family, "kerberos-sec", 88, "udp"); 148 add_port_service(context, family, "kerberos-sec", 88, "tcp"); 149 if(enable_http) 150 add_port_service(context, family, "http", 80, "tcp"); 151 if(config->enable_kx509) { 152 add_port_service(context, family, "kca_service", 9878, "udp"); 153 add_port_service(context, family, "kca_service", 9878, "tcp"); 154 } 155 156 } 157 158 /* 159 * parse the set of space-delimited ports in `str' and add them. 160 * "+" => all the standard ones 161 * otherwise it's port|service[/protocol] 162 */ 163 164 static void 165 parse_ports(krb5_context context, 166 krb5_kdc_configuration *config, 167 const char *str) 168 { 169 char *pos = NULL; 170 char *p; 171 char *str_copy = strdup (str); 172 173 p = strtok_r(str_copy, " \t", &pos); 174 while(p != NULL) { 175 if(strcmp(p, "+") == 0) { 176 #ifdef HAVE_IPV6 177 add_standard_ports(context, config, AF_INET6); 178 #endif 179 add_standard_ports(context, config, AF_INET); 180 } else { 181 char *q = strchr(p, '/'); 182 if(q){ 183 *q++ = 0; 184 #ifdef HAVE_IPV6 185 add_port_string(context, AF_INET6, p, q); 186 #endif 187 add_port_string(context, AF_INET, p, q); 188 }else { 189 #ifdef HAVE_IPV6 190 add_port_string(context, AF_INET6, p, "udp"); 191 add_port_string(context, AF_INET6, p, "tcp"); 192 #endif 193 add_port_string(context, AF_INET, p, "udp"); 194 add_port_string(context, AF_INET, p, "tcp"); 195 } 196 } 197 198 p = strtok_r(NULL, " \t", &pos); 199 } 200 free (str_copy); 201 } 202 203 /* 204 * every socket we listen on 205 */ 206 207 struct descr { 208 krb5_socket_t s; 209 int type; 210 int port; 211 unsigned char *buf; 212 size_t size; 213 size_t len; 214 time_t timeout; 215 struct sockaddr_storage __ss; 216 struct sockaddr *sa; 217 socklen_t sock_len; 218 char addr_string[128]; 219 }; 220 221 static void 222 init_descr(struct descr *d) 223 { 224 memset(d, 0, sizeof(*d)); 225 d->sa = (struct sockaddr *)&d->__ss; 226 d->s = rk_INVALID_SOCKET; 227 } 228 229 /* 230 * re-initialize all `n' ->sa in `d'. 231 */ 232 233 static void 234 reinit_descrs (struct descr *d, int n) 235 { 236 int i; 237 238 for (i = 0; i < n; ++i) 239 d[i].sa = (struct sockaddr *)&d[i].__ss; 240 } 241 242 /* 243 * Create the socket (family, type, port) in `d' 244 */ 245 246 static void 247 init_socket(krb5_context context, 248 krb5_kdc_configuration *config, 249 struct descr *d, krb5_address *a, int family, int type, int port) 250 { 251 krb5_error_code ret; 252 struct sockaddr_storage __ss; 253 struct sockaddr *sa = (struct sockaddr *)&__ss; 254 krb5_socklen_t sa_size = sizeof(__ss); 255 256 init_descr (d); 257 258 ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port); 259 if (ret) { 260 krb5_warn(context, ret, "krb5_addr2sockaddr"); 261 rk_closesocket(d->s); 262 d->s = rk_INVALID_SOCKET; 263 return; 264 } 265 266 if (sa->sa_family != family) 267 return; 268 269 d->s = socket(family, type, 0); 270 if(rk_IS_BAD_SOCKET(d->s)){ 271 krb5_warn(context, errno, "socket(%d, %d, 0)", family, type); 272 d->s = rk_INVALID_SOCKET; 273 return; 274 } 275 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR) 276 { 277 int one = 1; 278 setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 279 } 280 #endif 281 d->type = type; 282 d->port = port; 283 284 if(rk_IS_SOCKET_ERROR(bind(d->s, sa, sa_size))){ 285 char a_str[256]; 286 size_t len; 287 288 krb5_print_address (a, a_str, sizeof(a_str), &len); 289 krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port)); 290 rk_closesocket(d->s); 291 d->s = rk_INVALID_SOCKET; 292 return; 293 } 294 if(type == SOCK_STREAM && rk_IS_SOCKET_ERROR(listen(d->s, SOMAXCONN))){ 295 char a_str[256]; 296 size_t len; 297 298 krb5_print_address (a, a_str, sizeof(a_str), &len); 299 krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port)); 300 rk_closesocket(d->s); 301 d->s = rk_INVALID_SOCKET; 302 return; 303 } 304 } 305 306 /* 307 * Allocate descriptors for all the sockets that we should listen on 308 * and return the number of them. 309 */ 310 311 static int 312 init_sockets(krb5_context context, 313 krb5_kdc_configuration *config, 314 struct descr **desc) 315 { 316 krb5_error_code ret; 317 size_t i, j; 318 struct descr *d; 319 int num = 0; 320 krb5_addresses addresses; 321 322 if (explicit_addresses.len) { 323 addresses = explicit_addresses; 324 } else { 325 ret = krb5_get_all_server_addrs (context, &addresses); 326 if (ret) 327 krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); 328 } 329 parse_ports(context, config, port_str); 330 d = malloc(addresses.len * num_ports * sizeof(*d)); 331 if (d == NULL) 332 krb5_errx(context, 1, "malloc(%lu) failed", 333 (unsigned long)num_ports * sizeof(*d)); 334 335 for (i = 0; i < num_ports; i++){ 336 for (j = 0; j < addresses.len; ++j) { 337 init_socket(context, config, &d[num], &addresses.val[j], 338 ports[i].family, ports[i].type, ports[i].port); 339 if(d[num].s != rk_INVALID_SOCKET){ 340 char a_str[80]; 341 size_t len; 342 343 krb5_print_address (&addresses.val[j], a_str, 344 sizeof(a_str), &len); 345 346 kdc_log(context, config, 5, "listening on %s port %u/%s", 347 a_str, 348 ntohs(ports[i].port), 349 (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); 350 /* XXX */ 351 num++; 352 } 353 } 354 } 355 krb5_free_addresses (context, &addresses); 356 d = realloc(d, num * sizeof(*d)); 357 if (d == NULL && num != 0) 358 krb5_errx(context, 1, "realloc(%lu) failed", 359 (unsigned long)num * sizeof(*d)); 360 reinit_descrs (d, num); 361 *desc = d; 362 return num; 363 } 364 365 /* 366 * 367 */ 368 369 static const char * 370 descr_type(struct descr *d) 371 { 372 if (d->type == SOCK_DGRAM) 373 return "udp"; 374 else if (d->type == SOCK_STREAM) 375 return "tcp"; 376 return "unknown"; 377 } 378 379 static void 380 addr_to_string(krb5_context context, 381 struct sockaddr *addr, size_t addr_len, char *str, size_t len) 382 { 383 krb5_address a; 384 if(krb5_sockaddr2address(context, addr, &a) == 0) { 385 if(krb5_print_address(&a, str, len, &len) == 0) { 386 krb5_free_address(context, &a); 387 return; 388 } 389 krb5_free_address(context, &a); 390 } 391 snprintf(str, len, "<family=%d>", addr->sa_family); 392 } 393 394 /* 395 * 396 */ 397 398 static void 399 send_reply(krb5_context context, 400 krb5_kdc_configuration *config, 401 krb5_boolean prependlength, 402 struct descr *d, 403 krb5_data *reply) 404 { 405 kdc_log(context, config, 5, 406 "sending %lu bytes to %s", (unsigned long)reply->length, 407 d->addr_string); 408 if(prependlength){ 409 unsigned char l[4]; 410 l[0] = (reply->length >> 24) & 0xff; 411 l[1] = (reply->length >> 16) & 0xff; 412 l[2] = (reply->length >> 8) & 0xff; 413 l[3] = reply->length & 0xff; 414 if(rk_IS_SOCKET_ERROR(sendto(d->s, l, sizeof(l), 0, d->sa, d->sock_len))) { 415 kdc_log (context, config, 416 0, "sendto(%s): %s", d->addr_string, 417 strerror(rk_SOCK_ERRNO)); 418 return; 419 } 420 } 421 if(rk_IS_SOCKET_ERROR(sendto(d->s, reply->data, reply->length, 0, d->sa, d->sock_len))) { 422 kdc_log (context, config, 0, "sendto(%s): %s", d->addr_string, 423 strerror(rk_SOCK_ERRNO)); 424 return; 425 } 426 } 427 428 /* 429 * Handle the request in `buf, len' to socket `d' 430 */ 431 432 static void 433 do_request(krb5_context context, 434 krb5_kdc_configuration *config, 435 void *buf, size_t len, krb5_boolean prependlength, 436 struct descr *d) 437 { 438 krb5_error_code ret; 439 krb5_data reply; 440 int datagram_reply = (d->type == SOCK_DGRAM); 441 442 krb5_kdc_update_time(NULL); 443 444 krb5_data_zero(&reply); 445 ret = krb5_kdc_process_request(context, config, 446 buf, len, &reply, &prependlength, 447 d->addr_string, d->sa, 448 datagram_reply); 449 if(request_log) 450 krb5_kdc_save_request(context, request_log, buf, len, &reply, d->sa); 451 if(reply.length){ 452 send_reply(context, config, prependlength, d, &reply); 453 krb5_data_free(&reply); 454 } 455 if(ret) 456 kdc_log(context, config, 0, 457 "Failed processing %lu byte request from %s", 458 (unsigned long)len, d->addr_string); 459 } 460 461 /* 462 * Handle incoming data to the UDP socket in `d' 463 */ 464 465 static void 466 handle_udp(krb5_context context, 467 krb5_kdc_configuration *config, 468 struct descr *d) 469 { 470 unsigned char *buf; 471 ssize_t n; 472 473 buf = malloc(max_request_udp); 474 if(buf == NULL){ 475 kdc_log(context, config, 0, "Failed to allocate %lu bytes", (unsigned long)max_request_udp); 476 return; 477 } 478 479 d->sock_len = sizeof(d->__ss); 480 n = recvfrom(d->s, buf, max_request_udp, 0, d->sa, &d->sock_len); 481 if(rk_IS_SOCKET_ERROR(n)) 482 krb5_warn(context, rk_SOCK_ERRNO, "recvfrom"); 483 else { 484 addr_to_string (context, d->sa, d->sock_len, 485 d->addr_string, sizeof(d->addr_string)); 486 if ((size_t)n == max_request_udp) { 487 krb5_data data; 488 krb5_warn(context, errno, 489 "recvfrom: truncated packet from %s, asking for TCP", 490 d->addr_string); 491 krb5_mk_error(context, 492 KRB5KRB_ERR_RESPONSE_TOO_BIG, 493 NULL, 494 NULL, 495 NULL, 496 NULL, 497 NULL, 498 NULL, 499 &data); 500 send_reply(context, config, FALSE, d, &data); 501 krb5_data_free(&data); 502 } else { 503 do_request(context, config, buf, n, FALSE, d); 504 } 505 } 506 free (buf); 507 } 508 509 static void 510 clear_descr(struct descr *d) 511 { 512 if(d->buf) 513 memset(d->buf, 0, d->size); 514 d->len = 0; 515 if(d->s != rk_INVALID_SOCKET) 516 rk_closesocket(d->s); 517 d->s = rk_INVALID_SOCKET; 518 } 519 520 521 /* remove HTTP %-quoting from buf */ 522 static int 523 de_http(char *buf) 524 { 525 unsigned char *p, *q; 526 for(p = q = (unsigned char *)buf; *p; p++, q++) { 527 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) { 528 unsigned int x; 529 if(sscanf((char *)p + 1, "%2x", &x) != 1) 530 return -1; 531 *q = x; 532 p += 2; 533 } else 534 *q = *p; 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 krb5_warn(context, rk_SOCK_ERRNO, "accept"); 560 return; 561 } 562 563 #ifdef FD_SETSIZE 564 if (s >= FD_SETSIZE) { 565 krb5_warnx(context, "socket FD too large"); 566 rk_closesocket (s); 567 return; 568 } 569 #endif 570 571 d[child].s = s; 572 d[child].timeout = time(NULL) + TCP_TIMEOUT; 573 d[child].type = SOCK_STREAM; 574 addr_to_string (context, 575 d[child].sa, d[child].sock_len, 576 d[child].addr_string, sizeof(d[child].addr_string)); 577 } 578 579 /* 580 * Grow `d' to handle at least `n'. 581 * Return != 0 if fails 582 */ 583 584 static int 585 grow_descr (krb5_context context, 586 krb5_kdc_configuration *config, 587 struct descr *d, size_t n) 588 { 589 if (d->size - d->len < n) { 590 unsigned char *tmp; 591 size_t grow; 592 593 grow = max(1024, d->len + n); 594 if (d->size + grow > max_request_tcp) { 595 kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).", 596 (unsigned long)d->size + grow); 597 clear_descr(d); 598 return -1; 599 } 600 tmp = realloc (d->buf, d->size + grow); 601 if (tmp == NULL) { 602 kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.", 603 (unsigned long)d->size + grow); 604 clear_descr(d); 605 return -1; 606 } 607 d->size += grow; 608 d->buf = tmp; 609 } 610 return 0; 611 } 612 613 /* 614 * Try to handle the TCP data at `d->buf, d->len'. 615 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 616 */ 617 618 static int 619 handle_vanilla_tcp (krb5_context context, 620 krb5_kdc_configuration *config, 621 struct descr *d) 622 { 623 krb5_storage *sp; 624 uint32_t len; 625 626 sp = krb5_storage_from_mem(d->buf, d->len); 627 if (sp == NULL) { 628 kdc_log (context, config, 0, "krb5_storage_from_mem failed"); 629 return -1; 630 } 631 krb5_ret_uint32(sp, &len); 632 krb5_storage_free(sp); 633 if(d->len - 4 >= len) { 634 memmove(d->buf, d->buf + 4, d->len - 4); 635 d->len -= 4; 636 return 1; 637 } 638 return 0; 639 } 640 641 /* 642 * Try to handle the TCP/HTTP data at `d->buf, d->len'. 643 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 644 */ 645 646 static int 647 handle_http_tcp (krb5_context context, 648 krb5_kdc_configuration *config, 649 struct descr *d) 650 { 651 char *s, *p, *t; 652 void *data; 653 char *proto; 654 int len; 655 656 s = (char *)d->buf; 657 658 /* If its a multi line query, truncate off the first line */ 659 p = strstr(s, "\r\n"); 660 if (p) 661 *p = 0; 662 663 p = NULL; 664 t = strtok_r(s, " \t", &p); 665 if (t == NULL) { 666 kdc_log(context, config, 0, 667 "Missing HTTP operand (GET) request from %s", d->addr_string); 668 return -1; 669 } 670 671 t = strtok_r(NULL, " \t", &p); 672 if(t == NULL) { 673 kdc_log(context, config, 0, 674 "Missing HTTP GET data in request from %s", d->addr_string); 675 return -1; 676 } 677 678 data = malloc(strlen(t)); 679 if (data == NULL) { 680 kdc_log(context, config, 0, "Failed to allocate %lu bytes", 681 (unsigned long)strlen(t)); 682 return -1; 683 } 684 if(*t == '/') 685 t++; 686 if(de_http(t) != 0) { 687 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 688 kdc_log(context, config, 5, "HTTP request: %s", t); 689 free(data); 690 return -1; 691 } 692 proto = strtok_r(NULL, " \t", &p); 693 if (proto == NULL) { 694 kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); 695 free(data); 696 return -1; 697 } 698 len = base64_decode(t, data); 699 if(len <= 0){ 700 const char *msg = 701 " 404 Not found\r\n" 702 "Server: Heimdal/" VERSION "\r\n" 703 "Cache-Control: no-cache\r\n" 704 "Pragma: no-cache\r\n" 705 "Content-type: text/html\r\n" 706 "Content-transfer-encoding: 8bit\r\n\r\n" 707 "<TITLE>404 Not found</TITLE>\r\n" 708 "<H1>404 Not found</H1>\r\n" 709 "That page doesn't exist, maybe you are looking for " 710 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n"; 711 kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string); 712 kdc_log(context, config, 5, "HTTP request: %s", t); 713 free(data); 714 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 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 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) { 720 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 721 d->addr_string, strerror(rk_SOCK_ERRNO)); 722 return -1; 723 } 724 return -1; 725 } 726 { 727 const char *msg = 728 " 200 OK\r\n" 729 "Server: Heimdal/" VERSION "\r\n" 730 "Cache-Control: no-cache\r\n" 731 "Pragma: no-cache\r\n" 732 "Content-type: application/octet-stream\r\n" 733 "Content-transfer-encoding: binary\r\n\r\n"; 734 if (rk_IS_SOCKET_ERROR(send(d->s, proto, strlen(proto), 0))) { 735 free(data); 736 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 737 d->addr_string, strerror(rk_SOCK_ERRNO)); 738 return -1; 739 } 740 if (rk_IS_SOCKET_ERROR(send(d->s, msg, strlen(msg), 0))) { 741 free(data); 742 kdc_log(context, config, 0, "HTTP write failed: %s: %s", 743 d->addr_string, strerror(rk_SOCK_ERRNO)); 744 return -1; 745 } 746 } 747 if ((size_t)len > d->len) 748 len = d->len; 749 memcpy(d->buf, data, len); 750 d->len = len; 751 free(data); 752 return 1; 753 } 754 755 /* 756 * Handle incoming data to the TCP socket in `d[index]' 757 */ 758 759 static void 760 handle_tcp(krb5_context context, 761 krb5_kdc_configuration *config, 762 struct descr *d, int idx, int min_free) 763 { 764 unsigned char buf[1024]; 765 int n; 766 int ret = 0; 767 768 if (d[idx].timeout == 0) { 769 add_new_tcp (context, config, d, idx, min_free); 770 return; 771 } 772 773 n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL); 774 if(rk_IS_SOCKET_ERROR(n)){ 775 krb5_warn(context, rk_SOCK_ERRNO, "recvfrom failed from %s to %s/%d", 776 d[idx].addr_string, descr_type(d + idx), 777 ntohs(d[idx].port)); 778 return; 779 } else if (n == 0) { 780 krb5_warnx(context, "connection closed before end of data after %lu " 781 "bytes from %s to %s/%d", (unsigned long)d[idx].len, 782 d[idx].addr_string, descr_type(d + idx), 783 ntohs(d[idx].port)); 784 clear_descr (d + idx); 785 return; 786 } 787 if (grow_descr (context, config, &d[idx], n)) 788 return; 789 memcpy(d[idx].buf + d[idx].len, buf, n); 790 d[idx].len += n; 791 if(d[idx].len > 4 && d[idx].buf[0] == 0) { 792 ret = handle_vanilla_tcp (context, config, &d[idx]); 793 } else if(enable_http && 794 d[idx].len >= 4 && 795 strncmp((char *)d[idx].buf, "GET ", 4) == 0 && 796 strncmp((char *)d[idx].buf + d[idx].len - 4, 797 "\r\n\r\n", 4) == 0) { 798 799 /* remove the trailing \r\n\r\n so the string is NUL terminated */ 800 d[idx].buf[d[idx].len - 4] = '\0'; 801 802 ret = handle_http_tcp (context, config, &d[idx]); 803 if (ret < 0) 804 clear_descr (d + idx); 805 } else if (d[idx].len > 4) { 806 kdc_log (context, config, 807 0, "TCP data of strange type from %s to %s/%d", 808 d[idx].addr_string, descr_type(d + idx), 809 ntohs(d[idx].port)); 810 if (d[idx].buf[0] & 0x80) { 811 krb5_data reply; 812 813 kdc_log (context, config, 0, "TCP extension not supported"); 814 815 ret = krb5_mk_error(context, 816 KRB5KRB_ERR_FIELD_TOOLONG, 817 NULL, 818 NULL, 819 NULL, 820 NULL, 821 NULL, 822 NULL, 823 &reply); 824 if (ret == 0) { 825 send_reply(context, config, TRUE, d + idx, &reply); 826 krb5_data_free(&reply); 827 } 828 } 829 clear_descr(d + idx); 830 return; 831 } 832 if (ret < 0) 833 return; 834 else if (ret == 1) { 835 do_request(context, config, 836 d[idx].buf, d[idx].len, TRUE, &d[idx]); 837 clear_descr(d + idx); 838 } 839 } 840 841 void 842 loop(krb5_context context, 843 krb5_kdc_configuration *config) 844 { 845 struct descr *d; 846 unsigned int ndescr; 847 848 ndescr = init_sockets(context, config, &d); 849 if(ndescr <= 0) 850 krb5_errx(context, 1, "No sockets!"); 851 kdc_log(context, config, 0, "KDC started"); 852 while(exit_flag == 0){ 853 struct timeval tmout; 854 fd_set fds; 855 int min_free = -1; 856 int max_fd = 0; 857 size_t i; 858 859 FD_ZERO(&fds); 860 for(i = 0; i < ndescr; i++) { 861 if(!rk_IS_BAD_SOCKET(d[i].s)){ 862 if(d[i].type == SOCK_STREAM && 863 d[i].timeout && d[i].timeout < time(NULL)) { 864 kdc_log(context, config, 1, 865 "TCP-connection from %s expired after %lu bytes", 866 d[i].addr_string, (unsigned long)d[i].len); 867 clear_descr(&d[i]); 868 continue; 869 } 870 #ifndef NO_LIMIT_FD_SETSIZE 871 if(max_fd < d[i].s) 872 max_fd = d[i].s; 873 #ifdef FD_SETSIZE 874 if (max_fd >= FD_SETSIZE) 875 krb5_errx(context, 1, "fd too large"); 876 #endif 877 #endif 878 FD_SET(d[i].s, &fds); 879 } else if(min_free < 0 || i < (size_t)min_free) 880 min_free = i; 881 } 882 if(min_free == -1){ 883 struct descr *tmp; 884 tmp = realloc(d, (ndescr + 4) * sizeof(*d)); 885 if(tmp == NULL) 886 krb5_warnx(context, "No memory"); 887 else { 888 d = tmp; 889 reinit_descrs (d, ndescr); 890 memset(d + ndescr, 0, 4 * sizeof(*d)); 891 for(i = ndescr; i < ndescr + 4; i++) 892 init_descr (&d[i]); 893 min_free = ndescr; 894 ndescr += 4; 895 } 896 } 897 898 tmout.tv_sec = TCP_TIMEOUT; 899 tmout.tv_usec = 0; 900 switch(select(max_fd + 1, &fds, 0, 0, &tmout)){ 901 case 0: 902 break; 903 case -1: 904 if (errno != EINTR) 905 krb5_warn(context, rk_SOCK_ERRNO, "select"); 906 break; 907 default: 908 for(i = 0; i < ndescr; i++) 909 if(!rk_IS_BAD_SOCKET(d[i].s) && FD_ISSET(d[i].s, &fds)) { 910 if(d[i].type == SOCK_DGRAM) 911 handle_udp(context, config, &d[i]); 912 else if(d[i].type == SOCK_STREAM) 913 handle_tcp(context, config, d, i, min_free); 914 } 915 } 916 } 917 if (0); 918 #ifdef SIGXCPU 919 else if(exit_flag == SIGXCPU) 920 kdc_log(context, config, 0, "CPU time limit exceeded"); 921 #endif 922 else if(exit_flag == SIGINT || exit_flag == SIGTERM) 923 kdc_log(context, config, 0, "Terminated"); 924 else 925 kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag); 926 free (d); 927 } 928