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