155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2002 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
3655682Smarkm#ifdef __osf__
3755682Smarkm/* hate */
3855682Smarkmstruct rtentry;
3955682Smarkmstruct mbuf;
4055682Smarkm#endif
4155682Smarkm#ifdef HAVE_NET_IF_H
4255682Smarkm#include <net/if.h>
4355682Smarkm#endif
4472445Sassar#include <ifaddrs.h>
4555682Smarkm
4655682Smarkmstatic krb5_error_code
4778527Sassargethostname_fallback (krb5_context context, krb5_addresses *res)
4855682Smarkm{
4978527Sassar    krb5_error_code ret;
5072445Sassar    char hostname[MAXHOSTNAMELEN];
5172445Sassar    struct hostent *hostent;
5255682Smarkm
5378527Sassar    if (gethostname (hostname, sizeof(hostname))) {
5478527Sassar	ret = errno;
55233294Sstas	krb5_set_error_message(context, ret, "gethostname: %s", strerror(ret));
5678527Sassar	return ret;
5778527Sassar    }
5872445Sassar    hostent = roken_gethostbyname (hostname);
5978527Sassar    if (hostent == NULL) {
6078527Sassar	ret = errno;
61233294Sstas	krb5_set_error_message (context, ret, "gethostbyname %s: %s",
62233294Sstas				hostname, strerror(ret));
6378527Sassar	return ret;
6478527Sassar    }
6572445Sassar    res->len = 1;
6672445Sassar    res->val = malloc (sizeof(*res->val));
6778527Sassar    if (res->val == NULL) {
68233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
6972445Sassar	return ENOMEM;
7078527Sassar    }
7172445Sassar    res->val[0].addr_type = hostent->h_addrtype;
7272445Sassar    res->val[0].address.data = NULL;
7372445Sassar    res->val[0].address.length = 0;
7478527Sassar    ret = krb5_data_copy (&res->val[0].address,
7572445Sassar			  hostent->h_addr,
7672445Sassar			  hostent->h_length);
7778527Sassar    if (ret) {
7872445Sassar	free (res->val);
7978527Sassar	return ret;
8072445Sassar    }
8172445Sassar    return 0;
8255682Smarkm}
8355682Smarkm
8455682Smarkmenum {
85233294Sstas    LOOP            = 1,	/* do include loopback addrs */
86233294Sstas    LOOP_IF_NONE    = 2,	/* include loopback addrs if no others */
8755682Smarkm    EXTRA_ADDRESSES = 4,	/* include extra addresses */
8855682Smarkm    SCAN_INTERFACES = 8		/* scan interfaces for addresses */
8955682Smarkm};
9055682Smarkm
9155682Smarkm/*
9255682Smarkm * Try to figure out the addresses of all configured interfaces with a
9355682Smarkm * lot of magic ioctls.
9455682Smarkm */
9555682Smarkm
9655682Smarkmstatic krb5_error_code
9772445Sassarfind_all_addresses (krb5_context context, krb5_addresses *res, int flags)
9855682Smarkm{
9972445Sassar    struct sockaddr sa_zero;
10072445Sassar    struct ifaddrs *ifa0, *ifa;
101233294Sstas    krb5_error_code ret = ENXIO;
102233294Sstas    unsigned int num, idx;
10390926Snectar    krb5_addresses ignore_addresses;
10455682Smarkm
10578527Sassar    if (getifaddrs(&ifa0) == -1) {
10678527Sassar	ret = errno;
107233294Sstas	krb5_set_error_message(context, ret, "getifaddrs: %s", strerror(ret));
10878527Sassar	return (ret);
10978527Sassar    }
11055682Smarkm
11172445Sassar    memset(&sa_zero, 0, sizeof(sa_zero));
11255682Smarkm
11372445Sassar    /* First, count all the ifaddrs. */
11472445Sassar    for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++)
11572445Sassar	/* nothing */;
11655682Smarkm
11772445Sassar    if (num == 0) {
11872445Sassar	freeifaddrs(ifa0);
119233294Sstas	krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
12072445Sassar	return (ENXIO);
12172445Sassar    }
12255682Smarkm
12390926Snectar    if (flags & EXTRA_ADDRESSES) {
12490926Snectar	/* we'll remove the addresses we don't care about */
12590926Snectar	ret = krb5_get_ignore_addresses(context, &ignore_addresses);
12690926Snectar	if(ret)
12790926Snectar	    return ret;
12890926Snectar    }
12990926Snectar
13072445Sassar    /* Allocate storage for them. */
13172445Sassar    res->val = calloc(num, sizeof(*res->val));
13272445Sassar    if (res->val == NULL) {
13390926Snectar	krb5_free_addresses(context, &ignore_addresses);
13472445Sassar	freeifaddrs(ifa0);
135233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
136233294Sstas	return ENOMEM;
13772445Sassar    }
13855682Smarkm
13972445Sassar    /* Now traverse the list. */
14072445Sassar    for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) {
14172445Sassar	if ((ifa->ifa_flags & IFF_UP) == 0)
14272445Sassar	    continue;
143120945Snectar	if (ifa->ifa_addr == NULL)
144120945Snectar	    continue;
14572445Sassar	if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
14672445Sassar	    continue;
14772445Sassar	if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
14872445Sassar	    continue;
149233294Sstas	if (krb5_sockaddr_is_loopback(ifa->ifa_addr) && (flags & LOOP) == 0)
15072445Sassar	    /* We'll deal with the LOOP_IF_NONE case later. */
151233294Sstas	    continue;
15255682Smarkm
15378527Sassar	ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]);
15472445Sassar	if (ret) {
15572445Sassar	    /*
15672445Sassar	     * The most likely error here is going to be "Program
15772445Sassar	     * lacks support for address type".  This is no big
15872445Sassar	     * deal -- just continue, and we'll listen on the
15972445Sassar	     * addresses who's type we *do* support.
16072445Sassar	     */
16172445Sassar	    continue;
16272445Sassar	}
16390926Snectar	/* possibly skip this address? */
164233294Sstas	if((flags & EXTRA_ADDRESSES) &&
16590926Snectar	   krb5_address_search(context, &res->val[idx], &ignore_addresses)) {
16690926Snectar	    krb5_free_address(context, &res->val[idx]);
16790926Snectar	    flags &= ~LOOP_IF_NONE; /* we actually found an address,
16890926Snectar                                       so don't add any loop-back
16990926Snectar                                       addresses */
17090926Snectar	    continue;
17190926Snectar	}
17290926Snectar
17372445Sassar	idx++;
17472445Sassar    }
17555682Smarkm
17672445Sassar    /*
17772445Sassar     * If no addresses were found, and LOOP_IF_NONE is set, then find
17872445Sassar     * the loopback addresses and add them to our list.
17972445Sassar     */
18072445Sassar    if ((flags & LOOP_IF_NONE) != 0 && idx == 0) {
18172445Sassar	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
18272445Sassar	    if ((ifa->ifa_flags & IFF_UP) == 0)
18372445Sassar		continue;
184120945Snectar	    if (ifa->ifa_addr == NULL)
185120945Snectar		continue;
18672445Sassar	    if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
18772445Sassar		continue;
18872445Sassar	    if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
18972445Sassar		continue;
190233294Sstas	    if (!krb5_sockaddr_is_loopback(ifa->ifa_addr))
191233294Sstas		continue;
192233294Sstas	    if ((ifa->ifa_flags & IFF_LOOPBACK) == 0)
193233294Sstas		/* Presumably loopback addrs are only used on loopback ifs! */
194233294Sstas		continue;
195233294Sstas	    ret = krb5_sockaddr2address(context,
196233294Sstas					ifa->ifa_addr, &res->val[idx]);
197233294Sstas	    if (ret)
198233294Sstas		continue; /* We don't consider this failure fatal */
199233294Sstas	    if((flags & EXTRA_ADDRESSES) &&
200233294Sstas	       krb5_address_search(context, &res->val[idx],
201233294Sstas				   &ignore_addresses)) {
202233294Sstas		krb5_free_address(context, &res->val[idx]);
203233294Sstas		continue;
20472445Sassar	    }
205233294Sstas	    idx++;
20672445Sassar	}
20772445Sassar    }
20855682Smarkm
20990926Snectar    if (flags & EXTRA_ADDRESSES)
21090926Snectar	krb5_free_addresses(context, &ignore_addresses);
21172445Sassar    freeifaddrs(ifa0);
212233294Sstas    if (ret) {
21372445Sassar	free(res->val);
214233294Sstas	res->val = NULL;
215233294Sstas    } else
21672445Sassar	res->len = idx;        /* Now a count. */
21772445Sassar    return (ret);
21855682Smarkm}
21955682Smarkm
22055682Smarkmstatic krb5_error_code
22155682Smarkmget_addrs_int (krb5_context context, krb5_addresses *res, int flags)
22255682Smarkm{
22355682Smarkm    krb5_error_code ret = -1;
22455682Smarkm
225233294Sstas    res->len = 0;
226233294Sstas    res->val = NULL;
227233294Sstas
22855682Smarkm    if (flags & SCAN_INTERFACES) {
22972445Sassar	ret = find_all_addresses (context, res, flags);
23055682Smarkm	if(ret || res->len == 0)
23178527Sassar	    ret = gethostname_fallback (context, res);
232102644Snectar    } else {
23355682Smarkm	ret = 0;
234102644Snectar    }
23555682Smarkm
23655682Smarkm    if(ret == 0 && (flags & EXTRA_ADDRESSES)) {
23790926Snectar	krb5_addresses a;
23855682Smarkm	/* append user specified addresses */
23955682Smarkm	ret = krb5_get_extra_addresses(context, &a);
24055682Smarkm	if(ret) {
24155682Smarkm	    krb5_free_addresses(context, res);
24255682Smarkm	    return ret;
24355682Smarkm	}
24455682Smarkm	ret = krb5_append_addresses(context, res, &a);
24555682Smarkm	if(ret) {
24655682Smarkm	    krb5_free_addresses(context, res);
24755682Smarkm	    return ret;
24855682Smarkm	}
24955682Smarkm	krb5_free_addresses(context, &a);
25055682Smarkm    }
25190926Snectar    if(res->len == 0) {
25290926Snectar	free(res->val);
25390926Snectar	res->val = NULL;
25490926Snectar    }
25555682Smarkm    return ret;
25655682Smarkm}
25755682Smarkm
25855682Smarkm/*
25955682Smarkm * Try to get all addresses, but return the one corresponding to
26055682Smarkm * `hostname' if we fail.
26155682Smarkm *
26255682Smarkm * Only include loopback address if there are no other.
26355682Smarkm */
26455682Smarkm
265233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
26655682Smarkmkrb5_get_all_client_addrs (krb5_context context, krb5_addresses *res)
26755682Smarkm{
26855682Smarkm    int flags = LOOP_IF_NONE | EXTRA_ADDRESSES;
26955682Smarkm
27055682Smarkm    if (context->scan_interfaces)
27155682Smarkm	flags |= SCAN_INTERFACES;
27255682Smarkm
27355682Smarkm    return get_addrs_int (context, res, flags);
27455682Smarkm}
27555682Smarkm
27655682Smarkm/*
27755682Smarkm * Try to get all local addresses that a server should listen to.
27855682Smarkm * If that fails, we return the address corresponding to `hostname'.
27955682Smarkm */
28055682Smarkm
281233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
28255682Smarkmkrb5_get_all_server_addrs (krb5_context context, krb5_addresses *res)
28355682Smarkm{
28455682Smarkm    return get_addrs_int (context, res, LOOP | SCAN_INTERFACES);
28555682Smarkm}
286