From: Greg Hudson Date: Fri, 11 Jun 2010 21:03:03 +0000 (+0000) Subject: Use getaddrinfo() in kprop and kpropd, and recognize IPv6 addresses X-Git-Tag: krb5-1.9-beta1~192 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=c58f231b1a988eecc99e73f8394f298bf1d2a166;p=krb5.git 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 . ticket: 6686 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24134 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/slave/Makefile.in b/src/slave/Makefile.in index 20bef7cfb..66305622e 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 764b0f46b..22ac3a6a8 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 4ab53de74..573014bcc 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 000000000..54479c9a4 --- /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 +#include +#include + +#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 f669d8bbb..c931d43cd 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));