summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-06-11 21:03:03 +0000
committerGreg Hudson <ghudson@mit.edu>2010-06-11 21:03:03 +0000
commitc58f231b1a988eecc99e73f8394f298bf1d2a166 (patch)
tree3b1c3ba0443a34dd5b33206ee504a9e5b13d27f2 /src
parentc7a6ad31cd2bfe9c6ac93ae498c6d345ae1db1ba (diff)
downloadkrb5-c58f231b1a988eecc99e73f8394f298bf1d2a166.tar.gz
krb5-c58f231b1a988eecc99e73f8394f298bf1d2a166.tar.xz
krb5-c58f231b1a988eecc99e73f8394f298bf1d2a166.zip
Use getaddrinfo() in kprop and kpropd, and recognize IPv6 addresses
when setting up krb5_address structures. kpropd still only binds to one socket to avoid the need for a select() loop, so we turn off IPV6_V6ONLY on that socket to ensure that IPv4 connections will still be accepted. Based on a patch from Michael Stapelberg <michael@stapelberg.de>. ticket: 6686 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24134 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/slave/Makefile.in8
-rw-r--r--src/slave/kprop.c143
-rw-r--r--src/slave/kprop.h3
-rw-r--r--src/slave/kprop_sock.c69
-rw-r--r--src/slave/kpropd.c130
5 files changed, 191 insertions, 162 deletions
diff --git a/src/slave/Makefile.in b/src/slave/Makefile.in
index 20bef7cfbe..66305622e6 100644
--- a/src/slave/Makefile.in
+++ b/src/slave/Makefile.in
@@ -6,11 +6,11 @@ DEFS=
all:: kprop kpropd kproplog
-CLIENTSRCS= $(srcdir)/kprop.c
-CLIENTOBJS= kprop.o
+CLIENTSRCS= $(srcdir)/kprop.c $(srcdir)/kprop_sock.c
+CLIENTOBJS= kprop.o kprop_sock.o
-SERVERSRCS= $(srcdir)/kpropd.c $(srcdir)/kpropd_rpc.c
-SERVEROBJS= kpropd.o kpropd_rpc.o
+SERVERSRCS= $(srcdir)/kpropd.c $(srcdir)/kpropd_rpc.c $(srcdir)/kprop_sock.c
+SERVEROBJS= kpropd.o kpropd_rpc.o kprop_sock.o
LOGSRCS= $(srcdir)/kproplog.c
LOGOBJS= kproplog.o
diff --git a/src/slave/kprop.c b/src/slave/kprop.c
index 764b0f46be..22ac3a6a84 100644
--- a/src/slave/kprop.c
+++ b/src/slave/kprop.c
@@ -59,20 +59,20 @@ char *srvtab = 0;
char *slave_host;
char *realm = 0;
char *file = KPROP_DEFAULT_FILE;
-short port = 0;
krb5_principal my_principal; /* The Kerberos principal we'll be */
/* running under, initialized in */
/* get_tickets() */
krb5_ccache ccache; /* Credentials cache which we'll be using */
krb5_creds creds;
-krb5_address sender_addr;
-krb5_address receiver_addr;
+krb5_address *sender_addr;
+krb5_address *receiver_addr;
+const char *port = KPROP_SERVICE;
void PRS(int, char **);
void get_tickets(krb5_context);
static void usage(void);
-krb5_error_code open_connection(char *, int *, char *, unsigned int);
+static void open_connection(krb5_context, char *, int *);
void kerberos_authenticate(krb5_context, krb5_auth_context *,
int, krb5_principal, krb5_creds **);
int open_database(krb5_context, char *, int *);
@@ -99,7 +99,6 @@ main(argc, argv)
krb5_context context;
krb5_creds *my_creds;
krb5_auth_context auth_context;
- char Errmsg[256];
retval = krb5_init_context(&context);
if (retval) {
@@ -110,17 +109,7 @@ main(argc, argv)
get_tickets(context);
database_fd = open_database(context, file, &database_size);
- retval = open_connection(slave_host, &fd, Errmsg, sizeof(Errmsg));
- if (retval) {
- com_err(progname, retval, "%s while opening connection to %s",
- Errmsg, slave_host);
- exit(1);
- }
- if (fd < 0) {
- fprintf(stderr, "%s: %s while opening connection to %s\n",
- progname, Errmsg, slave_host);
- exit(1);
- }
+ open_connection(context, slave_host, &fd);
kerberos_authenticate(context, &auth_context, fd, my_principal,
&my_creds);
xmit_database(context, auth_context, my_creds, fd, database_fd,
@@ -166,11 +155,8 @@ void PRS(argc, argv)
debug++;
break;
case 'P':
- if (*word)
- port = htons(atoi(word));
- else
- port = htons(atoi(*argv++));
- if (!port)
+ port = (*word != '\0') ? word : *argv++;
+ if (port == NULL)
usage();
word = 0;
break;
@@ -311,75 +297,72 @@ void get_tickets(context)
}
}
-krb5_error_code
-open_connection(host, fd, Errmsg, ErrmsgSz)
- char *host;
- int *fd;
- char *Errmsg;
- unsigned int ErrmsgSz;
+static void
+open_connection(krb5_context context, char *host, int *fd)
{
int s;
krb5_error_code retval;
-
- struct hostent *hp;
- register struct servent *sp;
- struct sockaddr_in my_sin;
GETSOCKNAME_ARG3_TYPE socket_length;
+ struct addrinfo hints, *res, *answers;
+ struct sockaddr *sa;
+ struct sockaddr_storage my_sin;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &answers);
+ if (error != 0) {
+ com_err(progname, 0, "%s: %s", host, gai_strerror(error));
+ exit(1);
+ }
- hp = gethostbyname(host);
- if (hp == NULL) {
- (void) snprintf(Errmsg, ErrmsgSz, "%s: unknown host", host);
- *fd = -1;
- return(0);
- }
- my_sin.sin_family = hp->h_addrtype;
- memcpy(&my_sin.sin_addr, hp->h_addr, sizeof(my_sin.sin_addr));
- if(!port) {
- sp = getservbyname(KPROP_SERVICE, "tcp");
- if (sp == 0) {
- my_sin.sin_port = htons(KPROP_PORT);
- } else {
- my_sin.sin_port = sp->s_port;
+ s = -1;
+ retval = EINVAL;
+ for (res = answers; res != NULL; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0) {
+ com_err(progname, errno, "while creating socket");
+ exit(1);
}
- } else
- my_sin.sin_port = port;
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0) {
- (void) snprintf(Errmsg, ErrmsgSz, "in call to socket");
- return(errno);
- }
- if (connect(s, (struct sockaddr *)&my_sin, sizeof my_sin) < 0) {
- retval = errno;
- close(s);
- (void) snprintf(Errmsg, ErrmsgSz, "in call to connect");
- return(retval);
+ if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
+ retval = errno;
+ close(s);
+ s = -1;
+ continue;
+ }
+
+ /* We successfully connect()ed */
+ *fd = s;
+ retval = sockaddr2krbaddr(context, res->ai_family, res->ai_addr,
+ &receiver_addr);
+ if (retval != 0) {
+ com_err(progname, retval, "while converting server address");
+ exit(1);
+ }
+
+ break;
}
- *fd = s;
- /*
- * Set receiver_addr and sender_addr.
- */
- receiver_addr.addrtype = ADDRTYPE_INET;
- receiver_addr.length = sizeof(my_sin.sin_addr);
- receiver_addr.contents = (krb5_octet *) malloc(sizeof(my_sin.sin_addr));
- memcpy(receiver_addr.contents, &my_sin.sin_addr,
- sizeof(my_sin.sin_addr));
+ freeaddrinfo(answers);
+ if (s == -1) {
+ com_err(progname, retval, "while connecting to server");
+ exit(1);
+ }
+
+ /* Set sender_addr. */
socket_length = sizeof(my_sin);
if (getsockname(s, (struct sockaddr *)&my_sin, &socket_length) < 0) {
- retval = errno;
- close(s);
- (void) snprintf(Errmsg, ErrmsgSz, "in call to getsockname");
- return(retval);
- }
- sender_addr.addrtype = ADDRTYPE_INET;
- sender_addr.length = sizeof(my_sin.sin_addr);
- sender_addr.contents = (krb5_octet *) malloc(sizeof(my_sin.sin_addr));
- memcpy(sender_addr.contents, &my_sin.sin_addr,
- sizeof(my_sin.sin_addr));
-
- return(0);
+ com_err(progname, errno, "while getting local socket address");
+ exit(1);
+ }
+ sa = (struct sockaddr *) &my_sin;
+ if (sockaddr2krbaddr(context, sa->sa_family, sa, &sender_addr) != 0) {
+ com_err(progname, errno, "while converting local address");
+ exit(1);
+ }
}
@@ -401,8 +384,8 @@ void kerberos_authenticate(context, auth_context, fd, me, new_creds)
krb5_auth_con_setflags(context, *auth_context,
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
- retval = krb5_auth_con_setaddrs(context, *auth_context, &sender_addr,
- &receiver_addr);
+ retval = krb5_auth_con_setaddrs(context, *auth_context, sender_addr,
+ receiver_addr);
if (retval) {
com_err(progname, retval, "in krb5_auth_con_setaddrs");
exit(1);
diff --git a/src/slave/kprop.h b/src/slave/kprop.h
index 4ab53de74f..573014bcc5 100644
--- a/src/slave/kprop.h
+++ b/src/slave/kprop.h
@@ -37,3 +37,6 @@
#define KPROP_BUFSIZ 32768
/* pathnames are in osconf.h, included via k5-int.h */
+
+int sockaddr2krbaddr(krb5_context context, int family, struct sockaddr *sa,
+ krb5_address **dest);
diff --git a/src/slave/kprop_sock.c b/src/slave/kprop_sock.c
new file mode 100644
index 0000000000..54479c9a4f
--- /dev/null
+++ b/src/slave/kprop_sock.c
@@ -0,0 +1,69 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * slave/kprop_sock.c
+ *
+ * Copyright (C) 2010 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * sockaddr2krbaddr() utility function used by kprop and kpropd.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "k5-int.h"
+#include "kprop.h"
+
+/*
+ * Convert an IPv4 or IPv6 socket address to a newly allocated krb5_address.
+ * There is similar code elsewhere in the tree, so this should possibly become
+ * a libkrb5 API in the future.
+ */
+krb5_error_code
+sockaddr2krbaddr(krb5_context context, int family, struct sockaddr *sa,
+ krb5_address **dest)
+{
+ krb5_address addr;
+
+ if (family == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
+ addr.addrtype = ADDRTYPE_INET;
+ addr.length = sizeof(sa4->sin_addr);
+ addr.contents = (krb5_octet *) &sa4->sin_addr;
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
+ if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+ addr.addrtype = ADDRTYPE_INET;
+ addr.contents = (krb5_octet *) &sa6->sin6_addr + 12;
+ addr.length = 4;
+ } else {
+ addr.addrtype = ADDRTYPE_INET6;
+ addr.length = sizeof(sa6->sin6_addr);
+ addr.contents = (krb5_octet *) &sa6->sin6_addr;
+ }
+ } else
+ return KRB5_PROG_ATYPE_NOSUPP;
+
+ return krb5_copy_addr(context, &addr, dest);
+}
diff --git a/src/slave/kpropd.c b/src/slave/kpropd.c
index f669d8bbbd..c931d43cd3 100644
--- a/src/slave/kpropd.c
+++ b/src/slave/kpropd.c
@@ -146,9 +146,9 @@ char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
char *kerb_database = NULL;
char *acl_file_name = KPROPD_ACL_FILE;
-krb5_address sender_addr;
-krb5_address receiver_addr;
-short port = 0;
+krb5_address *sender_addr;
+krb5_address *receiver_addr;
+const char *port = KPROP_SERVICE;
char **db_args = NULL;
int db_args_size = 0;
@@ -157,12 +157,8 @@ void PRS(char**);
int do_standalone(iprop_role iproprole);
void doit(int);
krb5_error_code do_iprop(kdb_log_context *log_ctx);
-void kerberos_authenticate(
- krb5_context,
- int,
- krb5_principal *,
- krb5_enctype *,
- struct sockaddr_in);
+void kerberos_authenticate(krb5_context, int, krb5_principal *,
+ krb5_enctype *, struct sockaddr_storage *);
krb5_boolean authorized_principal(krb5_context, krb5_principal, krb5_enctype);
void recv_database(krb5_context, int, int, krb5_data *);
void load_database(krb5_context, char *, char *);
@@ -241,11 +237,11 @@ static void resync_alarm(int sn)
int do_standalone(iprop_role iproprole)
{
- struct sockaddr_in my_sin, frominet;
- struct servent *sp;
+ struct sockaddr_in frominet;
+ struct addrinfo hints, *res;
int finet, s;
GETPEERNAME_ARG3_TYPE fromlen;
- int ret;
+ int ret, error, val;
/*
* Timer for accept/read calls, in case of network type errors.
*/
@@ -253,23 +249,34 @@ int do_standalone(iprop_role iproprole)
retry:
- finet = socket(AF_INET, SOCK_STREAM, 0);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ error = getaddrinfo(NULL, port, &hints, &res);
+ if (error != 0) {
+ (void) fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
+ exit(1);
+ }
+
+ finet = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (finet < 0) {
com_err(progname, errno, "while obtaining socket");
exit(1);
}
- memset(&my_sin,0, sizeof(my_sin));
- if(!port) {
- sp = getservbyname(KPROP_SERVICE, "tcp");
- if (sp == NULL) {
- com_err(progname, 0, "%s/tcp: unknown service", KPROP_SERVICE);
- my_sin.sin_port = htons(KPROP_PORT);
- }
- else my_sin.sin_port = sp->s_port;
- } else {
- my_sin.sin_port = port;
- }
- my_sin.sin_family = AF_INET;
+
+ val = 1;
+ if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
+ com_err(progname, errno, "while setting SO_REUSEADDR option");
+
+#ifdef IPV6_V6ONLY
+ /* Typically, res will be the IPv6 wildcard address. Some systems, such as
+ * the *BSDs, don't accept IPv4 connections on this address by default. */
+ val = 0;
+ if (setsockopt(finet, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) < 0)
+ com_err(progname, errno, "while unsetting IPV6_V6ONLY option");
+#endif
/*
* We need to close the socket immediately if iprop is enabled,
@@ -277,13 +284,8 @@ retry:
* linger around for too long
*/
if (iproprole == IPROP_SLAVE) {
- int on = 1;
struct linger linger;
- if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
- (char *)&on, sizeof(on)) < 0)
- com_err(progname, errno,
- _("while setting socket option (SO_REUSEADDR)"));
linger.l_onoff = 1;
linger.l_linger = 2;
if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
@@ -308,22 +310,9 @@ retry:
}
backoff_timer *= 2;
}
- if ((ret = bind(finet, (struct sockaddr *) &my_sin, sizeof(my_sin))) < 0) {
- if (debug) {
- int on = 1;
- fprintf(stderr,
- "%s: attempting to rebind socket with SO_REUSEADDR\n",
- progname);
- if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
- (char *)&on, sizeof(on)) < 0)
- com_err(progname, errno, "in setsockopt(SO_REUSEADDR)");
- ret = bind(finet, (struct sockaddr *) &my_sin, sizeof(my_sin));
- }
- if (ret < 0) {
- perror("bind");
- com_err(progname, errno, "while binding listener socket");
- exit(1);
- }
+ if ((ret = bind(finet, res->ai_addr, res->ai_addrlen)) < 0) {
+ com_err(progname, errno, "while binding listener socket");
+ exit(1);
}
if (!debug && iproprole != IPROP_SLAVE)
daemon(1, 0);
@@ -419,16 +408,16 @@ retry:
void doit(fd)
int fd;
{
- struct sockaddr_in from;
+ struct sockaddr_storage from;
int on = 1;
GETPEERNAME_ARG3_TYPE fromlen;
- struct hostent *hp;
krb5_error_code retval;
krb5_data confmsg;
int lock_fd;
mode_t omask;
krb5_enctype etype;
int database_fd;
+ char host[INET6_ADDRSTRLEN+1];
if (kpropd_context->kdblog_context &&
kpropd_context->kdblog_context->iproprole == IPROP_SLAVE) {
@@ -468,23 +457,17 @@ void doit(fd)
"while attempting setsockopt (SO_KEEPALIVE)");
}
- if (!(hp = gethostbyaddr((char *) &(from.sin_addr.s_addr), fromlen,
- AF_INET))) {
- syslog(LOG_INFO, "Connection from %s",
- inet_ntoa(from.sin_addr));
- if (debug)
- printf("Connection from %s\n",
- inet_ntoa(from.sin_addr));
- } else {
- syslog(LOG_INFO, "Connection from %s", hp->h_name);
+ if (getnameinfo((const struct sockaddr *) &from, fromlen,
+ host, sizeof(host), NULL, 0, 0) == 0) {
+ syslog(LOG_INFO, "Connection from %s", host);
if (debug)
- printf("Connection from %s\n", hp->h_name);
+ printf("Connection from %s\n", host);
}
/*
* Now do the authentication
*/
- kerberos_authenticate(kpropd_context, fd, &client, &etype, from);
+ kerberos_authenticate(kpropd_context, fd, &client, &etype, &from);
/*
* Turn off alarm upon successful authentication from master.
@@ -1070,11 +1053,8 @@ void PRS(argv)
word = 0;
break;
case 'P':
- if (*word)
- port = htons(atoi(word));
- else
- port = htons(atoi(*argv++));
- if (!port)
+ port = (*word != '\0') ? word : *argv++;
+ if (port == NULL)
usage();
word = 0;
break;
@@ -1216,22 +1196,19 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
int fd;
krb5_principal * clientp;
krb5_enctype * etype;
- struct sockaddr_in my_sin;
+ struct sockaddr_storage * my_sin;
{
krb5_error_code retval;
krb5_ticket * ticket;
- struct sockaddr_in r_sin;
+ struct sockaddr_storage r_sin;
GETSOCKNAME_ARG3_TYPE sin_length;
krb5_keytab keytab = NULL;
/*
* Set recv_addr and send_addr
*/
- sender_addr.addrtype = ADDRTYPE_INET;
- sender_addr.length = sizeof(my_sin.sin_addr);
- sender_addr.contents = (krb5_octet *) malloc(sizeof(my_sin.sin_addr));
- memcpy(sender_addr.contents, &my_sin.sin_addr,
- sizeof(my_sin.sin_addr));
+ sockaddr2krbaddr(context, my_sin->ss_family, (struct sockaddr *) my_sin,
+ &sender_addr);
sin_length = sizeof(r_sin);
if (getsockname(fd, (struct sockaddr *) &r_sin, &sin_length)) {
@@ -1239,11 +1216,8 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
exit(1);
}
- receiver_addr.addrtype = ADDRTYPE_INET;
- receiver_addr.length = sizeof(r_sin.sin_addr);
- receiver_addr.contents = (krb5_octet *) malloc(sizeof(r_sin.sin_addr));
- memcpy(receiver_addr.contents, &r_sin.sin_addr,
- sizeof(r_sin.sin_addr));
+ sockaddr2krbaddr(context, r_sin.ss_family, (struct sockaddr *) &r_sin,
+ &receiver_addr);
if (debug) {
char *name;
@@ -1272,8 +1246,8 @@ kerberos_authenticate(context, fd, clientp, etype, my_sin)
exit(1);
}
- retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr,
- &sender_addr);
+ retval = krb5_auth_con_setaddrs(context, auth_context, receiver_addr,
+ sender_addr);
if (retval) {
syslog(LOG_ERR, "Error in krb5_auth_con_setaddrs: %s",
error_message(retval));