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