From d2e5db39ff65837c07cdbf5b2669455667678bfb Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Fri, 25 Feb 2000 20:46:35 +0000 Subject: [PATCH] Patches from Alec Peterson, plus some work of my own, to let a multihomed KDC respond to requests from the same IP address that the requests were sent to. **N.B. This will perform worse in the case of addresses dynamically added and removed after the KDC has started, since it will be incapable of using any new addresses. I'm unclear on why the loopback interface address needs to be included in the list of addresses. Apparently, on NetBSD-current, if it's not, packets sent to other local addresses but over the loopback interface are queued but not received?? Needs further investigation; could just be a NetBSD bug. * configure.in: Invoke KRB5_SOCKADDR_SA_LEN. * network.c: Include , , . (foreach_localaddr): New function, copied from lib/krb5/os/localaddr.c. Tweaked to not exclude loopback interface. (NEED_SOCKETS): Define before including k5-int.h. (n_sockets): New variable. (setup_port): New function; creates listening udp ports given an address. (setup_network): Call foreach_localaddr to set up listening sockets on each local address, so we can always respond from the receiving address. (listen_and_process): Use n_sockets as upper bound of loop. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12070 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kdc/ChangeLog | 18 ++++ src/kdc/configure.in | 1 + src/kdc/network.c | 223 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 218 insertions(+), 24 deletions(-) diff --git a/src/kdc/ChangeLog b/src/kdc/ChangeLog index 11bee1b88..30aea06b7 100644 --- a/src/kdc/ChangeLog +++ b/src/kdc/ChangeLog @@ -1,3 +1,21 @@ +2000-02-25 Ken Raeburn + Alec H. Peterson + + * configure.in: Invoke KRB5_SOCKADDR_SA_LEN. + + * network.c: Include , , . + (foreach_localaddr): New function, copied from + lib/krb5/os/localaddr.c. Tweaked to not exclude loopback + interface. + (NEED_SOCKETS): Define before including k5-int.h. + (n_sockets): New variable. + (setup_port): New function; creates listening udp ports given an + address. + (setup_network): Call foreach_localaddr to set up listening + sockets on each local address, so we can always respond from the + receiving address. + (listen_and_process): Use n_sockets as upper bound of loop. + 2000-02-24 Ken Raeburn * kerberos_v4.c (v4_klog): Don't treat the formatted text as a diff --git a/src/kdc/configure.in b/src/kdc/configure.in index bf4d06edb..76b3ab83e 100644 --- a/src/kdc/configure.in +++ b/src/kdc/configure.in @@ -5,6 +5,7 @@ AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK([tcsetattr],AC_DEFINE(POSIX_TERMIOS))) AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h) AC_CHECK_FUNCS(openlog syslog closelog strftime vsprintf) AC_PROG_AWK +KRB5_SOCKADDR_SA_LEN CHECK_SIGNALS HAS_ANSI_VOLATILE dnl diff --git a/src/kdc/network.c b/src/kdc/network.c index 3ff47e0f0..05ca79aa9 100644 --- a/src/kdc/network.c +++ b/src/kdc/network.c @@ -26,11 +26,14 @@ * Network code for Kerberos v5 KDC. */ +#define NEED_SOCKETS #include "k5-int.h" #include "com_err.h" #include "kdc_util.h" #include "extern.h" #include "kdc5_err.h" +#include +#include #include #ifdef HAVE_NETINET_IN_H @@ -43,11 +46,14 @@ #endif #include +#include + extern int errno; static int *udp_port_fds = (int *) NULL; static u_short *udp_port_nums = (u_short *) NULL; static int n_udp_ports = 0; +static int n_sockets = 0; static int max_udp_ports = 0; static fd_set select_fds; static int select_nfds; @@ -87,19 +93,196 @@ static krb5_error_code add_port(port) } #undef safe_realloc +/* Keep in sync with lib/krb5/os/localaddr.c version. */ + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq) + */ +#define USE_AF AF_INET +#define USE_TYPE SOCK_DGRAM +#define USE_PROTO 0 +#define SOCKET_ERRNO errno + +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + +static int +foreach_localaddr (data, pass1fn, betweenfn, pass2fn) + void *data; + int (*pass1fn) (void *, struct sockaddr *); + int (*betweenfn) (void *); + int (*pass2fn) (void *, struct sockaddr *); +{ + struct ifreq *ifr, ifreq; + struct ifconf ifc; + int s, code, n, i; + int est_if_count = 8, est_ifreq_size; + char *buf = 0; + size_t current_buf_size = 0; + + s = socket (USE_AF, USE_TYPE, USE_PROTO); + if (s < 0) + return SOCKET_ERRNO; + + /* At least on NetBSD, an ifreq can hold an IPv4 address, but + isn't big enough for an IPv6 or ethernet address. So add a + little more space. */ + est_ifreq_size = sizeof (struct ifreq) + 8; + current_buf_size = est_ifreq_size * est_if_count; + buf = malloc (current_buf_size); + + ask_again: + memset(buf, 0, current_buf_size); + ifc.ifc_len = current_buf_size; + ifc.ifc_buf = buf; + + code = ioctl (s, SIOCGIFCONF, (char *)&ifc); + if (code < 0) { + int retval = errno; + closesocket (s); + return retval; + } + /* Test that the buffer was big enough that another ifreq could've + fit easily, if the OS wanted to provide one. That seems to be + the only indication we get, complicated by the fact that the + associated address may make the required storage a little + bigger than the size of an ifreq. */ + if (current_buf_size - ifc.ifc_len < sizeof (struct ifreq) + 40) { + int new_size; + char *newbuf; + + est_if_count *= 2; + new_size = est_ifreq_size * est_if_count; + newbuf = realloc (buf, new_size); + if (newbuf == 0) { + krb5_error_code e = errno; + free (buf); + return e; + } + current_buf_size = new_size; + buf = newbuf; + goto ask_again; + } + + n = ifc.ifc_len; + + for (i = 0; i < n; i+= ifreq_size(*ifr) ) { + ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i); + + strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name)); + if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + skip: + /* mark for next pass */ + ifr->ifr_name[0] = 0; + + continue; + } +#if 0 /* Access from same host doesn't work if loopback is omitted? */ +#ifdef IFF_LOOPBACK + /* None of the current callers want loopback addresses. */ + if (ifreq.ifr_flags & IFF_LOOPBACK) + goto skip; +#endif +#endif + /* Ignore interfaces that are down. */ + if (!(ifreq.ifr_flags & IFF_UP)) + goto skip; + + if ((*pass1fn) (data, &ifr->ifr_addr)) { + abort (); + } + } + + if (betweenfn && (*betweenfn)(data)) { + abort (); + } + + if (pass2fn) + for (i = 0; i < n; i+= ifreq_size(*ifr) ) { + ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i); + + if (ifr->ifr_name[0] == 0) + /* Marked in first pass to be ignored. */ + continue; + + if ((*pass2fn) (data, &ifr->ifr_addr)) { + abort (); + } + } + closesocket(s); + free (buf); + + return 0; +} + +struct socksetup { + const char *prog; + krb5_error_code retval; +}; + +static int +setup_port(void *P_data, struct sockaddr *addr) +{ + struct socksetup *data = P_data; + int sock, i; + + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *) addr, psin; + for (i = 0; i < n_udp_ports; i++) { + sock = socket (PF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + data->retval = errno; + com_err(data->prog, data->retval, + "Cannot create server socket for port %d address %s", + udp_port_nums[i], inet_ntoa (sin->sin_addr)); + return 1; + } + psin = *sin; + psin.sin_port = htons (udp_port_nums[i]); + if (bind (sock, (struct sockaddr *)&psin, sizeof (psin)) == -1) { + data->retval = errno; + com_err(data->prog, data->retval, + "Cannot bind server socket to port %d address %s", + udp_port_nums[i], inet_ntoa (sin->sin_addr)); + return 1; + } + FD_SET (sock, &select_fds); + if (sock > select_nfds) + select_nfds = sock; + udp_port_fds[n_sockets++] = sock; + krb5_klog_syslog (LOG_INFO, "listening on fd %d: %s port %d", sock, + inet_ntoa (sin->sin_addr), udp_port_nums[i]); + } + } + default: + break; + } + return 0; +} + krb5_error_code setup_network(prog) -const char *prog; + const char *prog; { - struct sockaddr_in sin; + struct socksetup setup_data; krb5_error_code retval; - u_short port; char *cp; - int i; + int i, port; FD_ZERO(&select_fds); select_nfds = 0; - memset((char *)&sin, 0, sizeof(sin)); /* Handle each realm's ports */ for (i=0; i select_nfds) - select_nfds = udp_port_fds[i]+1; + setup_data.prog = prog; + setup_data.retval = 0; + krb5_klog_syslog (LOG_INFO, "setting up network..."); + if (foreach_localaddr (&setup_data, setup_port, 0, 0)) { + return setup_data.retval; + } + krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets); + if (n_sockets == 0) { + com_err(prog, 0, "no sockets set up?"); + return -1; } return 0; @@ -223,7 +398,7 @@ const char *prog; com_err(prog, errno, "while selecting for network input"); continue; } - for (i=0; i