1 /* 2 * Copyright (c) 1997-2000 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 RCSID("$Id: connect.c,v 1.70 2000/02/19 18:41:24 assar Exp $"); 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 int num_ports; 52 53 /* 54 * add `family, port, protocol' to the list with duplicate suppresion. 55 */ 56 57 static void 58 add_port(int family, int port, const char *protocol) 59 { 60 int type; 61 int i; 62 63 if(strcmp(protocol, "udp") == 0) 64 type = SOCK_DGRAM; 65 else if(strcmp(protocol, "tcp") == 0) 66 type = SOCK_STREAM; 67 else 68 return; 69 for(i = 0; i < num_ports; i++){ 70 if(ports[i].type == type 71 && ports[i].port == port 72 && ports[i].family == family) 73 return; 74 } 75 ports = realloc(ports, (num_ports + 1) * sizeof(*ports)); 76 if (ports == NULL) 77 krb5_err (context, 1, errno, "realloc"); 78 ports[num_ports].family = family; 79 ports[num_ports].type = type; 80 ports[num_ports].port = port; 81 num_ports++; 82 } 83 84 /* 85 * add a triple but with service -> port lookup 86 * (this prints warnings for stuff that does not exist) 87 */ 88 89 static void 90 add_port_service(int family, const char *service, int port, 91 const char *protocol) 92 { 93 port = krb5_getportbyname (context, service, protocol, port); 94 add_port (family, port, protocol); 95 } 96 97 /* 98 * add the port with service -> port lookup or string -> number 99 * (no warning is printed) 100 */ 101 102 static void 103 add_port_string (int family, const char *port_str, const char *protocol) 104 { 105 struct servent *sp; 106 int port; 107 108 sp = roken_getservbyname (port_str, protocol); 109 if (sp != NULL) { 110 port = sp->s_port; 111 } else { 112 char *end; 113 114 port = htons(strtol(port_str, &end, 0)); 115 if (end == port_str) 116 return; 117 } 118 add_port (family, port, protocol); 119 } 120 121 /* 122 * add the standard collection of ports for `family' 123 */ 124 125 static void 126 add_standard_ports (int family) 127 { 128 add_port_service(family, "kerberos", 88, "udp"); 129 add_port_service(family, "kerberos", 88, "tcp"); 130 add_port_service(family, "kerberos-sec", 88, "udp"); 131 add_port_service(family, "kerberos-sec", 88, "tcp"); 132 add_port_service(family, "kerberos-iv", 750, "udp"); 133 add_port_service(family, "kerberos-iv", 750, "tcp"); 134 if(enable_http) 135 add_port_service(family, "http", 80, "tcp"); 136 #ifdef KASERVER 137 if (enable_kaserver) 138 add_port_service(family, "afs3-kaserver", 7004, "udp"); 139 #endif 140 } 141 142 /* 143 * parse the set of space-delimited ports in `str' and add them. 144 * "+" => all the standard ones 145 * otherwise it's port|service[/protocol] 146 */ 147 148 static void 149 parse_ports(const char *str) 150 { 151 char *pos = NULL; 152 char *p; 153 char *str_copy = strdup (str); 154 155 p = strtok_r(str_copy, " \t", &pos); 156 while(p != NULL) { 157 if(strcmp(p, "+") == 0) { 158 #ifdef HAVE_IPV6 159 add_standard_ports(AF_INET6); 160 #endif 161 add_standard_ports(AF_INET); 162 } else { 163 char *q = strchr(p, '/'); 164 if(q){ 165 *q++ = 0; 166 #ifdef HAVE_IPV6 167 add_port_string(AF_INET6, p, q); 168 #endif 169 add_port_string(AF_INET, p, q); 170 }else { 171 #ifdef HAVE_IPV6 172 add_port_string(AF_INET6, p, "udp"); 173 add_port_string(AF_INET6, p, "tcp"); 174 #endif 175 add_port_string(AF_INET, p, "udp"); 176 add_port_string(AF_INET, p, "tcp"); 177 } 178 } 179 180 p = strtok_r(NULL, " \t", &pos); 181 } 182 free (str_copy); 183 } 184 185 /* 186 * every socket we listen on 187 */ 188 189 struct descr { 190 int s; 191 int type; 192 unsigned char *buf; 193 size_t size; 194 size_t len; 195 time_t timeout; 196 struct sockaddr_storage __ss; 197 struct sockaddr *sa; 198 int sock_len; 199 char addr_string[128]; 200 }; 201 202 /* 203 * Create the socket (family, type, port) in `d' 204 */ 205 206 static void 207 init_socket(struct descr *d, krb5_address *a, int family, int type, int port) 208 { 209 krb5_error_code ret; 210 struct sockaddr_storage __ss; 211 struct sockaddr *sa = (struct sockaddr *)&__ss; 212 int sa_size; 213 214 memset(d, 0, sizeof(*d)); 215 d->sa = (struct sockaddr *)&d->__ss; 216 d->s = -1; 217 218 ret = krb5_addr2sockaddr (a, sa, &sa_size, port); 219 if (ret) { 220 krb5_warn(context, ret, "krb5_addr2sockaddr"); 221 close(d->s); 222 d->s = -1; 223 return; 224 } 225 226 if (sa->sa_family != family) 227 return; 228 229 d->s = socket(family, type, 0); 230 if(d->s < 0){ 231 krb5_warn(context, errno, "socket(%d, %d, 0)", family, type); 232 d->s = -1; 233 return; 234 } 235 #if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR) 236 { 237 int one = 1; 238 setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 239 } 240 #endif 241 d->type = type; 242 243 if(bind(d->s, sa, sa_size) < 0){ 244 char a_str[256]; 245 size_t len; 246 247 krb5_print_address (a, a_str, sizeof(a_str), &len); 248 krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port)); 249 close(d->s); 250 d->s = -1; 251 return; 252 } 253 if(type == SOCK_STREAM && listen(d->s, SOMAXCONN) < 0){ 254 char a_str[256]; 255 size_t len; 256 257 krb5_print_address (a, a_str, sizeof(a_str), &len); 258 krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port)); 259 close(d->s); 260 d->s = -1; 261 return; 262 } 263 } 264 265 /* 266 * Allocate descriptors for all the sockets that we should listen on 267 * and return the number of them. 268 */ 269 270 static int 271 init_sockets(struct descr **desc) 272 { 273 krb5_error_code ret; 274 int i, j; 275 struct descr *d; 276 int num = 0; 277 krb5_addresses addresses; 278 279 if (explicit_addresses.len) { 280 addresses = explicit_addresses; 281 } else { 282 ret = krb5_get_all_server_addrs (context, &addresses); 283 if (ret) 284 krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); 285 } 286 parse_ports(port_str); 287 d = malloc(addresses.len * num_ports * sizeof(*d)); 288 if (d == NULL) 289 krb5_errx(context, 1, "malloc(%u) failed", num_ports * sizeof(*d)); 290 291 for (i = 0; i < num_ports; i++){ 292 for (j = 0; j < addresses.len; ++j) { 293 init_socket(&d[num], &addresses.val[j], 294 ports[i].family, ports[i].type, ports[i].port); 295 if(d[num].s != -1){ 296 char a_str[80]; 297 size_t len; 298 299 krb5_print_address (&addresses.val[j], a_str, 300 sizeof(a_str), &len); 301 302 kdc_log(5, "listening on %s port %u/%s", 303 a_str, 304 ntohs(ports[i].port), 305 (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); 306 /* XXX */ 307 num++; 308 } 309 } 310 } 311 krb5_free_addresses (context, &addresses); 312 d = realloc(d, num * sizeof(*d)); 313 if (d == NULL && num != 0) 314 krb5_errx(context, 1, "realloc(%u) failed", num * sizeof(*d)); 315 *desc = d; 316 return num; 317 } 318 319 /* 320 * handle the request in `buf, len', from `addr' (or `from' as a string), 321 * sending a reply in `reply'. 322 */ 323 324 static int 325 process_request(unsigned char *buf, 326 size_t len, 327 krb5_data *reply, 328 int *sendlength, 329 const char *from, 330 struct sockaddr *addr) 331 { 332 KDC_REQ req; 333 #ifdef KRB4 334 Ticket ticket; 335 #endif 336 krb5_error_code ret; 337 size_t i; 338 339 gettimeofday(&now, NULL); 340 if(decode_AS_REQ(buf, len, &req, &i) == 0){ 341 ret = as_rep(&req, reply, from, addr); 342 free_AS_REQ(&req); 343 return ret; 344 }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){ 345 ret = tgs_rep(&req, reply, from, addr); 346 free_TGS_REQ(&req); 347 return ret; 348 } 349 #ifdef KRB4 350 else if(maybe_version4(buf, len)){ 351 *sendlength = 0; /* elbitapmoc sdrawkcab XXX */ 352 do_version4(buf, len, reply, from, (struct sockaddr_in*)addr); 353 return 0; 354 }else if(decode_Ticket(buf, len, &ticket, &i) == 0){ 355 ret = do_524(&ticket, reply, from, addr); 356 free_Ticket(&ticket); 357 return ret; 358 } 359 #endif 360 #ifdef KASERVER 361 else if (enable_kaserver) { 362 ret = do_kaserver (buf, len, reply, from, (struct sockaddr_in*)addr); 363 return ret; 364 } 365 #endif 366 367 return -1; 368 } 369 370 static void 371 addr_to_string(struct sockaddr *addr, size_t addr_len, char *str, size_t len) 372 { 373 krb5_address a; 374 krb5_sockaddr2address(addr, &a); 375 if(krb5_print_address(&a, str, len, &len) == 0) { 376 krb5_free_address(context, &a); 377 return; 378 } 379 krb5_free_address(context, &a); 380 snprintf(str, len, "<family=%d>", addr->sa_family); 381 } 382 383 /* 384 * Handle the request in `buf, len' to socket `d' 385 */ 386 387 static void 388 do_request(void *buf, size_t len, int sendlength, 389 struct descr *d) 390 { 391 krb5_error_code ret; 392 krb5_data reply; 393 394 reply.length = 0; 395 ret = process_request(buf, len, &reply, &sendlength, 396 d->addr_string, d->sa); 397 if(reply.length){ 398 kdc_log(5, "sending %d bytes to %s", reply.length, d->addr_string); 399 if(sendlength){ 400 unsigned char len[4]; 401 len[0] = (reply.length >> 24) & 0xff; 402 len[1] = (reply.length >> 16) & 0xff; 403 len[2] = (reply.length >> 8) & 0xff; 404 len[3] = reply.length & 0xff; 405 if(sendto(d->s, len, sizeof(len), 0, d->sa, d->sock_len) < 0) { 406 kdc_log (0, "sendto(%s): %s", d->addr_string, strerror(errno)); 407 krb5_data_free(&reply); 408 return; 409 } 410 } 411 if(sendto(d->s, reply.data, reply.length, 0, d->sa, d->sock_len) < 0) { 412 kdc_log (0, "sendto(%s): %s", d->addr_string, strerror(errno)); 413 krb5_data_free(&reply); 414 return; 415 } 416 krb5_data_free(&reply); 417 } 418 if(ret) 419 kdc_log(0, "Failed processing %lu byte request from %s", 420 (unsigned long)len, d->addr_string); 421 } 422 423 /* 424 * Handle incoming data to the UDP socket in `d' 425 */ 426 427 static void 428 handle_udp(struct descr *d) 429 { 430 unsigned char *buf; 431 int n; 432 433 buf = malloc(max_request); 434 if(buf == NULL){ 435 kdc_log(0, "Failed to allocate %u bytes", max_request); 436 return; 437 } 438 439 d->sock_len = sizeof(d->__ss); 440 n = recvfrom(d->s, buf, max_request, 0, d->sa, &d->sock_len); 441 if(n < 0) 442 krb5_warn(context, errno, "recvfrom"); 443 else { 444 addr_to_string (d->sa, d->sock_len, 445 d->addr_string, sizeof(d->addr_string)); 446 do_request(buf, n, 0, d); 447 } 448 free (buf); 449 } 450 451 static void 452 clear_descr(struct descr *d) 453 { 454 if(d->buf) 455 memset(d->buf, 0, d->size); 456 d->len = 0; 457 if(d->s != -1) 458 close(d->s); 459 d->s = -1; 460 } 461 462 463 /* remove HTTP %-quoting from buf */ 464 static int 465 de_http(char *buf) 466 { 467 char *p, *q; 468 for(p = q = buf; *p; p++, q++) { 469 if(*p == '%') { 470 unsigned int x; 471 if(sscanf(p + 1, "%2x", &x) != 1) 472 return -1; 473 *q = x; 474 p += 2; 475 } else 476 *q = *p; 477 } 478 *q = '\0'; 479 return 0; 480 } 481 482 #define TCP_TIMEOUT 4 483 484 /* 485 * accept a new TCP connection on `d[index]' 486 */ 487 488 static void 489 add_new_tcp (struct descr *d, int index, int min_free) 490 { 491 int s; 492 493 d->sock_len = sizeof(d->__ss); 494 s = accept(d[index].s, d->sa, &d->sock_len); 495 if(s < 0) { 496 krb5_warn(context, errno, "accept"); 497 return; 498 } 499 if(min_free == -1){ 500 close(s); 501 return; 502 } 503 504 d[min_free].s = s; 505 d[min_free].timeout = time(NULL) + TCP_TIMEOUT; 506 d[min_free].type = SOCK_STREAM; 507 addr_to_string (d[min_free].sa, d[min_free].sock_len, 508 d[min_free].addr_string, sizeof(d[min_free].addr_string)); 509 } 510 511 /* 512 * Grow `d' to handle at least `n'. 513 * Return != 0 if fails 514 */ 515 516 static int 517 grow_descr (struct descr *d, size_t n) 518 { 519 if (d->size - d->len < n) { 520 unsigned char *tmp; 521 522 d->size += max(1024, d->len + n); 523 if (d->size >= max_request) { 524 kdc_log(0, "Request exceeds max request size (%u bytes).", 525 d->size); 526 clear_descr(d); 527 return -1; 528 } 529 tmp = realloc (d->buf, d->size); 530 if (tmp == NULL) { 531 kdc_log(0, "Failed to re-allocate %u bytes.", d->size); 532 clear_descr(d); 533 return -1; 534 } 535 d->buf = tmp; 536 } 537 return 0; 538 } 539 540 /* 541 * Try to handle the TCP data at `d->buf, d->len'. 542 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 543 */ 544 545 static int 546 handle_vanilla_tcp (struct descr *d) 547 { 548 krb5_storage *sp; 549 int32_t len; 550 551 sp = krb5_storage_from_mem(d->buf, d->len); 552 if (sp == NULL) { 553 kdc_log (0, "krb5_storage_from_mem failed"); 554 return -1; 555 } 556 krb5_ret_int32(sp, &len); 557 krb5_storage_free(sp); 558 if(d->len - 4 >= len) { 559 memcpy(d->buf, d->buf + 4, d->len - 4); 560 return 1; 561 } 562 return 0; 563 } 564 565 /* 566 * Try to handle the TCP/HTTP data at `d->buf, d->len'. 567 * Return -1 if failed, 0 if succesful, and 1 if data is complete. 568 */ 569 570 static int 571 handle_http_tcp (struct descr *d) 572 { 573 char *s, *p, *t; 574 void *data; 575 char *proto; 576 int len; 577 578 s = (char *)d->buf; 579 580 p = strstr(s, "\r\n"); 581 if (p == NULL) { 582 kdc_log(0, "Malformed HTTP request from %s", d->addr_string); 583 return -1; 584 } 585 *p = 0; 586 587 p = NULL; 588 t = strtok_r(s, " \t", &p); 589 if (t == NULL) { 590 kdc_log(0, "Malformed HTTP request from %s", d->addr_string); 591 return -1; 592 } 593 t = strtok_r(NULL, " \t", &p); 594 if(t == NULL) { 595 kdc_log(0, "Malformed HTTP request from %s", d->addr_string); 596 return -1; 597 } 598 data = malloc(strlen(t)); 599 if (data == NULL) { 600 kdc_log(0, "Failed to allocate %u bytes", strlen(t)); 601 return -1; 602 } 603 if(*t == '/') 604 t++; 605 if(de_http(t) != 0) { 606 kdc_log(0, "Malformed HTTP request from %s", d->addr_string); 607 kdc_log(5, "Request: %s", t); 608 free(data); 609 return -1; 610 } 611 proto = strtok_r(NULL, " \t", &p); 612 if (proto == NULL) { 613 kdc_log(0, "Malformed HTTP request from %s", d->addr_string); 614 free(data); 615 return -1; 616 } 617 len = base64_decode(t, data); 618 if(len <= 0){ 619 const char *msg = 620 " 404 Not found\r\n" 621 "Server: Heimdal/" VERSION "\r\n" 622 "Content-type: text/html\r\n" 623 "Content-transfer-encoding: 8bit\r\n\r\n" 624 "<TITLE>404 Not found</TITLE>\r\n" 625 "<H1>404 Not found</H1>\r\n" 626 "That page doesn't exist, maybe you are looking for " 627 "<A HREF=\"http://www.pdc.kth.se/heimdal\">Heimdal</A>?\r\n"; 628 write(d->s, proto, strlen(proto)); 629 write(d->s, msg, strlen(msg)); 630 kdc_log(0, "HTTP request from %s is non KDC request", d->addr_string); 631 kdc_log(5, "Request: %s", t); 632 free(data); 633 return -1; 634 } 635 { 636 const char *msg = 637 " 200 OK\r\n" 638 "Server: Heimdal/" VERSION "\r\n" 639 "Content-type: application/octet-stream\r\n" 640 "Content-transfer-encoding: binary\r\n\r\n"; 641 write(d->s, proto, strlen(proto)); 642 write(d->s, msg, strlen(msg)); 643 } 644 memcpy(d->buf, data, len); 645 d->len = len; 646 free(data); 647 return 1; 648 } 649 650 /* 651 * Handle incoming data to the TCP socket in `d[index]' 652 */ 653 654 static void 655 handle_tcp(struct descr *d, int index, int min_free) 656 { 657 unsigned char buf[1024]; 658 int n; 659 int ret = 0; 660 661 if (d[index].timeout == 0) { 662 add_new_tcp (d, index, min_free); 663 return; 664 } 665 666 n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL); 667 if(n < 0){ 668 krb5_warn(context, errno, "recvfrom"); 669 return; 670 } 671 if (grow_descr (&d[index], n)) 672 return; 673 memcpy(d[index].buf + d[index].len, buf, n); 674 d[index].len += n; 675 if(d[index].len > 4 && d[index].buf[0] == 0) { 676 ret = handle_vanilla_tcp (&d[index]); 677 } else if(enable_http && 678 d[index].len >= 4 && 679 strncmp((char *)d[index].buf, "GET ", 4) == 0 && 680 strncmp((char *)d[index].buf + d[index].len - 4, 681 "\r\n\r\n", 4) == 0) { 682 ret = handle_http_tcp (&d[index]); 683 if (ret < 0) 684 clear_descr (d + index); 685 } else if (d[index].len > 4) { 686 kdc_log (0, "TCP data of strange type from %s", d[index].addr_string); 687 return; 688 } 689 if (ret < 0) 690 return; 691 else if (ret == 1) { 692 do_request(d[index].buf, d[index].len, 1, &d[index]); 693 clear_descr(d + index); 694 } 695 } 696 697 void 698 loop(void) 699 { 700 struct descr *d; 701 int ndescr; 702 703 ndescr = init_sockets(&d); 704 if(ndescr <= 0) 705 krb5_errx(context, 1, "No sockets!"); 706 while(exit_flag == 0){ 707 struct timeval tmout; 708 fd_set fds; 709 int min_free = -1; 710 int max_fd = 0; 711 int i; 712 FD_ZERO(&fds); 713 for(i = 0; i < ndescr; i++){ 714 if(d[i].s >= 0){ 715 if(d[i].type == SOCK_STREAM && 716 d[i].timeout && d[i].timeout < time(NULL)) { 717 kdc_log(1, "TCP-connection from %s expired after %u bytes", 718 d[i].addr_string, d[i].len); 719 clear_descr(&d[i]); 720 continue; 721 } 722 if(max_fd < d[i].s) 723 max_fd = d[i].s; 724 FD_SET(d[i].s, &fds); 725 }else if(min_free < 0 || i < min_free) 726 min_free = i; 727 } 728 if(min_free == -1){ 729 struct descr *tmp; 730 tmp = realloc(d, (ndescr + 4) * sizeof(*d)); 731 if(tmp == NULL) 732 krb5_warnx(context, "No memory"); 733 else{ 734 d = tmp; 735 memset(d + ndescr, 0, 4 * sizeof(*d)); 736 for(i = ndescr; i < ndescr + 4; i++) 737 d[i].s = -1; 738 min_free = ndescr; 739 ndescr += 4; 740 } 741 } 742 743 tmout.tv_sec = TCP_TIMEOUT; 744 tmout.tv_usec = 0; 745 switch(select(max_fd + 1, &fds, 0, 0, &tmout)){ 746 case 0: 747 break; 748 case -1: 749 if (errno != EINTR) 750 krb5_warn(context, errno, "select"); 751 break; 752 default: 753 for(i = 0; i < ndescr; i++) 754 if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) { 755 if(d[i].type == SOCK_DGRAM) 756 handle_udp(&d[i]); 757 else if(d[i].type == SOCK_STREAM) 758 handle_tcp(d, i, min_free); 759 } 760 } 761 } 762 free (d); 763 } 764