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