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 */
38struct rtentry;
39struct mbuf;
40#endif
41#ifdef HAVE_NET_IF_H
42#include <net/if.h>
43#endif
44#include <ifaddrs.h>
45
46static krb5_error_code
47gethostname_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
84enum {
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
96static krb5_error_code
97find_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
220static krb5_error_code
221get_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
265KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
266krb5_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
281KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
282krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res)
283{
284    return get_addrs_int (context, res, LOOP | SCAN_INTERFACES);
285}
286
287KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
288krb5_get_all_any_addrs(krb5_context context, krb5_addresses *res)
289{
290    krb5_error_code ret;
291    krb5_addresses as;
292    krb5_address a;
293    struct sockaddr_storage ss;
294    krb5_socklen_t sa_size;
295    unsigned int n;
296    int types[] = {
297	AF_INET6,
298	AF_INET
299    };
300
301    memset(&a, 0, sizeof(a));
302
303    res->len = 0;
304    res->val = NULL;
305
306    for (n = 0; n < sizeof(types)/sizeof(types[0]); n++) {
307	sa_size = sizeof(ss);
308	ret = krb5_anyaddr(context, types[n], (struct sockaddr *)&ss, &sa_size, 0);
309	if (ret)
310	    continue;
311
312	ret = krb5_sockaddr2address(context, (struct sockaddr *)&ss, &a);
313	if (ret)
314	    continue;
315
316	as.val = &a;
317	as.len = 1;
318	ret = krb5_append_addresses(context, res, &as);
319	krb5_free_address(context, &a);
320	if(ret) {
321	    krb5_free_addresses(context, res);
322	    return ret;
323	}
324    }
325    if (res->len == 0) {
326	krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
327	return (ENXIO);
328    }
329    return 0;
330}
331