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