Home | History | Annotate | Line # | Download | only in krb5
get_addrs.c revision 1.1.1.1
      1 /*	$NetBSD: get_addrs.c,v 1.1.1.1 2011/04/13 18:15:33 elric Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2002 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "krb5_locl.h"
     37 
     38 #ifdef __osf__
     39 /* hate */
     40 struct rtentry;
     41 struct mbuf;
     42 #endif
     43 #ifdef HAVE_NET_IF_H
     44 #include <net/if.h>
     45 #endif
     46 #include <ifaddrs.h>
     47 
     48 static krb5_error_code
     49 gethostname_fallback (krb5_context context, krb5_addresses *res)
     50 {
     51     krb5_error_code ret;
     52     char hostname[MAXHOSTNAMELEN];
     53     struct hostent *hostent;
     54 
     55     if (gethostname (hostname, sizeof(hostname))) {
     56 	ret = errno;
     57 	krb5_set_error_message(context, ret, "gethostname: %s", strerror(ret));
     58 	return ret;
     59     }
     60     hostent = roken_gethostbyname (hostname);
     61     if (hostent == NULL) {
     62 	ret = errno;
     63 	krb5_set_error_message (context, ret, "gethostbyname %s: %s",
     64 				hostname, strerror(ret));
     65 	return ret;
     66     }
     67     res->len = 1;
     68     res->val = malloc (sizeof(*res->val));
     69     if (res->val == NULL) {
     70 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
     71 	return ENOMEM;
     72     }
     73     res->val[0].addr_type = hostent->h_addrtype;
     74     res->val[0].address.data = NULL;
     75     res->val[0].address.length = 0;
     76     ret = krb5_data_copy (&res->val[0].address,
     77 			  hostent->h_addr,
     78 			  hostent->h_length);
     79     if (ret) {
     80 	free (res->val);
     81 	return ret;
     82     }
     83     return 0;
     84 }
     85 
     86 enum {
     87     LOOP            = 1,	/* do include loopback addrs */
     88     LOOP_IF_NONE    = 2,	/* include loopback addrs if no others */
     89     EXTRA_ADDRESSES = 4,	/* include extra addresses */
     90     SCAN_INTERFACES = 8		/* scan interfaces for addresses */
     91 };
     92 
     93 /*
     94  * Try to figure out the addresses of all configured interfaces with a
     95  * lot of magic ioctls.
     96  */
     97 
     98 static krb5_error_code
     99 find_all_addresses (krb5_context context, krb5_addresses *res, int flags)
    100 {
    101     struct sockaddr sa_zero;
    102     struct ifaddrs *ifa0, *ifa;
    103     krb5_error_code ret = ENXIO;
    104     unsigned int num, idx;
    105     krb5_addresses ignore_addresses;
    106 
    107     if (getifaddrs(&ifa0) == -1) {
    108 	ret = errno;
    109 	krb5_set_error_message(context, ret, "getifaddrs: %s", strerror(ret));
    110 	return (ret);
    111     }
    112 
    113     memset(&sa_zero, 0, sizeof(sa_zero));
    114 
    115     /* First, count all the ifaddrs. */
    116     for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++)
    117 	/* nothing */;
    118 
    119     if (num == 0) {
    120 	freeifaddrs(ifa0);
    121 	krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
    122 	return (ENXIO);
    123     }
    124 
    125     if (flags & EXTRA_ADDRESSES) {
    126 	/* we'll remove the addresses we don't care about */
    127 	ret = krb5_get_ignore_addresses(context, &ignore_addresses);
    128 	if(ret)
    129 	    return ret;
    130     }
    131 
    132     /* Allocate storage for them. */
    133     res->val = calloc(num, sizeof(*res->val));
    134     if (res->val == NULL) {
    135 	krb5_free_addresses(context, &ignore_addresses);
    136 	freeifaddrs(ifa0);
    137 	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
    138 	return ENOMEM;
    139     }
    140 
    141     /* Now traverse the list. */
    142     for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) {
    143 	if ((ifa->ifa_flags & IFF_UP) == 0)
    144 	    continue;
    145 	if (ifa->ifa_addr == NULL)
    146 	    continue;
    147 	if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
    148 	    continue;
    149 	if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
    150 	    continue;
    151 	if (krb5_sockaddr_is_loopback(ifa->ifa_addr) && (flags & LOOP) == 0)
    152 	    /* We'll deal with the LOOP_IF_NONE case later. */
    153 	    continue;
    154 
    155 	ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]);
    156 	if (ret) {
    157 	    /*
    158 	     * The most likely error here is going to be "Program
    159 	     * lacks support for address type".  This is no big
    160 	     * deal -- just continue, and we'll listen on the
    161 	     * addresses who's type we *do* support.
    162 	     */
    163 	    continue;
    164 	}
    165 	/* possibly skip this address? */
    166 	if((flags & EXTRA_ADDRESSES) &&
    167 	   krb5_address_search(context, &res->val[idx], &ignore_addresses)) {
    168 	    krb5_free_address(context, &res->val[idx]);
    169 	    flags &= ~LOOP_IF_NONE; /* we actually found an address,
    170                                        so don't add any loop-back
    171                                        addresses */
    172 	    continue;
    173 	}
    174 
    175 	idx++;
    176     }
    177 
    178     /*
    179      * If no addresses were found, and LOOP_IF_NONE is set, then find
    180      * the loopback addresses and add them to our list.
    181      */
    182     if ((flags & LOOP_IF_NONE) != 0 && idx == 0) {
    183 	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
    184 	    if ((ifa->ifa_flags & IFF_UP) == 0)
    185 		continue;
    186 	    if (ifa->ifa_addr == NULL)
    187 		continue;
    188 	    if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
    189 		continue;
    190 	    if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
    191 		continue;
    192 	    if (!krb5_sockaddr_is_loopback(ifa->ifa_addr))
    193 		continue;
    194 	    if ((ifa->ifa_flags & IFF_LOOPBACK) == 0)
    195 		/* Presumably loopback addrs are only used on loopback ifs! */
    196 		continue;
    197 	    ret = krb5_sockaddr2address(context,
    198 					ifa->ifa_addr, &res->val[idx]);
    199 	    if (ret)
    200 		continue; /* We don't consider this failure fatal */
    201 	    if((flags & EXTRA_ADDRESSES) &&
    202 	       krb5_address_search(context, &res->val[idx],
    203 				   &ignore_addresses)) {
    204 		krb5_free_address(context, &res->val[idx]);
    205 		continue;
    206 	    }
    207 	    idx++;
    208 	}
    209     }
    210 
    211     if (flags & EXTRA_ADDRESSES)
    212 	krb5_free_addresses(context, &ignore_addresses);
    213     freeifaddrs(ifa0);
    214     if (ret) {
    215 	free(res->val);
    216 	res->val = NULL;
    217     } else
    218 	res->len = idx;        /* Now a count. */
    219     return (ret);
    220 }
    221 
    222 static krb5_error_code
    223 get_addrs_int (krb5_context context, krb5_addresses *res, int flags)
    224 {
    225     krb5_error_code ret = -1;
    226 
    227     res->len = 0;
    228     res->val = NULL;
    229 
    230     if (flags & SCAN_INTERFACES) {
    231 	ret = find_all_addresses (context, res, flags);
    232 	if(ret || res->len == 0)
    233 	    ret = gethostname_fallback (context, res);
    234     } else {
    235 	ret = 0;
    236     }
    237 
    238     if(ret == 0 && (flags & EXTRA_ADDRESSES)) {
    239 	krb5_addresses a;
    240 	/* append user specified addresses */
    241 	ret = krb5_get_extra_addresses(context, &a);
    242 	if(ret) {
    243 	    krb5_free_addresses(context, res);
    244 	    return ret;
    245 	}
    246 	ret = krb5_append_addresses(context, res, &a);
    247 	if(ret) {
    248 	    krb5_free_addresses(context, res);
    249 	    return ret;
    250 	}
    251 	krb5_free_addresses(context, &a);
    252     }
    253     if(res->len == 0) {
    254 	free(res->val);
    255 	res->val = NULL;
    256     }
    257     return ret;
    258 }
    259 
    260 /*
    261  * Try to get all addresses, but return the one corresponding to
    262  * `hostname' if we fail.
    263  *
    264  * Only include loopback address if there are no other.
    265  */
    266 
    267 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    268 krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res)
    269 {
    270     int flags = LOOP_IF_NONE | EXTRA_ADDRESSES;
    271 
    272     if (context->scan_interfaces)
    273 	flags |= SCAN_INTERFACES;
    274 
    275     return get_addrs_int (context, res, flags);
    276 }
    277 
    278 /*
    279  * Try to get all local addresses that a server should listen to.
    280  * If that fails, we return the address corresponding to `hostname'.
    281  */
    282 
    283 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    284 krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res)
    285 {
    286     return get_addrs_int (context, res, LOOP | SCAN_INTERFACES);
    287 }
    288