1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * kdc/network.c 8 * 9 * Copyright 1990,2000 by the Massachusetts Institute of Technology. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 * 31 * Network code for Kerberos v5 KDC. 32 */ 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #define NEED_SOCKETS 36 #include "k5-int.h" 37 #include "com_err.h" 38 #include "kdc_util.h" 39 #include "extern.h" 40 #include "kdc5_err.h" 41 #include "adm_proto.h" 42 #include <sys/ioctl.h> 43 #include <syslog.h> 44 45 #include <stddef.h> 46 #include <ctype.h> 47 #include <port-sockets.h> 48 /* #include <socket-utils.h> */ 49 50 #ifdef HAVE_NETINET_IN_H 51 #include <sys/types.h> 52 #include <netinet/in.h> 53 #include <sys/socket.h> 54 #ifdef HAVE_SYS_SOCKIO_H 55 /* for SIOCGIFCONF, etc. */ 56 #include <sys/sockio.h> 57 #endif 58 #include <sys/time.h> 59 #include <libintl.h> 60 61 #if HAVE_SYS_SELECT_H 62 #include <sys/select.h> 63 #endif 64 #include <arpa/inet.h> 65 #include <inet/ip.h> 66 #include <inet/ip6.h> 67 68 #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */ 69 #include <net/if.h> 70 #endif 71 72 #ifdef HAVE_SYS_FILIO_H 73 #include <sys/filio.h> /* FIONBIO */ 74 #endif 75 76 #include <fake-addrinfo.h> 77 78 /* Misc utility routines. */ 79 static void 80 set_sa_port(struct sockaddr *addr, int port) 81 { 82 switch (addr->sa_family) { 83 case AF_INET: 84 sa2sin(addr)->sin_port = port; 85 break; 86 #ifdef KRB5_USE_INET6 87 case AF_INET6: 88 sa2sin6(addr)->sin6_port = port; 89 break; 90 #endif 91 default: 92 break; 93 } 94 } 95 96 static int 97 ipv6_enabled() 98 { 99 #ifdef KRB5_USE_INET6 100 static int result = -1; 101 if (result == -1) { 102 int s; 103 s = socket(AF_INET6, SOCK_STREAM, 0); 104 if (s >= 0) { 105 result = 1; 106 close(s); 107 } else 108 result = 0; 109 } 110 return (result); 111 #else 112 return (0); 113 #endif 114 } 115 116 static int 117 setreuseaddr(int sock, int value) 118 { 119 return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 120 } 121 122 #if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY) 123 static int 124 setv6only(int sock, int value) 125 { 126 return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)); 127 } 128 #endif 129 130 131 static const char *paddr (struct sockaddr *sa) 132 { 133 static char buf[100]; 134 char portbuf[10]; 135 if (getnameinfo(sa, socklen(sa), 136 buf, sizeof(buf), portbuf, sizeof(portbuf), 137 NI_NUMERICHOST|NI_NUMERICSERV)) 138 strcpy(buf, "<unprintable>"); 139 else { 140 int len = sizeof(buf) - strlen(buf); 141 char *p = buf + strlen(buf); 142 if (len > 2+strlen(portbuf)) { 143 *p++ = '.'; 144 len--; 145 strncpy(p, portbuf, len); 146 } 147 } 148 return buf; 149 } 150 151 /* KDC data. */ 152 153 /* Per-connection info. */ 154 struct connection { 155 int fd; 156 enum { CONN_UDP, CONN_TCP_LISTENER, CONN_TCP } type; 157 void (*service)(struct connection *, const char *, int); 158 /* Solaris Kerberos: for auditing */ 159 in_port_t port; /* local port */ 160 union { 161 /* Type-specific information. */ 162 struct { 163 int x; 164 } udp; 165 struct { 166 int x; 167 } tcp_listener; 168 struct { 169 /* connection */ 170 struct sockaddr_storage addr_s; 171 socklen_t addrlen; 172 char addrbuf[56]; 173 krb5_fulladdr faddr; 174 krb5_address kaddr; 175 /* incoming */ 176 size_t bufsiz; 177 size_t offset; 178 char *buffer; 179 size_t msglen; 180 /* outgoing */ 181 krb5_data *response; 182 unsigned char lenbuf[4]; 183 sg_buf sgbuf[2]; 184 sg_buf *sgp; 185 int sgnum; 186 /* crude denial-of-service avoidance support */ 187 time_t start_time; 188 } tcp; 189 } u; 190 }; 191 192 193 #define SET(TYPE) struct { TYPE *data; int n, max; } 194 195 /* Start at the top and work down -- this should allow for deletions 196 without disrupting the iteration, since we delete by overwriting 197 the element to be removed with the last element. */ 198 #define FOREACH_ELT(set,idx,vvar) \ 199 for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--) 200 201 #define GROW_SET(set, incr, tmpptr) \ 202 (((int)(set.max + incr) < set.max \ 203 || (((size_t)((int)(set.max + incr) * sizeof(set.data[0])) \ 204 / sizeof(set.data[0])) \ 205 != (set.max + incr))) \ 206 ? 0 /* overflow */ \ 207 : ((tmpptr = realloc(set.data, \ 208 (int)(set.max + incr) * sizeof(set.data[0]))) \ 209 ? (set.data = tmpptr, set.max += incr, 1) \ 210 : 0)) 211 212 /* 1 = success, 0 = failure */ 213 #define ADD(set, val, tmpptr) \ 214 ((set.n < set.max || GROW_SET(set, 10, tmpptr)) \ 215 ? (set.data[set.n++] = val, 1) \ 216 : 0) 217 218 #define DEL(set, idx) \ 219 (set.data[idx] = set.data[--set.n], 0) 220 221 #define FREE_SET_DATA(set) if(set.data) free(set.data); \ 222 (set.data = 0, set.max = 0) 223 224 225 /* Set<struct connection *> connections; */ 226 static SET(struct connection *) connections; 227 #define n_sockets connections.n 228 #define conns connections.data 229 230 /* Set<u_short> udp_port_data, tcp_port_data; */ 231 static SET(u_short) udp_port_data, tcp_port_data; 232 233 #include <cm.h> 234 235 static struct select_state sstate; 236 237 static krb5_error_code add_udp_port(int port) 238 { 239 int i; 240 void *tmp; 241 u_short val; 242 u_short s_port = port; 243 244 if (s_port != port) 245 return EINVAL; 246 247 FOREACH_ELT (udp_port_data, i, val) 248 if (s_port == val) 249 return 0; 250 if (!ADD(udp_port_data, s_port, tmp)) 251 return ENOMEM; 252 return 0; 253 } 254 255 static krb5_error_code add_tcp_port(int port) 256 { 257 int i; 258 void *tmp; 259 u_short val; 260 u_short s_port = port; 261 262 if (s_port != port) 263 return EINVAL; 264 265 FOREACH_ELT (tcp_port_data, i, val) 266 if (s_port == val) 267 return 0; 268 if (!ADD(tcp_port_data, s_port, tmp)) 269 return ENOMEM; 270 return 0; 271 } 272 273 #define USE_AF AF_INET 274 #define USE_TYPE SOCK_DGRAM 275 #define USE_PROTO 0 276 #define SOCKET_ERRNO errno 277 278 struct socksetup { 279 const char *prog; 280 krb5_error_code retval; 281 }; 282 283 static struct connection * 284 add_fd (struct socksetup *data, int sock, int conntype, 285 void (*service)(struct connection *, const char *, int)) 286 { 287 struct connection *newconn; 288 void *tmp; 289 290 newconn = malloc(sizeof(*newconn)); 291 if (newconn == 0) { 292 data->retval = errno; 293 com_err(data->prog, errno, 294 gettext("cannot allocate storage for connection info")); 295 return 0; 296 } 297 if (!ADD(connections, newconn, tmp)) { 298 data->retval = errno; 299 com_err(data->prog, data->retval, gettext("cannot save socket info")); 300 free(newconn); 301 return 0; 302 } 303 304 memset(newconn, 0, sizeof(*newconn)); 305 newconn->type = conntype; 306 newconn->fd = sock; 307 newconn->service = service; 308 309 return newconn; 310 } 311 312 static void process_packet(struct connection *, const char *, int); 313 static void accept_tcp_connection(struct connection *, const char *, int); 314 static void process_tcp_connection(struct connection *, const char *, int); 315 316 static struct connection * 317 add_udp_fd (struct socksetup *data, int sock) 318 { 319 return add_fd(data, sock, CONN_UDP, process_packet); 320 } 321 322 static struct connection * 323 add_tcp_listener_fd (struct socksetup *data, int sock) 324 { 325 return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection); 326 } 327 328 static struct connection * 329 add_tcp_data_fd (struct socksetup *data, int sock) 330 { 331 return add_fd(data, sock, CONN_TCP, process_tcp_connection); 332 } 333 334 static void 335 delete_fd (struct connection *xconn) 336 { 337 struct connection *conn; 338 int i; 339 340 FOREACH_ELT(connections, i, conn) 341 if (conn == xconn) { 342 DEL(connections, i); 343 /* Solaris kerberos: fix memory leak */ 344 free(xconn); 345 return; 346 } 347 348 free(xconn); 349 } 350 351 static int 352 setnbio(int sock) 353 { 354 static const int one = 1; 355 return ioctlsocket(sock, FIONBIO, (const void *)&one); 356 } 357 358 static int 359 setnolinger(int s) 360 { 361 static const struct linger ling = { 0, 0 }; 362 return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); 363 } 364 365 /* Returns -1 or socket fd. */ 366 static int 367 setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr) 368 { 369 int sock; 370 371 sock = socket(addr->sa_family, SOCK_STREAM, 0); 372 if (sock == -1) { 373 com_err(data->prog, errno, 374 gettext("Cannot create TCP server socket on %s"), 375 paddr(addr)); 376 return -1; 377 } 378 /* 379 * Solaris Kerberos: noticed that there where bind problems for tcp sockets 380 * if kdc restarted quickly. Setting SO_REUSEADDR allowed binds to succeed. 381 */ 382 if (setreuseaddr(sock, 1) < 0) { 383 com_err(data->prog, errno, 384 gettext("enabling SO_REUSEADDR on TCP socket")); 385 close(sock); 386 return -1; 387 } 388 if (bind(sock, addr, socklen(addr)) == -1) { 389 com_err(data->prog, errno, 390 gettext("Cannot bind TCP server socket on %s"), paddr(addr)); 391 close(sock); 392 return -1; 393 } 394 if (listen(sock, 5) < 0) { 395 com_err(data->prog, errno, 396 gettext("Cannot listen on TCP server socket on %s"), 397 paddr(addr)); 398 close(sock); 399 return -1; 400 } 401 if (setnbio(sock)) { 402 com_err(data->prog, errno, 403 gettext("cannot set listening tcp socket on %s non-blocking"), 404 paddr(addr)); 405 close(sock); 406 return -1; 407 } 408 if (setnolinger(sock)) { 409 com_err(data->prog, errno, 410 gettext("disabling SO_LINGER on TCP socket on %s"), 411 paddr(addr)); 412 close(sock); 413 return -1; 414 } 415 return sock; 416 } 417 418 static int 419 setup_tcp_listener_ports(struct socksetup *data) 420 { 421 struct sockaddr_in sin4; 422 #ifdef KRB5_USE_INET6 423 struct sockaddr_in6 sin6; 424 #endif 425 int i, port; 426 427 memset(&sin4, 0, sizeof(sin4)); 428 sin4.sin_family = AF_INET; 429 #ifdef HAVE_SA_LEN 430 sin4.sin_len = sizeof(sin4); 431 #endif 432 sin4.sin_addr.s_addr = INADDR_ANY; 433 434 #ifdef KRB5_USE_INET6 435 memset(&sin6, 0, sizeof(sin6)); 436 sin6.sin6_family = AF_INET6; 437 #ifdef SIN6_LEN 438 sin6.sin6_len = sizeof(sin6); 439 #endif 440 sin6.sin6_addr = in6addr_any; 441 #endif 442 443 FOREACH_ELT (tcp_port_data, i, port) { 444 int s4, s6; 445 446 set_sa_port((struct sockaddr *)&sin4, htons(port)); 447 if (!ipv6_enabled()) { 448 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4); 449 if (s4 < 0) 450 return -1; 451 s6 = -1; 452 } else { 453 #ifndef KRB5_USE_INET6 454 abort(); 455 #else 456 s4 = s6 = -1; 457 458 set_sa_port((struct sockaddr *)&sin6, htons(port)); 459 460 s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6); 461 if (s6 < 0) 462 return -1; 463 #ifdef IPV6_V6ONLY 464 if (setv6only(s6, 0)) 465 com_err(data->prog, errno, 466 gettext("setsockopt(IPV6_V6ONLY,0) failed")); 467 #endif 468 469 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4); 470 #endif /* KRB5_USE_INET6 */ 471 } 472 473 /* Sockets are created, prepare to listen on them. */ 474 if (s4 >= 0) { 475 FD_SET(s4, &sstate.rfds); 476 if (s4 >= sstate.max) 477 sstate.max = s4 + 1; 478 if (add_tcp_listener_fd(data, s4) == 0) 479 close(s4); 480 else 481 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s port %d", 482 s4, paddr((struct sockaddr *)&sin4), port); 483 } 484 #ifdef KRB5_USE_INET6 485 if (s6 >= 0) { 486 FD_SET(s6, &sstate.rfds); 487 if (s6 >= sstate.max) 488 sstate.max = s6 + 1; 489 if (add_tcp_listener_fd(data, s6) == 0) { 490 close(s6); 491 s6 = -1; 492 } else 493 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s port %d", 494 s6, paddr((struct sockaddr *)&sin6), port); 495 if (s4 < 0) 496 krb5_klog_syslog(LOG_INFO, 497 "assuming IPv6 socket accepts IPv4"); 498 } 499 #endif 500 } 501 return 0; 502 } 503 504 static int 505 setup_udp_port(void *P_data, struct sockaddr *addr) 506 { 507 struct socksetup *data = P_data; 508 int sock = -1, i; 509 char haddrbuf[NI_MAXHOST]; 510 int err; 511 u_short port; 512 513 err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf), 514 0, 0, NI_NUMERICHOST); 515 if (err) 516 strcpy(haddrbuf, "<unprintable>"); 517 518 switch (addr->sa_family) { 519 case AF_INET: 520 break; 521 #ifdef AF_INET6 522 case AF_INET6: 523 #ifdef KRB5_USE_INET6 524 break; 525 #else 526 { 527 static int first = 1; 528 if (first) { 529 krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses"); 530 first = 0; 531 } 532 return 0; 533 } 534 #endif 535 #endif 536 #ifdef AF_LINK /* some BSD systems, AIX */ 537 case AF_LINK: 538 return 0; 539 #endif 540 default: 541 krb5_klog_syslog (LOG_INFO, 542 "skipping unrecognized local address family %d", 543 addr->sa_family); 544 return 0; 545 } 546 547 FOREACH_ELT (udp_port_data, i, port) { 548 sock = socket (addr->sa_family, SOCK_DGRAM, 0); 549 if (sock == -1) { 550 data->retval = errno; 551 com_err(data->prog, data->retval, 552 gettext("Cannot create server socket for port %d address %s"), 553 port, haddrbuf); 554 return 1; 555 } 556 set_sa_port(addr, htons(port)); 557 if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) { 558 data->retval = errno; 559 com_err(data->prog, data->retval, 560 gettext("Cannot bind server socket to port %d address %s"), 561 port, haddrbuf); 562 return 1; 563 } 564 FD_SET (sock, &sstate.rfds); 565 if (sock >= sstate.max) 566 sstate.max = sock + 1; 567 krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s port %d", sock, 568 paddr((struct sockaddr *)addr), port); 569 if (add_udp_fd (data, sock) == 0) 570 return 1; 571 } 572 return 0; 573 } 574 575 #if 1 576 static void klog_handler(const void *data, size_t len) 577 { 578 static char buf[BUFSIZ]; 579 static int bufoffset; 580 void *p; 581 582 #define flush_buf() \ 583 (bufoffset \ 584 ? (((buf[0] == 0 || buf[0] == '\n') \ 585 ? (fork()==0?abort():(void)0) \ 586 : (void)0), \ 587 krb5_klog_syslog(LOG_INFO, "%s", buf), \ 588 memset(buf, 0, sizeof(buf)), \ 589 bufoffset = 0) \ 590 : 0) 591 592 p = memchr(data, 0, len); 593 if (p) 594 len = (const char *)p - (const char *)data; 595 scan_for_newlines: 596 if (len == 0) 597 return; 598 p = memchr(data, '\n', len); 599 if (p) { 600 if (p != data) 601 klog_handler(data, (size_t)((const char *)p - (const char *)data)); 602 flush_buf(); 603 len -= ((const char *)p - (const char *)data) + 1; 604 data = 1 + (const char *)p; 605 goto scan_for_newlines; 606 } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) { 607 size_t x = sizeof(buf) - len - 1; 608 klog_handler(data, x); 609 flush_buf(); 610 len -= x; 611 data = (const char *)data + x; 612 goto scan_for_newlines; 613 } else { 614 memcpy(buf + bufoffset, data, len); 615 bufoffset += len; 616 } 617 } 618 #endif 619 620 extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t); 621 622 krb5_error_code 623 setup_network(const char *prog) 624 { 625 struct socksetup setup_data; 626 krb5_error_code retval; 627 char *cp; 628 int i, port; 629 630 FD_ZERO(&sstate.rfds); 631 FD_ZERO(&sstate.wfds); 632 FD_ZERO(&sstate.xfds); 633 sstate.max = 0; 634 635 krb5int_sendtokdc_debug_handler = klog_handler; 636 637 /* Handle each realm's ports */ 638 for (i=0; i<kdc_numrealms; i++) { 639 cp = kdc_realmlist[i]->realm_ports; 640 while (cp && *cp) { 641 if (*cp == ',' || isspace((int) *cp)) { 642 cp++; 643 continue; 644 } 645 port = strtol(cp, &cp, 10); 646 if (cp == 0) 647 break; 648 retval = add_udp_port(port); 649 if (retval) 650 return retval; 651 } 652 653 cp = kdc_realmlist[i]->realm_tcp_ports; 654 while (cp && *cp) { 655 if (*cp == ',' || isspace((int) *cp)) { 656 cp++; 657 continue; 658 } 659 port = strtol(cp, &cp, 10); 660 if (cp == 0) 661 break; 662 retval = add_tcp_port(port); 663 if (retval) 664 return retval; 665 } 666 } 667 668 setup_data.prog = prog; 669 setup_data.retval = 0; 670 krb5_klog_syslog (LOG_INFO, "setting up network..."); 671 /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO, 672 so we might need only one UDP socket; fall back to binding 673 sockets on each address only if IPV6_PKTINFO isn't 674 supported. */ 675 if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) { 676 return setup_data.retval; 677 } 678 setup_tcp_listener_ports(&setup_data); 679 krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets); 680 if (n_sockets == 0) { 681 com_err(prog, 0, gettext("no sockets set up?")); 682 exit (1); 683 } 684 685 return 0; 686 } 687 688 static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa) 689 { 690 switch (sa->sa_family) { 691 case AF_INET: 692 faddr->address->addrtype = ADDRTYPE_INET; 693 faddr->address->length = IPV4_ADDR_LEN; 694 faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr; 695 faddr->port = ntohs(sa2sin(sa)->sin_port); 696 break; 697 #ifdef KRB5_USE_INET6 698 case AF_INET6: 699 if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) { 700 faddr->address->addrtype = ADDRTYPE_INET; 701 faddr->address->length = IPV4_ADDR_LEN; 702 /* offset to RAM address of ipv4 part of ipv6 address */ 703 faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) + 704 (krb5_octet *) &sa2sin6(sa)->sin6_addr; 705 } else { 706 faddr->address->addrtype = ADDRTYPE_INET6; 707 faddr->address->length = IPV6_ADDR_LEN; 708 faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr; 709 } 710 faddr->port = ntohs(sa2sin6(sa)->sin6_port); 711 break; 712 #endif 713 default: 714 faddr->address->addrtype = -1; 715 faddr->address->length = 0; 716 faddr->address->contents = 0; 717 faddr->port = 0; 718 break; 719 } 720 } 721 722 static void process_packet(struct connection *conn, const char *prog, 723 int selflags) 724 { 725 int cc; 726 socklen_t saddr_len; 727 krb5_fulladdr faddr; 728 krb5_error_code retval; 729 struct sockaddr_storage saddr; 730 krb5_address addr; 731 krb5_data request; 732 krb5_data *response; 733 char pktbuf[MAX_DGRAM_SIZE]; 734 int port_fd = conn->fd; 735 736 response = NULL; 737 saddr_len = sizeof(saddr); 738 cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0, 739 (struct sockaddr *)&saddr, &saddr_len); 740 if (cc == -1) { 741 if (errno != EINTR 742 /* This is how Linux indicates that a previous 743 transmission was refused, e.g., if the client timed out 744 before getting the response packet. */ 745 && errno != ECONNREFUSED 746 ) 747 com_err(prog, errno, gettext("while receiving from network")); 748 return; 749 } 750 if (!cc) 751 return; /* zero-length packet? */ 752 753 request.length = cc; 754 request.data = pktbuf; 755 faddr.address = &addr; 756 init_addr(&faddr, ss2sa(&saddr)); 757 /* this address is in net order */ 758 if ((retval = dispatch(&request, &faddr, conn->port, &response))) { 759 com_err(prog, retval, gettext("while dispatching (udp)")); 760 return; 761 } 762 cc = sendto(port_fd, response->data, (socklen_t) response->length, 0, 763 (struct sockaddr *)&saddr, saddr_len); 764 if (cc == -1) { 765 char addrbuf[46]; 766 krb5_free_data(kdc_context, response); 767 if (inet_ntop(((struct sockaddr *)&saddr)->sa_family, 768 addr.contents, addrbuf, sizeof(addrbuf)) == 0) { 769 strcpy(addrbuf, "?"); 770 } 771 com_err(prog, errno, gettext("while sending reply to %s/%d"), 772 addrbuf, faddr.port); 773 return; 774 } 775 if (cc != response->length) { 776 krb5_free_data(kdc_context, response); 777 com_err(prog, 0, gettext("short reply write %d vs %d\n"), 778 response->length, cc); 779 return; 780 } 781 krb5_free_data(kdc_context, response); 782 return; 783 } 784 785 static int tcp_data_counter; 786 /* Solaris kerberos: getting this value from elsewhere */ 787 extern int max_tcp_data_connections; 788 789 static void kill_tcp_connection(struct connection *); 790 791 static void accept_tcp_connection(struct connection *conn, const char *prog, 792 int selflags) 793 { 794 int s; 795 struct sockaddr_storage addr_s; 796 struct sockaddr *addr = (struct sockaddr *)&addr_s; 797 socklen_t addrlen = sizeof(addr_s); 798 struct socksetup sockdata; 799 struct connection *newconn; 800 char tmpbuf[10]; 801 802 s = accept(conn->fd, addr, &addrlen); 803 if (s < 0) 804 return; 805 setnbio(s), setnolinger(s); 806 807 sockdata.prog = prog; 808 sockdata.retval = 0; 809 810 newconn = add_tcp_data_fd(&sockdata, s); 811 if (newconn == 0) 812 return; 813 814 if (getnameinfo((struct sockaddr *)&addr_s, addrlen, 815 newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf), 816 tmpbuf, sizeof(tmpbuf), 817 NI_NUMERICHOST | NI_NUMERICSERV)) 818 strcpy(newconn->u.tcp.addrbuf, "???"); 819 else { 820 char *p, *end; 821 p = newconn->u.tcp.addrbuf; 822 end = p + sizeof(newconn->u.tcp.addrbuf); 823 p += strlen(p); 824 if (end - p > 2 + strlen(tmpbuf)) { 825 *p++ = '.'; 826 strcpy(p, tmpbuf); 827 } 828 } 829 830 newconn->u.tcp.addr_s = addr_s; 831 newconn->u.tcp.addrlen = addrlen; 832 newconn->u.tcp.bufsiz = 1024 * 1024; 833 newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz); 834 newconn->u.tcp.start_time = time(0); 835 836 if (++tcp_data_counter > max_tcp_data_connections) { 837 struct connection *oldest_tcp = NULL; 838 struct connection *c; 839 int i; 840 841 krb5_klog_syslog(LOG_INFO, "too many connections"); 842 843 FOREACH_ELT (connections, i, c) { 844 if (c->type != CONN_TCP) 845 continue; 846 if (c == newconn) 847 continue; 848 #if 0 849 krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd, 850 c->u.tcp.start_time); 851 #endif 852 if (oldest_tcp == NULL 853 || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time) 854 oldest_tcp = c; 855 } 856 if (oldest_tcp != NULL) { 857 krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s", 858 oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf); 859 kill_tcp_connection(oldest_tcp); 860 oldest_tcp = NULL; 861 } 862 } 863 if (newconn->u.tcp.buffer == 0) { 864 com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"), 865 newconn->u.tcp.addrbuf); 866 delete_fd(newconn); 867 close(s); 868 return; 869 } 870 newconn->u.tcp.offset = 0; 871 newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr; 872 init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s)); 873 SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4); 874 SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0); 875 876 FD_SET(s, &sstate.rfds); 877 if (sstate.max <= s) 878 sstate.max = s + 1; 879 } 880 881 static void 882 kill_tcp_connection(struct connection *conn) 883 { 884 if (conn->u.tcp.response) 885 krb5_free_data(kdc_context, conn->u.tcp.response); 886 if (conn->u.tcp.buffer) 887 free(conn->u.tcp.buffer); 888 FD_CLR(conn->fd, &sstate.rfds); 889 FD_CLR(conn->fd, &sstate.wfds); 890 if (sstate.max == conn->fd + 1) 891 while (sstate.max > 0 892 && ! FD_ISSET(sstate.max-1, &sstate.rfds) 893 && ! FD_ISSET(sstate.max-1, &sstate.wfds) 894 /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */ 895 ) 896 sstate.max--; 897 close(conn->fd); 898 conn->fd = -1; 899 tcp_data_counter--; 900 /* Solaris kerberos: fix memory leak */ 901 delete_fd(conn); 902 } 903 904 static void 905 process_tcp_connection(struct connection *conn, const char *prog, int selflags) 906 { 907 908 if (selflags & SSF_WRITE) { 909 ssize_t nwrote; 910 SOCKET_WRITEV_TEMP tmp; 911 krb5_error_code e; 912 913 nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum, 914 tmp); 915 if (nwrote < 0) { 916 e = SOCKET_ERRNO; 917 goto kill_tcp_connection; 918 } 919 if (nwrote == 0) 920 /* eof */ 921 goto kill_tcp_connection; 922 while (nwrote) { 923 sg_buf *sgp = conn->u.tcp.sgp; 924 if (nwrote < SG_LEN(sgp)) { 925 SG_ADVANCE(sgp, nwrote); 926 nwrote = 0; 927 } else { 928 nwrote -= SG_LEN(sgp); 929 conn->u.tcp.sgp++; 930 conn->u.tcp.sgnum--; 931 if (conn->u.tcp.sgnum == 0 && nwrote != 0) 932 abort(); 933 } 934 } 935 if (conn->u.tcp.sgnum == 0) { 936 /* finished sending */ 937 /* should go back to reading */ 938 goto kill_tcp_connection; 939 } 940 } else if (selflags & SSF_READ) { 941 /* Read message length and data into one big buffer, already 942 allocated at connect time. If we have a complete message, 943 we stop reading, so we should only be here if there is no 944 data in the buffer, or only an incomplete message. */ 945 size_t len; 946 ssize_t nread; 947 if (conn->u.tcp.offset < 4) { 948 /* msglen has not been computed */ 949 /* XXX Doing at least two reads here, letting the kernel 950 worry about buffering. It'll be faster when we add 951 code to manage the buffer here. */ 952 len = 4 - conn->u.tcp.offset; 953 nread = SOCKET_READ(conn->fd, 954 conn->u.tcp.buffer + conn->u.tcp.offset, len); 955 if (nread < 0) 956 /* error */ 957 goto kill_tcp_connection; 958 if (nread == 0) 959 /* eof */ 960 goto kill_tcp_connection; 961 conn->u.tcp.offset += nread; 962 if (conn->u.tcp.offset == 4) { 963 unsigned char *p = (unsigned char *)conn->u.tcp.buffer; 964 conn->u.tcp.msglen = ((p[0] << 24) 965 | (p[1] << 16) 966 | (p[2] << 8) 967 | p[3]); 968 if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) { 969 /* message too big */ 970 krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu", 971 conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen, 972 (unsigned long) conn->u.tcp.bufsiz - 4); 973 /* XXX Should return an error. */ 974 goto kill_tcp_connection; 975 } 976 } 977 } else { 978 /* msglen known */ 979 krb5_data request; 980 krb5_error_code err; 981 982 len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4); 983 nread = SOCKET_READ(conn->fd, 984 conn->u.tcp.buffer + conn->u.tcp.offset, len); 985 if (nread < 0) 986 /* error */ 987 goto kill_tcp_connection; 988 if (nread == 0) 989 /* eof */ 990 goto kill_tcp_connection; 991 conn->u.tcp.offset += nread; 992 if (conn->u.tcp.offset < conn->u.tcp.msglen + 4) 993 return; 994 995 /* have a complete message, and exactly one message */ 996 request.length = conn->u.tcp.msglen; 997 request.data = conn->u.tcp.buffer + 4; 998 err = dispatch(&request, &conn->u.tcp.faddr, conn->port, 999 &conn->u.tcp.response); 1000 if (err) { 1001 com_err(prog, err, gettext("while dispatching (tcp)")); 1002 goto kill_tcp_connection; 1003 } 1004 conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24); 1005 conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16); 1006 conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8); 1007 conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0); 1008 SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data, 1009 conn->u.tcp.response->length); 1010 conn->u.tcp.sgp = conn->u.tcp.sgbuf; 1011 conn->u.tcp.sgnum = 2; 1012 FD_CLR(conn->fd, &sstate.rfds); 1013 FD_SET(conn->fd, &sstate.wfds); 1014 } 1015 } else 1016 abort(); 1017 1018 return; 1019 1020 kill_tcp_connection: 1021 kill_tcp_connection(conn); 1022 } 1023 1024 static void service_conn(struct connection *conn, const char *prog, 1025 int selflags) 1026 { 1027 conn->service(conn, prog, selflags); 1028 } 1029 1030 krb5_error_code 1031 listen_and_process(const char *prog) 1032 { 1033 int nfound; 1034 struct select_state sout; 1035 int i, sret; 1036 krb5_error_code err; 1037 1038 if (conns == (struct connection **) NULL) 1039 return KDC5_NONET; 1040 1041 while (!signal_requests_exit) { 1042 if (signal_requests_hup) { 1043 krb5_klog_reopen(kdc_context); 1044 signal_requests_hup = 0; 1045 } 1046 sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0; 1047 err = krb5int_cm_call_select(&sstate, &sout, &sret); 1048 if (err) { 1049 com_err(prog, err, gettext("while selecting for network input(1)")); 1050 continue; 1051 } 1052 if (sret == -1) { 1053 if (errno != EINTR) 1054 com_err(prog, errno, gettext("while selecting for network input(2)")); 1055 continue; 1056 } 1057 nfound = sret; 1058 for (i=0; i<n_sockets && nfound > 0; i++) { 1059 int sflags = 0; 1060 if (conns[i]->fd < 0) 1061 abort(); 1062 if (FD_ISSET(conns[i]->fd, &sout.rfds)) 1063 sflags |= SSF_READ, nfound--; 1064 if (FD_ISSET(conns[i]->fd, &sout.wfds)) 1065 sflags |= SSF_WRITE, nfound--; 1066 if (sflags) 1067 service_conn(conns[i], prog, sflags); 1068 } 1069 } 1070 return 0; 1071 } 1072 1073 krb5_error_code 1074 closedown_network(const char *prog) 1075 { 1076 int i; 1077 struct connection *conn; 1078 1079 if (conns == (struct connection **) NULL) 1080 return KDC5_NONET; 1081 1082 FOREACH_ELT (connections, i, conn) { 1083 if (conn->fd >= 0) 1084 (void) close(conn->fd); 1085 DEL (connections, i); 1086 } 1087 FREE_SET_DATA(connections); 1088 FREE_SET_DATA(udp_port_data); 1089 FREE_SET_DATA(tcp_port_data); 1090 1091 return 0; 1092 } 1093 1094 #endif /* INET */ 1095