1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
6 /*
7  * include/foreachaddr.c
8  *
9  * Copyright 1990,1991,2000,2001,2002 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  *
32  * Iterate over the protocol addresses supported by this host, invoking
33  * a callback function or three supplied by the caller.
34  *
35  * XNS support is untested, but "should just work".  (Hah!)
36  */
37 
38 /* This is the primary "export" of this file.  It's a static function,
39    so this file must be #included in the .c file containing the
40    caller.
41 
42    This function iterates over all the addresses it can find for the
43    local system, in one or two passes.  In each pass, and between the
44    two, it can invoke callback functions supplied by the caller.  The
45    two passes should operate on the same information, though not
46    necessarily in the same order each time.  Duplicate and local
47    addresses should be eliminated.  Storage passed to callback
48    functions should not be assumed to be valid after foreach_localaddr
49    returns.
50 
51    The int return value is an errno value (XXX or krb5_error_code
52    returned for a socket error) if something internal to
53    foreach_localaddr fails.  If one of the callback functions wants to
54    indicate an error, it should store something via the 'data' handle.
55    If any callback function returns a non-zero value,
56    foreach_localaddr will clean up and return immediately.
57 
58    Multiple definitions are provided below, dependent on various
59    system facilities for extracting the necessary information.  */
60 
61 /* Solaris Kerberos: changing foreach_localaddr to non-static as it's called in
62  * a couple places.
63  */
64 
65 #ifdef TEST
66 # define Tprintf(X) printf X
67 # define Tperror(X) perror(X)
68 #else
69 # define Tprintf(X) (void) X
70 # define Tperror(X) (void)(X)
71 #endif
72 
73 /*
74  * The SIOCGIF* ioctls require a socket.
75  * It doesn't matter *what* kind of socket they use, but it has to be
76  * a socket.
77  *
78  * Of course, you can't just ask the kernel for a socket of arbitrary
79  * type; you have to ask for one with a valid type.
80  *
81  */
82 #ifdef HAVE_NETINET_IN_H
83 #include <netinet/in.h>
84 #ifndef USE_AF
85 #define USE_AF AF_INET
86 #define USE_TYPE SOCK_DGRAM
87 #define USE_PROTO 0
88 #endif
89 #endif
90 
91 #ifdef KRB5_USE_NS
92 #include <netns/ns.h>
93 #ifndef USE_AF
94 #define USE_AF AF_NS
95 #define USE_TYPE SOCK_DGRAM
96 #define USE_PROTO 0		/* guess */
97 #endif
98 #endif
99 /*
100  * Add more address families here.
101  */
102 
103 #include <errno.h>
104 #include <fake-addrinfo.h>
105 #include <sys/sockio.h>
106 #include <k5-int.h>
107 
108 /*
109  * Return all the protocol addresses of this host.
110  *
111  * We could kludge up something to return all addresses, assuming that
112  * they're valid kerberos protocol addresses, but we wouldn't know the
113  * real size of the sockaddr or know which part of it was actually the
114  * host part.
115  *
116  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
117  */
118 
119 /*
120  * BSD 4.4 defines the size of an ifreq to be
121  * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
122  * However, under earlier systems, sa_len isn't present, so the size is
123  * just sizeof(struct ifreq).
124  */
125 #ifdef HAVE_SA_LEN
126 #ifndef max
127 #define max(a,b) ((a) > (b) ? (a) : (b))
128 #endif
129 #define ifreq_size(i) max(sizeof(struct ifreq),\
130      sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
131 #else
132 #define ifreq_size(i) sizeof(struct ifreq)
133 #endif /* HAVE_SA_LEN*/
134 
135 
136 #ifdef SIOCGLIFCONF /* Solaris */
137 static int
138 get_lifconf (int af, int s, size_t *lenp, char *buf)
139     /*@modifies *buf,*lenp@*/
140 {
141     int ret;
142     struct lifconf lifc;
143 
144     lifc.lifc_family = af;
145     lifc.lifc_flags = 0;
146     lifc.lifc_len = *lenp;
147     lifc.lifc_buf = buf;
148     memset(buf, 0, *lenp);
149 
150     ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc);
151     if (ret)
152 	Tperror ("SIOCGLIFCONF");
153 
154     *lenp = lifc.lifc_len;
155     return ret;
156 }
157 #endif
158 
159 /* Return value is errno if internal stuff failed, otherwise zero,
160    even in the case where a called function terminated the iteration.
161 
162    If one of the callback functions wants to pass back an error
163    indication, it should do it via some field pointed to by the DATA
164    argument.  */
165 
166 int
167 foreach_localaddr (void *data,
168 		   int (*pass1fn) (void *, struct sockaddr *),
169 		   int (*betweenfn) (void *),
170 		   int (*pass2fn) (void *, struct sockaddr *))
171 {
172     /* Okay, this is kind of odd.  We have to use each of the address
173        families we care about, because with an AF_INET socket, extra
174        interfaces like hme0:1 that have only AF_INET6 addresses will
175        cause errors.  Similarly, if hme0 has more AF_INET addresses
176        than AF_INET6 addresses, we won't be able to retrieve all of
177        the AF_INET addresses if we use an AF_INET6 socket.  Since
178        neither family is guaranteed to have the greater number of
179        addresses, we should use both.
180 
181        If it weren't for this little quirk, we could use one socket of
182        any type, and ask for addresses of all types.  At least, it
183        seems to work that way.  */
184 
185     /* Solaris kerberos: avoid using AF_NS if no define */
186 #if defined (KRB5_USE_INET6) && defined (KRB5_USE_NS)
187     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
188 #elif defined (KRB5_USE_INET6)
189     static const int afs[] = { AF_INET, AF_INET6 };
190 #else
191     static const int afs[] = { AF_INET };
192 #endif
193 
194 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
195     struct {
196 	int af;
197 	int sock;
198 	void *buf;
199 	size_t buf_size;
200 	struct lifnum lifnum;
201     } afp[N_AFS];
202     int code, i, j;
203     int retval = 0, afidx;
204     krb5_error_code sock_err = 0;
205     struct lifreq *lifr, lifreq, *lifr2;
206 
207 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
208 #define P (afp[afidx])
209 
210     KRB5_LOG0(KRB5_INFO, "foreach_localaddr() start");
211     /* init */
212     FOREACH_AF () {
213 	P.af = afs[afidx];
214 	P.sock = -1;
215 	P.buf = 0;
216     }
217 
218     /* first pass: get raw data, discard uninteresting addresses, callback */
219     FOREACH_AF () {
220 	KRB5_LOG (KRB5_INFO, "foreach_localaddr() trying af %d", P.af);
221 	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
222 	if (P.sock < 0) {
223 	    sock_err = SOCKET_ERROR;
224 	    Tperror ("socket");
225 	    continue;
226 	}
227 
228 	P.lifnum.lifn_family = P.af;
229 	P.lifnum.lifn_flags = 0;
230 	P.lifnum.lifn_count = 0;
231 	code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum);
232 	if (code) {
233 	    Tperror ("ioctl(SIOCGLIFNUM)");
234 	    retval = errno;
235 	    goto punt;
236 	}
237 
238 	KRB5_LOG (KRB5_INFO, "foreach_localaddr() lifn_count %d",
239 		P.lifnum.lifn_count);
240 	P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2;
241 	P.buf = malloc (P.buf_size);
242 	if (P.buf == NULL) {
243 	    retval = errno;
244 	    goto punt;
245 	}
246 
247 	code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
248 	if (code < 0) {
249 	    retval = errno;
250 	    goto punt;
251 	}
252 
253 	for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) {
254 	    /*LINTED*/
255 	    lifr = (struct lifreq *)((caddr_t) P.buf+i);
256 
257 	    strncpy(lifreq.lifr_name, lifr->lifr_name,
258 		    sizeof (lifreq.lifr_name));
259 	    KRB5_LOG (KRB5_INFO, "foreach_localaddr() interface %s",
260 		    lifreq.lifr_name);
261 	    /* ioctl unknown to lclint */
262 	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
263 		Tperror ("ioctl(SIOCGLIFFLAGS)");
264 	    skip:
265 		KRB5_LOG (KRB5_INFO,
266 			"foreach_localaddr() skipping interface %s",
267 			lifr->lifr_name);
268 		/* mark for next pass */
269 		lifr->lifr_name[0] = '\0';
270 		continue;
271 	    }
272 
273 #ifdef IFF_LOOPBACK
274 	    /* None of the current callers want loopback addresses.  */
275 	    if (lifreq.lifr_flags & IFF_LOOPBACK) {
276 		Tprintf (("  loopback\n"));
277 		goto skip;
278 	    }
279 #endif
280 	    /* Ignore interfaces that are down.  */
281 	    if ((lifreq.lifr_flags & IFF_UP) == 0) {
282 		Tprintf (("  down\n"));
283 		goto skip;
284 	    }
285 
286 	    /* Make sure we didn't process this address already.  */
287 	    for (j = 0; j < i; j += sizeof (*lifr2)) {
288 		/*LINTED*/
289 		lifr2 = (struct lifreq *)((caddr_t) P.buf+j);
290 		if (lifr2->lifr_name[0] == '\0')
291 		    continue;
292 		if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family
293 		    /* Compare address info.  If this isn't good enough --
294 		       i.e., if random padding bytes turn out to differ
295 		       when the addresses are the same -- then we'll have
296 		       to do it on a per address family basis.  */
297 		    && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr,
298 				sizeof (*lifr))) {
299 		    Tprintf (("  duplicate addr\n"));
300 		    KRB5_LOG0 (KRB5_INFO, "foreach_localaddr() dup addr");
301 		    goto skip;
302 		}
303 	    }
304 
305 	    if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
306 		goto punt;
307 	}
308     }
309 
310     /* Did we actually get any working sockets?  */
311     FOREACH_AF ()
312 	if (P.sock != -1)
313 	    goto have_working_socket;
314     retval = sock_err;
315     goto punt;
316 have_working_socket:
317 
318     if (betweenfn != NULL && (*betweenfn)(data))
319 	goto punt;
320 
321     if (pass2fn)
322 	FOREACH_AF ()
323 	    if (P.sock >= 0) {
324 		for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) {
325 		    /*LINTED*/
326 		    lifr = (struct lifreq *)((caddr_t) P.buf+i);
327 
328 		    if (lifr->lifr_name[0] == '\0')
329 			/* Marked in first pass to be ignored.  */
330 			continue;
331 
332 		    KRB5_LOG (KRB5_INFO,
333 			    "foreach_localaddr() doing pass2fn i = %d",
334 			    i);
335 		    if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
336 			goto punt;
337 		}
338 	    }
339 punt:
340     FOREACH_AF () {
341 	closesocket(P.sock);
342 	free (P.buf);
343     }
344 
345     return retval;
346 }
347