From: Ken Raeburn Date: Thu, 26 Apr 2001 03:54:43 +0000 (+0000) Subject: localaddr.c (foreach_localaddr): support SIOCGIFNUM, for Solaris X-Git-Tag: krb5-1.3-alpha1~1550 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=3bbea9e561adf69da5de52caf9e8257b548258af;p=krb5.git localaddr.c (foreach_localaddr): support SIOCGIFNUM, for Solaris git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@13197 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index 5ae26110f..c0dffe749 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -2,6 +2,10 @@ * localaddr.c (socklen_t, socklen): Deleted; definitions now in k5-int.h instead. + (foreach_localaddr): Use SIOCGIFNUM if it's available and + SIOCGSIZIFCONF is not (e.g., Solaris 7 and 8). + (print_addr) [!HAVE_GETNAMEINFO]: Only declare variable buf if + AF_INET6 is defined. 2001-03-31 Ken Raeburn diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c index d0d0dc21d..d3c842a86 100644 --- a/src/lib/krb5/os/accessor.c +++ b/src/lib/krb5/os/accessor.c @@ -37,6 +37,7 @@ krb5int_accessor(internals, version) if (version == KRB5INT_ACCESS_VERSION) { krb5int_access internals_temp; + internals_temp.krb5_locate_server = krb5int_locate_server; internals_temp.krb5_locate_kdc = krb5_locate_kdc; internals_temp.krb5_max_skdc_timeout = krb5_max_skdc_timeout; internals_temp.krb5_skdc_timeout_shift = krb5_skdc_timeout_shift; diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c index 61cfea01f..1eefe6d98 100644 --- a/src/lib/krb5/os/changepw.c +++ b/src/lib/krb5/os/changepw.c @@ -8,7 +8,7 @@ * 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 @@ -22,7 +22,7 @@ * 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. - * + * */ #define NEED_SOCKETS @@ -49,6 +49,21 @@ #endif #endif /* _WIN32 && !__CYGWIN32__ */ +/* There's a lot of confusion between pointers to different sockaddr + types, and pointers with different degrees of indirection. Use + this function in this file to ensure we don't do something silly + like cast a "sockaddr **" to a "sockaddr_in *". */ +static struct sockaddr_in *sa2sin (struct sockaddr *sa) +{ + return (struct sockaddr_in *) sa; +} +#ifdef KRB5_USE_INET6xxNotUsed +static struct sockaddr_in6 *sa2sin6 (struct sockaddr *sa) +{ + return (struct sockaddr_in6 *) sa; +} +#endif + /* * Wrapper function for the two backends */ @@ -57,54 +72,30 @@ static krb5_error_code krb5_locate_kpasswd(context, realm, addr_pp, naddrs) krb5_context context; const krb5_data *realm; - struct sockaddr **addr_pp; + struct sockaddr ***addr_pp; int *naddrs; { krb5_error_code code; - int i; - /* - * We always try the local file first - */ - - code = krb5_locate_srv_conf(context, realm, "kpasswd_server", - addr_pp, naddrs, 0); + code = krb5int_locate_server (context, realm, addr_pp, naddrs, 0, + "kpasswd_server", "_kpasswd", 0, + DEFAULT_KPASSWD_PORT, 0); if (code) { - code = krb5_locate_srv_conf(context, realm, "admin_server", - addr_pp, naddrs, 0); - if ( !code ) { - /* success with admin_server but now we need to change the port */ - /* number to use DEFAULT_KPASSWD_PORT. */ - for ( i=0;i<*naddrs;i++ ) { - struct sockaddr_in *sockin = (struct sockaddr_in *) addr_pp[i]; - sockin->sin_port = htons(DEFAULT_KPASSWD_PORT); - } - } + code = krb5int_locate_server (context, realm, addr_pp, naddrs, 0, + "admin_server", "_kerberos-adm", 1, + DEFAULT_KPASSWD_PORT, 0); + if (!code) { + /* Success with admin_server but now we need to change the + port number to use DEFAULT_KPASSWD_PORT. */ + int i; + for ( i=0;i<*naddrs;i++ ) { + struct sockaddr *a = (*addr_pp)[i]; + if (a->sa_family == AF_INET) + sa2sin (a)->sin_port = htons(DEFAULT_KPASSWD_PORT); + } + } } -#ifdef KRB5_DNS_LOOKUP - if (code) { - int use_dns = _krb5_use_dns_kdc(context); - if ( use_dns ) { - code = krb5_locate_srv_dns(realm, "_kpasswd", "_udp", - addr_pp, naddrs); - if ( code ) { - code = krb5_locate_srv_dns(realm, - "_kerberos-adm", - "_tcp", - addr_pp, naddrs); - if ( !code ) { - /* success with admin_server but now we need to change the port */ - /* number to use DEFAULT_KPASSWD_PORT. */ - for ( i=0;i<*naddrs;i++ ) { - struct sockaddr_in *sockin = (struct sockaddr_in *) addr_pp[i]; - sockin->sin_port = htons(DEFAULT_KPASSWD_PORT); - } - } - } - } - } -#endif /* KRB5_DNS_LOOKUP */ return (code); } @@ -125,7 +116,7 @@ krb5_change_password(context, creds, newpw, result_code, char *code_string; krb5_error_code code = 0; int i, addrlen; - struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr; + struct sockaddr **addr_p, local_addr, remote_addr, tmp_addr; int naddr_p; int cc, local_result_code, tmp_len; SOCKET s1 = INVALID_SOCKET, s2 = INVALID_SOCKET; @@ -137,18 +128,18 @@ krb5_change_password(context, creds, newpw, result_code, memset(&chpw_req, 0, sizeof(krb5_data)); memset(&chpw_rep, 0, sizeof(krb5_data)); memset(&ap_req, 0, sizeof(krb5_data)); - + /* initialize auth_context so that we know we have to free it */ if ((code = krb5_auth_con_init(context, &auth_context))) goto cleanup; - - if ((code = krb5_mk_req_extended(context, &auth_context, + + if ((code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &ap_req))) goto cleanup; - if ((code = krb5_locate_kpasswd(context, - krb5_princ_realm(context, creds->client), + if ((code = krb5_locate_kpasswd(context, + krb5_princ_realm(context, creds->client), &addr_p, &naddr_p))) goto cleanup; @@ -156,7 +147,7 @@ krb5_change_password(context, creds, newpw, result_code, is left unconnected in case the server is multihomed and routes are asymmetric. s2 is connected to resolve routes and get addresses. this is the *only* way to get proper addresses for - multihomed hosts if routing is asymmetric. + multihomed hosts if routing is asymmetric. A related problem in the server, but not the client, is that many os's have no way to disconnect a connected udp socket, so @@ -168,212 +159,207 @@ krb5_change_password(context, creds, newpw, result_code, hostname resolution to get the local ip addr) will work and interoperate if the client is single-homed. */ - if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - { + if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { + code = SOCKET_ERRNO; + goto cleanup; + } + + if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { + code = SOCKET_ERRNO; + goto cleanup; + } + + for (i=0; isa_family != AF_INET) + continue; + + if (connect(s2, addr_p[i], sizeof(addr_p[i])) == SOCKET_ERROR) { + if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH) + continue; /* try the next addr */ + code = SOCKET_ERRNO; goto cleanup; - } + } + + addrlen = sizeof(local_addr); + + if (getsockname(s2, &local_addr, &addrlen) < 0) { + if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH) + continue; /* try the next addr */ - if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - { code = SOCKET_ERRNO; goto cleanup; - } - - for (i=0; isin_addr.s_addr != 0) - { - local_kaddr.addrtype = ADDRTYPE_INET; - local_kaddr.length = sizeof(((struct sockaddr_in *) &local_addr)->sin_addr); - local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *) &local_addr)->sin_addr); - } - else - { - krb5_address **addrs; - - krb5_os_localaddr(context, &addrs); - - local_kaddr.magic = addrs[0]->magic; - local_kaddr.addrtype = addrs[0]->addrtype; - local_kaddr.length = addrs[0]->length; - local_kaddr.contents = malloc(addrs[0]->length); - memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); - - krb5_free_addresses(context, addrs); - } - - addrlen = sizeof(remote_addr); - if (getpeername(s2, &remote_addr, &addrlen) < 0) - { - if ((SOCKET_ERRNO == ECONNREFUSED) || (SOCKET_ERRNO == EHOSTUNREACH)) - continue; /* try the next addr */ - - code = SOCKET_ERRNO; - goto cleanup; - } - - remote_kaddr.addrtype = ADDRTYPE_INET; - remote_kaddr.length = sizeof(((struct sockaddr_in *) &remote_addr)->sin_addr); - remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *) &remote_addr)->sin_addr); - - /* mk_priv requires that the local address be set. - getsockname is used for this. rd_priv requires that the - remote address be set. recvfrom is used for this. If - rd_priv is given a local address, and the message has the - recipient addr in it, this will be checked. However, there - is simply no way to know ahead of time what address the - message will be delivered *to*. Therefore, it is important - that either no recipient address is in the messages when - mk_priv is called, or that no local address is passed to - rd_priv. Both is a better idea, and I have done that. In - summary, when mk_priv is called, *only* a local address is - specified. when rd_priv is called, *only* a remote address - is specified. Are we having fun yet? */ - - if ((code = krb5_auth_con_setaddrs(context, auth_context, - &local_kaddr, NULL))) - { - code = SOCKET_ERRNO; - goto cleanup; - } - - if ((code = krb5_mk_chpw_req(context, auth_context, &ap_req, - newpw, &chpw_req))) - { - code = SOCKET_ERRNO; - goto cleanup; - } - - if ((cc = sendto(s1, chpw_req.data, (int) chpw_req.length, 0, - (struct sockaddr *) &addr_p[i], - sizeof(addr_p[i]))) != chpw_req.length) - { - if ((cc < 0) && ((SOCKET_ERRNO == ECONNREFUSED) || - (SOCKET_ERRNO == EHOSTUNREACH))) - continue; /* try the next addr */ - - code = (cc < 0) ? SOCKET_ERRNO : ECONNABORTED; - goto cleanup; - } - - chpw_rep.length = 1500; - chpw_rep.data = (char *) malloc(chpw_rep.length); - - /* XXX need a timeout/retry loop here */ - FD_ZERO (&fdset); - FD_SET (s1, &fdset); - timeout.tv_sec = 120; - timeout.tv_usec = 0; - switch (select (s1 + 1, &fdset, 0, 0, &timeout)) { - case -1: - code = SOCKET_ERRNO; - goto cleanup; - case 0: - code = ETIMEDOUT; - goto cleanup; - default: - /* fall through */ - ; - } - - /* "recv" would be good enough here... except that Windows/NT - commits the atrocity of returning -1 to indicate failure, - but leaving errno set to 0. - - "recvfrom(...,NULL,NULL)" would seem to be a good enough - alternative, and it works on NT, but it doesn't work on - SunOS 4.1.4 or Irix 5.3. Thus we must actually accept the - value and discard it. */ - tmp_len = sizeof(tmp_addr); - if ((cc = recvfrom(s1, chpw_rep.data, (int) chpw_rep.length, - 0, &tmp_addr, &tmp_len)) < 0) - { - code = SOCKET_ERRNO; - goto cleanup; - } - - closesocket(s1); - s1 = INVALID_SOCKET; - closesocket(s2); - s2 = INVALID_SOCKET; - - chpw_rep.length = cc; - - if ((code = krb5_auth_con_setaddrs(context, auth_context, - NULL, &remote_kaddr))) - goto cleanup; - - if ((code = krb5_rd_chpw_rep(context, auth_context, &chpw_rep, - &local_result_code, - result_string))) - goto cleanup; - - if (result_code) - *result_code = local_result_code; - - if (result_code_string) - { - if ((code = krb5_chpw_result_code_string(context, - local_result_code, - &code_string))) - goto cleanup; - - result_code_string->length = strlen(code_string); - if ((result_code_string->data = - (char *) malloc(result_code_string->length)) == NULL) - return(ENOMEM); - strncpy(result_code_string->data, code_string, result_code_string->length); - } - - code = 0; + /* some brain-dead OS's don't return useful information from + * the getsockname call. Namely, windows and solaris. */ + + if (sa2sin(&local_addr)->sin_addr.s_addr != 0) { + local_kaddr.addrtype = ADDRTYPE_INET; + local_kaddr.length = sizeof(sa2sin(&local_addr)->sin_addr); + local_kaddr.contents = (krb5_octet *) &sa2sin(&local_addr)->sin_addr; + } else { + krb5_address **addrs; + + krb5_os_localaddr(context, &addrs); + + local_kaddr.magic = addrs[0]->magic; + local_kaddr.addrtype = addrs[0]->addrtype; + local_kaddr.length = addrs[0]->length; + local_kaddr.contents = malloc(addrs[0]->length); + memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); + + krb5_free_addresses(context, addrs); + } + + addrlen = sizeof(remote_addr); + if (getpeername(s2, &remote_addr, &addrlen) < 0) { + if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH) + continue; /* try the next addr */ + + code = SOCKET_ERRNO; + goto cleanup; + } + + remote_kaddr.addrtype = ADDRTYPE_INET; + remote_kaddr.length = sizeof(sa2sin(&remote_addr)->sin_addr); + remote_kaddr.contents = (krb5_octet *) &sa2sin(&remote_addr)->sin_addr; + + /* mk_priv requires that the local address be set. + getsockname is used for this. rd_priv requires that the + remote address be set. recvfrom is used for this. If + rd_priv is given a local address, and the message has the + recipient addr in it, this will be checked. However, there + is simply no way to know ahead of time what address the + message will be delivered *to*. Therefore, it is important + that either no recipient address is in the messages when + mk_priv is called, or that no local address is passed to + rd_priv. Both is a better idea, and I have done that. In + summary, when mk_priv is called, *only* a local address is + specified. when rd_priv is called, *only* a remote address + is specified. Are we having fun yet? */ + + if ((code = krb5_auth_con_setaddrs(context, auth_context, + &local_kaddr, NULL))) { + code = SOCKET_ERRNO; + goto cleanup; + } + + if ((code = krb5_mk_chpw_req(context, auth_context, &ap_req, + newpw, &chpw_req))) + { + code = SOCKET_ERRNO; + goto cleanup; + } + + if ((cc = sendto(s1, chpw_req.data, (int) chpw_req.length, 0, + addr_p[i], sizeof(addr_p[i]))) != chpw_req.length) + { + if ((cc < 0) && ((SOCKET_ERRNO == ECONNREFUSED) || + (SOCKET_ERRNO == EHOSTUNREACH))) + continue; /* try the next addr */ + + code = (cc < 0) ? SOCKET_ERRNO : ECONNABORTED; + goto cleanup; + } + + chpw_rep.length = 1500; + chpw_rep.data = (char *) malloc(chpw_rep.length); + + /* XXX need a timeout/retry loop here */ + FD_ZERO (&fdset); + FD_SET (s1, &fdset); + timeout.tv_sec = 120; + timeout.tv_usec = 0; + switch (select (s1 + 1, &fdset, 0, 0, &timeout)) { + case -1: + code = SOCKET_ERRNO; + goto cleanup; + case 0: + code = ETIMEDOUT; + goto cleanup; + default: + /* fall through */ + ; + } + + /* "recv" would be good enough here... except that Windows/NT + commits the atrocity of returning -1 to indicate failure, + but leaving errno set to 0. + + "recvfrom(...,NULL,NULL)" would seem to be a good enough + alternative, and it works on NT, but it doesn't work on + SunOS 4.1.4 or Irix 5.3. Thus we must actually accept the + value and discard it. */ + tmp_len = sizeof(tmp_addr); + if ((cc = recvfrom(s1, chpw_rep.data, (int) chpw_rep.length, + 0, &tmp_addr, &tmp_len)) < 0) + { + code = SOCKET_ERRNO; + goto cleanup; + } + + closesocket(s1); + s1 = INVALID_SOCKET; + closesocket(s2); + s2 = INVALID_SOCKET; + + chpw_rep.length = cc; + + if ((code = krb5_auth_con_setaddrs(context, auth_context, + NULL, &remote_kaddr))) + goto cleanup; + + if ((code = krb5_rd_chpw_rep(context, auth_context, &chpw_rep, + &local_result_code, + result_string))) + goto cleanup; + + if (result_code) + *result_code = local_result_code; + + if (result_code_string) { + if ((code = krb5_chpw_result_code_string(context, + local_result_code, + &code_string))) goto cleanup; - } + + result_code_string->length = strlen(code_string); + if ((result_code_string->data = + (char *) malloc(result_code_string->length)) == NULL) + return(ENOMEM); + strncpy(result_code_string->data, code_string, result_code_string->length); + } + + code = 0; + goto cleanup; + } code = SOCKET_ERRNO; - + cleanup: - if(auth_context != NULL) - krb5_auth_con_free(context, auth_context); - - if(addr_p != NULL) - krb5_xfree(addr_p); - - if(s1 != INVALID_SOCKET) - closesocket(s1); - - if(s2 != INVALID_SOCKET) - closesocket(s2); - + if (auth_context != NULL) + krb5_auth_con_free(context, auth_context); + + if (addr_p != NULL) { + for (i = 0; i < naddr_p; i++) + krb5_xfree (addr_p[i]); + krb5_xfree(addr_p); + } + + if (s1 != INVALID_SOCKET) + closesocket(s1); + + if (s2 != INVALID_SOCKET) + closesocket(s2); + krb5_free_data_contents(context, &chpw_req); krb5_free_data_contents(context, &chpw_rep); - krb5_free_data_contents(context, &ap_req); - + krb5_free_data_contents(context, &ap_req); + return(code); } diff --git a/src/lib/krb5/os/localaddr.c b/src/lib/krb5/os/localaddr.c index 1fc558d58..3eeb52e30 100644 --- a/src/lib/krb5/os/localaddr.c +++ b/src/lib/krb5/os/localaddr.c @@ -300,6 +300,9 @@ foreach_localaddr (/*@null@*/ void *data, int ifconfsize = -1; #endif int retval = 0; +#ifdef SIOCGIFNUM + int numifs = -1; +#endif s = socket (USE_AF, USE_TYPE, USE_PROTO); if (s < 0) @@ -315,6 +318,10 @@ foreach_localaddr (/*@null@*/ void *data, current_buf_size = ifconfsize; est_if_count = ifconfsize / est_ifreq_size; } +#elif defined (SIOCGIFNUM) + code = ioctl (s, SIOCGIFNUM, &numifs); + if (!code && numifs > 0) + est_if_count = numifs; #endif if (current_buf_size == 0) current_buf_size = est_ifreq_size * est_if_count; @@ -341,6 +348,8 @@ foreach_localaddr (/*@null@*/ void *data, if (current_buf_size - size < sizeof (struct ifreq) + 40 #ifdef SIOCGSIZIFCONF && ifconfsize <= 0 +#elif defined (SIOCGIFNUM) + && numifs <= 0 #endif ) { size_t new_size; @@ -468,7 +477,9 @@ static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa) printf ("addr %s\n", hostbuf); return 0; #else +#ifdef AF_INET6 char buf[50]; +#endif printf ("family %2d ", sa->sa_family); switch (sa->sa_family) { case AF_INET: diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c index 48a014770..808f868fc 100644 --- a/src/lib/krb5/os/locate_kdc.c +++ b/src/lib/krb5/os/locate_kdc.c @@ -1,7 +1,7 @@ /* * lib/krb5/os/locate_kdc.c * - * Copyright 1990 by the Massachusetts Institute of Technology. + * Copyright 1990,2000,2001 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -50,9 +50,6 @@ #endif #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) -#ifndef KPASSWD_PORTNAME -#define KPASSWD_PORTNAME "kpasswd" -#endif #if KRB5_DNS_LOOKUP_KDC #define DEFAULT_LOOKUP_KDC 1 @@ -107,32 +104,286 @@ _krb5_use_dns_realm(context) #endif /* KRB5_DNS_LOOKUP */ +static int get_port (const char *service, int stream, int defalt) +{ +#ifdef HAVE_GETADDRINFO + struct addrinfo hints = { 0 }; + struct addrinfo *ai; + int err; + + hints.ai_family = PF_INET; + hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM; + err = getaddrinfo (NULL, service, &hints, &ai); + if (err == 0 && ai != 0) { + if (ai->ai_addr->sa_family == AF_INET) { + int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port; + freeaddrinfo (ai); + return port; + } + freeaddrinfo (ai); + } + /* Any error - don't complain, just use default. */ + return htons (defalt); +#else + struct servent *sp; + sp = getservbyname (service, stream ? "tcp" : "udp"); /* NOT THREAD SAFE */ + if (sp) + return sp->s_port; + return htons (defalt); +#endif +} + +struct addrlist { + struct sockaddr **addrs; + int naddrs; + int space; +}; +#define ADDRLIST_INIT { 0, 0, 0 } + +static int +grow_list (struct addrlist *lp, int nmore) +{ + int i; + int newspace = lp->space + nmore; + size_t newsize = newspace * sizeof (struct addrlist); + struct sockaddr **newaddrs; + + newaddrs = realloc (lp->addrs, newsize); + + if (newaddrs == NULL) + return errno; + for (i = lp->space; i < newspace; i++) + newaddrs[i] = NULL; + lp->addrs = newaddrs; + lp->space = newspace; + return 0; +} + +static void +free_list (struct addrlist *lp) +{ + int i; + for (i = 0; i < lp->naddrs; i++) + free (lp->addrs[i]); + free (lp->addrs); + lp->addrs = NULL; + lp->naddrs = lp->space = 0; +} + +static int +add_sockaddr_to_list (struct addrlist *lp, const struct sockaddr *addr, + size_t len) +{ + struct sockaddr *copy; + +#ifdef TEST + fprintf (stderr, "\tadding sockaddr family %2d", addr->sa_family); + if (addr->sa_family == AF_INET) + fprintf (stderr, "\t%s", + inet_ntoa (((const struct sockaddr_in *)addr)->sin_addr)); + fprintf (stderr, "\n"); +#endif + + if (lp->naddrs == lp->space) { + int err = grow_list (lp, 1); + if (err) + return err; + } + copy = malloc (len); + if (copy == NULL) + return errno; + memcpy (copy, addr, len); + lp->addrs[lp->naddrs++] = copy; + return 0; +} + +#ifdef HAVE_GETADDRINFO +static int translate_ai_error (int err) +{ + switch (err) { + case 0: + return 0; + case EAI_ADDRFAMILY: + case EAI_BADFLAGS: + case EAI_FAMILY: + case EAI_SOCKTYPE: + case EAI_SERVICE: + /* All of these indicate bad inputs to getaddrinfo. */ + return EINVAL; + case EAI_AGAIN: + /* Translate to standard errno code. */ + return EAGAIN; + case EAI_MEMORY: + /* Translate to standard errno code. */ + return ENOMEM; + case EAI_NODATA: + case EAI_NONAME: + /* Name not known or no address data, but no error. Do + nothing more. */ + return 0; + case EAI_SYSTEM: + /* System error, duh. */ + return errno; + default: + /* An error code we haven't handled? */ + return EINVAL; + } +} + +static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a) +{ + return add_sockaddr_to_list (lp, a->ai_addr, a->ai_addrlen); +} + +static void set_port_num (struct sockaddr *addr, int num) +{ + switch (addr->sa_family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = num; + break; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = num; + break; + } +} +#endif + +/* There's also gethostbyname_r, which is IPv4-only but thread-safe; + it wants the caller to supply scratch space and a hostent + structure. Let's go for IPv6 support before thread safety, though; + it's easier to accomplish, and will probably be finished first + anyways. */ +#if !defined (HAVE_GETADDRINFO) && !defined (HAVE_GETIPNODEBYNAME) +#undef getipnodebyname +#define freehostent(X) ((void)(X)) +#ifdef HAVE_GETHOSTBYNAME2 +#define getipnodebyname(NAME,AF,FLAGS,ERRP) (gethostbyname2(NAME,AF)) +#else +#define getipnodebyname(NAME,AF,FLAGS,ERRP) ((AF)==AF_INET?gethostbyname(NAME):(struct hostent *)0) +#endif +#endif + +static int +add_host_to_list (struct addrlist *lp, const char *hostname, + int port, int secport) +{ +#ifdef HAVE_GETADDRINFO + int err; + struct addrinfo *addrs, *a; + +#ifdef TEST + fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname, + ntohs (port), ntohs (secport)); +#endif + + err = getaddrinfo (hostname, NULL, NULL, &addrs); + if (err) + return translate_ai_error (err); + for (a = addrs; a; a = a->ai_next) { + set_port_num (a->ai_addr, port); + err = add_addrinfo_to_list (lp, a); + if (err) + break; + + if (secport == 0) + continue; + + set_port_num (a->ai_addr, secport); + err = add_addrinfo_to_list (lp, a); + if (err) + break; + } + freeaddrinfo (addrs); + return err; +#else + int err; + struct hostent *hp; + int i; + +#ifdef TEST + fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname, port, secport); +#endif + + hp = getipnodebyname (hostname, AF_INET, 0, &err); + if (hp != NULL) { + for (i = 0; hp->h_addr_list[i] != 0; i++) { + struct sockaddr_in sin4; + + memset (&sin4, 0, sizeof (sin4)); + memcpy (&sin4.sin_addr, hp->h_addr_list[i], + sizeof (sin4.sin_addr)); + sin4.sin_family = AF_INET; + sin4.sin_port = port; + err = add_sockaddr_to_list (lp, (struct sockaddr *) &sin4, + sizeof (sin4)); + if (err) + break; + if (secport != 0) { + sin4.sin_port = secport; + err = add_sockaddr_to_list (lp, (struct sockaddr *) &sin4, + sizeof (sin4)); + } + + if (err) + break; + } + freehostent (hp); + } +#if defined (KRB5_USE_INET6) + hp = getipnodebyname (hostname, AF_INET6, 0, &err); /* NOT THREAD SAFE */ + if (hp != NULL) { + for (i = 0; hp->h_addr_list[i] != 0; i++) { + struct sockaddr_in6 sin6; + + memset (&sin6, 0, sizeof (sin6)); + memcpy (&sin6.sin6_addr, hp->h_addr_list[i], + sizeof (sin6.sin6_addr)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = port; + err = add_sockaddr_to_list (lp, (struct sockaddr *) &sin6, + sizeof (sin6)); + if (err) + break; + if (secport != 0) { + sin6.sin6_port = secport; + err = add_sockaddr_to_list (lp, (struct sockaddr *) &sin6, + sizeof (sin6)); + if (err) + break; + } + } + freehostent (hp); + } +#endif + return 0; +#endif +} + /* * returns count of number of addresses found * if master is non-NULL, it is filled in with the index of * the master kdc */ -krb5_error_code -krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) +static krb5_error_code +krb5_locate_srv_conf_1(context, realm, name, addrlist, get_masters, + udpport, sec_udpport) krb5_context context; const krb5_data *realm; const char * name; - struct sockaddr **addr_pp; - int *naddrs; + struct addrlist *addrlist; int get_masters; + int udpport, sec_udpport; { const char *realm_srv_names[4]; char **masterlist, **hostlist, *host, *port, *cp; krb5_error_code code; - int i, j, out, count, ismaster; - struct sockaddr *addr_p; - struct sockaddr_in *sin_p; - struct hostent *hp; - struct servent *sp; -#ifdef HAVE_NETINET_IN_H - u_short udpport; - u_short sec_udpport; + int i, j, count, ismaster; + +#ifdef TEST + fprintf (stderr, + "looking in krb5.conf for realm %s entry %s; ports %d,%d\n", + realm->data, name, ntohs (udpport), ntohs (sec_udpport)); #endif if ((host = malloc(realm->length + 1)) == NULL) @@ -158,27 +409,6 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) return code; } -#ifdef HAVE_NETINET_IN_H - if ( !strcmp(name,"kpasswd_server") ) { - if ((sp = getservbyname(KPASSWD_PORTNAME, "udp"))) - udpport = sp->s_port; - else - udpport = htons(DEFAULT_KPASSWD_PORT); - sec_udpport = 0; - } else { - if ((sp = getservbyname(KDC_PORTNAME, "udp"))) - udpport = sp->s_port; - else - udpport = htons(KRB5_DEFAULT_PORT); - if ((sp = getservbyname(KDC_SECONDARY_PORTNAME, "udp"))) - sec_udpport = sp->s_port; - else - sec_udpport = htons(KRB5_DEFAULT_SEC_PORT); - } -#endif - if (sec_udpport == udpport) - sec_udpport = 0; - count = 0; while (hostlist && hostlist[count]) count++; @@ -186,7 +416,7 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) if (count == 0) { profile_free_list(hostlist); krb5_xfree(host); - *naddrs = 0; + addrlist->naddrs = 0; return 0; } @@ -232,16 +462,9 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) count = count * 2; #endif - addr_p = (struct sockaddr *)malloc (sizeof (struct sockaddr) * count); - if (addr_p == NULL) { - if (hostlist) - profile_free_list(hostlist); - if (masterlist) - profile_free_list(masterlist); - return ENOMEM; - } + for (i=0; hostlist[i]; i++) { + int p1, p2; - for (i=0, out=0; hostlist[i]; i++) { host = hostlist[i]; /* * Strip off excess whitespace @@ -258,10 +481,6 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) port++; } - if ((hp = gethostbyname(hostlist[i])) == 0) { - continue; - } - ismaster = 0; if (masterlist) { for (j=0; masterlist[j]; j++) { @@ -271,44 +490,37 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) } } - if ( !get_masters || ismaster ) { - switch (hp->h_addrtype) { + if (get_masters && !ismaster) + continue; -#ifdef HAVE_NETINET_IN_H - case AF_INET: - for (j=0; hp->h_addr_list[j]; j++) { - sin_p = (struct sockaddr_in *) &addr_p[out++]; - memset ((char *)sin_p, 0, sizeof(struct sockaddr)); - sin_p->sin_family = hp->h_addrtype; - sin_p->sin_port = port ? htons(atoi(port)) : udpport; - memcpy((char *)&sin_p->sin_addr, - (char *)hp->h_addr_list[j], - sizeof(struct in_addr)); - if (out+1 >= count) { - count += 5; - addr_p = (struct sockaddr *) - realloc ((char *)addr_p, - sizeof(struct sockaddr) * count); - if (addr_p == NULL) { - if ( hostlist ) - profile_free_list(hostlist); - if ( masterlist ) - profile_free_list(masterlist); - return ENOMEM; - } - } - if (sec_udpport && !port) { - addr_p[out] = addr_p[out-1]; - sin_p = (struct sockaddr_in *) &addr_p[out++]; - sin_p->sin_port = sec_udpport; - } - } - break; + if (port) { + unsigned long l; +#ifdef HAVE_STROUL + char *endptr; + l = strtoul (port, &endptr, 10); + if (endptr == NULL || *endptr != 0) + return EINVAL; +#else + l = atoi (port); #endif - default: - break; - } - } + /* L is unsigned, don't need to check <0. */ + if (l > 65535) + return EINVAL; + p1 = htons (l); + p2 = 0; + } else { + p1 = udpport; + p2 = sec_udpport; + } + + code = add_host_to_list (addrlist, hostlist[i], p1, p2); + if (code) { + if (hostlist) + profile_free_list (hostlist); + if (masterlist) + profile_free_list (masterlist); + return code; + } } if (hostlist) @@ -316,40 +528,56 @@ krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters) if (masterlist) profile_free_list(masterlist); - if (out == 0) { /* Couldn't resolve any KDC names */ - free (addr_p); - return KRB5_REALM_CANT_RESOLVE; - } + return 0; +} - *addr_pp = addr_p; - *naddrs = out; +static krb5_error_code +krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters, + udpport, sec_udpport) + krb5_context context; + const krb5_data *realm; + const char * name; + struct sockaddr ***addr_pp; + int *naddrs; + int get_masters; + int udpport, sec_udpport; +{ + krb5_error_code ret; + struct addrlist al = ADDRLIST_INIT; + + ret = krb5_locate_srv_conf_1 (context, realm, name, &al, + get_masters, udpport, sec_udpport); + if (ret) { + free_list (&al); + return ret; + } + if (al.naddrs == 0) /* Couldn't resolve any KDC names */ + return KRB5_REALM_CANT_RESOLVE; + *addr_pp = al.addrs; + *naddrs = al.naddrs; return 0; } + #ifdef KRB5_DNS_LOOKUP /* * Lookup a KDC via DNS SRV records */ -krb5_error_code -krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) +static krb5_error_code +krb5_locate_srv_dns_1 (realm, service, protocol, addrlist) const krb5_data *realm; const char *service; const char *protocol; - struct sockaddr **addr_pp; - int *naddrs; + struct addrlist *addrlist; { - int out, j, count; union { unsigned char bytes[2048]; HEADER hdr; } answer; unsigned char *p=NULL; char host[MAX_DNS_NAMELEN], *h; - struct sockaddr *addr = NULL; - struct sockaddr_in *sin_p = NULL; - struct hostent *hp = NULL; int type, class; int priority, weight, size, len, numanswers, numqueries, rdlen; unsigned short port; @@ -364,13 +592,7 @@ krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) struct srv_dns_entry *head = NULL; struct srv_dns_entry *srv = NULL, *entry = NULL; - - out = 0; - addr = (struct sockaddr *) malloc(sizeof(struct sockaddr)); - if (addr == NULL) - return ENOMEM; - - count = 1; + krb5_error_code code = 0; /* * First off, build a query of the form: @@ -390,19 +612,22 @@ krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) realm->data); /* Realm names don't (normally) end with ".", but if the query - doesn't end with "." and doesn't get an answer as is, the - resolv code will try appending the local domain. Since the - realm names are absolutes, let's stop that. + doesn't end with "." and doesn't get an answer as is, the + resolv code will try appending the local domain. Since the + realm names are absolutes, let's stop that. - But only if a name has been specified. If we are performing - a search on the prefix alone then the intention is to allow - the local domain or domain search lists to be expanded. - */ + But only if a name has been specified. If we are performing + a search on the prefix alone then the intention is to allow + the local domain or domain search lists to be expanded. */ h = host + strlen (host); if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host))) strcpy (h, "."); +#ifdef TEST + fprintf (stderr, "sending DNS SRV query for %s\n", host); +#endif + size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes)); if (size < hdrsize) @@ -546,36 +771,20 @@ krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) if (head == NULL) goto out; +#ifdef TEST + fprintf (stderr, "walking answer list:\n"); +#endif for (entry = head; entry != NULL; entry = entry->next) { - hp = gethostbyname(entry->host); - if (hp != 0) { - switch (hp->h_addrtype) { -#ifdef HAVE_NETINET_IN_H - case AF_INET: - for (j=0; hp->h_addr_list[j]; j++) { - sin_p = (struct sockaddr_in *) &addr[out++]; - memset ((char *) sin_p, 0, sizeof (struct sockaddr)); - sin_p->sin_family = hp->h_addrtype; - sin_p->sin_port = htons(entry->port); - memcpy((char *) &sin_p->sin_addr, - (char *) hp->h_addr_list[j], - sizeof(struct in_addr)); - if (out + 1 >= count) { - count += 5; - addr = (struct sockaddr *) - realloc((char *) addr, - sizeof(struct sockaddr) * count); - if (!addr) - goto out; - } - } - break; -#endif /* HAVE_NETINET_IN_H */ - default: - break; - } - } +#ifdef TEST + fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host); +#endif + code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0); + if (code) + break; } +#ifdef TEST + fprintf (stderr, "[end]\n"); +#endif for (entry = head; entry != NULL; ) { free(entry->host); @@ -590,15 +799,22 @@ krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) if (srv) free(srv); - if (out == 0) { /* No good servers */ - if (addr) - free(addr); - return KRB5_REALM_CANT_RESOLVE; - } + return code; +} - *addr_pp = addr; - *naddrs = out; - return 0; +static krb5_error_code +krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) + const krb5_data *realm; + const char *service; + const char *protocol; + struct sockaddr ***addr_pp; + int *naddrs; +{ + struct addrlist al = ADDRLIST_INIT; + krb5_error_code code; + + code = krb5_locate_srv_dns_1 (realm, service, protocol, &al); + return code; } #endif /* KRB5_DNS_LOOKUP */ @@ -607,56 +823,63 @@ krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs) */ krb5_error_code -krb5_locate_kdc(context, realm, addr_pp, naddrs, get_masters) - krb5_context context; - const krb5_data *realm; - struct sockaddr **addr_pp; - int *naddrs; - int get_masters; +krb5int_locate_server (krb5_context context, const krb5_data *realm, + struct sockaddr ***addr_pp, int *naddrs, + int get_masters, + const char *profname, const char *dnsname, + int is_stream, + /* network order port numbers! */ + int dflport1, int dflport2) { krb5_error_code code; + struct addrlist al = ADDRLIST_INIT; /* * We always try the local file first */ - code = krb5_locate_srv_conf(context, realm, "kdc", addr_pp, naddrs, - get_masters); + code = krb5_locate_srv_conf(context, realm, profname, &al, get_masters); #ifdef KRB5_DNS_LOOKUP if (code) { - int use_dns = _krb5_use_dns_kdc(context); - if ( use_dns ) { - code = krb5_locate_srv_dns(realm, - get_masters ? "_kerberos-master" : "_kerberos", - "_udp", addr_pp, naddrs); - } + int use_dns = _krb5_use_dns_kdc(context); + if (use_dns) + code = krb5_locate_srv_dns(realm, dnsname, + is_stream ? "_tcp" : "_udp", &al); } #endif /* KRB5_DNS_LOOKUP */ - return (code); + if (al.naddrs == 0) { /* No good servers */ + return KRB5_REALM_CANT_RESOLVE; + } + if (code == 0) { + *addr_pp = al.addrs; + *naddrs = al.naddrs; + } + return code; } -#if 0 /* Why is this useful? It's not used now, and it's certainly - not useful if you don't have the DNS code enabled. -KR */ - -/* - * It turns out that it is really useful to be able to use these functions - * for other things (like admin servers), so create an abstract function - * for this - */ - krb5_error_code -krb5_locate_server(realm, name, proto, addr_pp, naddrs) +krb5_locate_kdc(context, realm, addr_pp, naddrs, get_masters) + krb5_context context; const krb5_data *realm; - const char *name, *proto; - struct sockaddr **addr_pp; + struct sockaddr ***addr_pp; int *naddrs; + int get_masters; { - krb5_error_code code = KRB5_REALM_UNKNOWN; -#ifdef KRB5_DNS_LOOKUP - code = krb5_locate_srv_dns(realm, name, proto, - (struct sockaddr **) addr_pp, naddrs); -#endif /* KRB5_DNS_LOOKUP */ - return (code); + int udpport, sec_udpport; + + udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT); + sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0, + (udpport == htons (KRB5_DEFAULT_PORT) + ? KRB5_DEFAULT_SEC_PORT + : KRB5_DEFAULT_PORT)); + if (sec_udpport == udpport) + sec_udpport = 0; + + return krb5int_locate_server (context, realm, addr_pp, naddrs, get_masters, + "kdc", + (get_masters + ? "_kerberos-master" + : "_kerberos"), + 0, udpport, sec_udpport); } -#endif diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h index 12e2afb48..ceba9b477 100644 --- a/src/lib/krb5/os/os-proto.h +++ b/src/lib/krb5/os/os-proto.h @@ -34,7 +34,7 @@ krb5_error_code krb5_locate_kdc PROTOTYPE((krb5_context, const krb5_data *, - struct sockaddr **, + struct sockaddr ***, int *, int)); #endif diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index 58c97f837..86665b412 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -63,7 +63,7 @@ krb5_sendto_kdc (context, message, realm, reply, use_master) int use_master; { register int timeout, host, i; - struct sockaddr *addr; + struct sockaddr **addr; int naddr; int sent, nready; krb5_error_code retval; @@ -90,6 +90,8 @@ krb5_sendto_kdc (context, message, realm, reply, use_master) socklist[i] = INVALID_SOCKET; if (!(reply->data = malloc(krb5_max_dgram_size))) { + for (i = 0; i < naddr; i++) + krb5_xfree (addr[i]); krb5_xfree(addr); krb5_xfree(socklist); return ENOMEM; @@ -133,7 +135,7 @@ krb5_sendto_kdc (context, message, realm, reply, use_master) * protocol exists to support a particular socket type * within a given protocol family. */ - socklist[host] = socket(addr[host].sa_family, SOCK_DGRAM, 0); + socklist[host] = socket(addr[host]->sa_family, SOCK_DGRAM, 0); if (socklist[host] == INVALID_SOCKET) continue; /* try other hosts */ /* have a socket to send/recv from */ @@ -143,7 +145,7 @@ krb5_sendto_kdc (context, message, realm, reply, use_master) sendto, recvfrom. The connect here may return an error if the destination host is known to be unreachable. */ if (connect(socklist[host], - &addr[host], sizeof(addr[host])) == SOCKET_ERROR) + addr[host], sizeof(addr[host])) == SOCKET_ERROR) continue; } if (send(socklist[host],