diff options
-rw-r--r-- | src/kdc/ChangeLog | 18 | ||||
-rw-r--r-- | src/kdc/configure.in | 1 | ||||
-rw-r--r-- | src/kdc/network.c | 223 |
3 files changed, 218 insertions, 24 deletions
diff --git a/src/kdc/ChangeLog b/src/kdc/ChangeLog index 11bee1b88a..30aea06b79 100644 --- a/src/kdc/ChangeLog +++ b/src/kdc/ChangeLog @@ -1,3 +1,21 @@ +2000-02-25 Ken Raeburn <raeburn@mit.edu> + Alec H. Peterson <ahp@hilander.com> + + * configure.in: Invoke KRB5_SOCKADDR_SA_LEN. + + * network.c: Include <sys/ioctl.h>, <syslog.h>, <net/if.h>. + (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 <raeburn@mit.edu> * 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 bf4d06edb7..76b3ab83e6 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 3ff47e0f0e..05ca79aa99 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 <sys/ioctl.h> +#include <syslog.h> #include <ctype.h> #ifdef HAVE_NETINET_IN_H @@ -43,11 +46,14 @@ #endif #include <arpa/inet.h> +#include <net/if.h> + 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<kdc_numrealms; i++) { @@ -118,24 +301,16 @@ const char *prog; } } - for (i=0; i<n_udp_ports; i++) { - if ((udp_port_fds[i] = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { - retval = errno; - com_err(prog, 0, "Cannot create server socket on port %d", - udp_port_nums[i]); - return(retval); - } - sin.sin_port = htons(udp_port_nums[i]); - if (bind(udp_port_fds[i], (struct sockaddr *) &sin, - sizeof(sin)) == -1) { - retval = errno; - com_err(prog, 0, "Cannot bind server socket on port %d", - udp_port_nums[i]); - return(retval); - } - FD_SET(udp_port_fds[i], &select_fds); - if (udp_port_fds[i]+1 > 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<n_udp_ports; i++) { + for (i=0; i<n_sockets; i++) { if (FD_ISSET(udp_port_fds[i], &readfds)) { process_packet(udp_port_fds[i], prog, udp_port_nums[i]); nfound--; |