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