xref: /freebsd/crypto/krb5/src/lib/krb5/os/localaddr.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/localaddr.c */
3 /*
4  * Copyright 1990,1991,2000,2001,2002,2004,2007,2008 by the Massachusetts
5  * Institute of Technology.  All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 /*
28  * Return the protocol addresses supported by this host.
29  * Exports from this file:
30  *   krb5int_foreach_localaddr (does callbacks)
31  *   krb5_os_localaddr (doesn't include krb5.conf extra_addresses)
32  *
33  * XNS support is untested, but "Should just work".  (Hah!)
34  */
35 
36 #include "k5-int.h"
37 #include "os-proto.h"
38 
39 #if !defined(_WIN32)
40 
41 /* needed for solaris, harmless elsewhere... */
42 #define BSD_COMP
43 #include <sys/ioctl.h>
44 #include <sys/time.h>
45 #include <errno.h>
46 #include <stddef.h>
47 #include <ctype.h>
48 
49 #if defined(TEST) || defined(DEBUG)
50 # include "fake-addrinfo.h"
51 #endif
52 
53 #include "foreachaddr.h"
54 
55 /* Note: foreach_localaddr is exported from the library through
56    krb5int_accessor, for the KDC to use.
57 
58    This function iterates over all the addresses it can find for the
59    local system, in one or two passes.  In each pass, and between the
60    two, it can invoke callback functions supplied by the caller.  The
61    two passes should operate on the same information, though not
62    necessarily in the same order each time.  Duplicate and local
63    addresses should be eliminated.  Storage passed to callback
64    functions should not be assumed to be valid after foreach_localaddr
65    returns.
66 
67    The int return value is an errno value (XXX or krb5_error_code
68    returned for a socket error) if something internal to
69    foreach_localaddr fails.  If one of the callback functions wants to
70    indicate an error, it should store something via the 'data' handle.
71    If any callback function returns a non-zero value,
72    foreach_localaddr will clean up and return immediately.
73 
74    Multiple definitions are provided below, dependent on various
75    system facilities for extracting the necessary information.  */
76 
77 /* Now, on to the implementations, and heaps of debugging code.  */
78 
79 #ifdef TEST
80 # define Tprintf(X) printf X
81 # define Tperror(X) perror(X)
82 #else
83 # define Tprintf(X) (void) X
84 # define Tperror(X) (void)(X)
85 #endif
86 
87 /*
88  * The SIOCGIF* ioctls require a socket.
89  * It doesn't matter *what* kind of socket they use, but it has to be
90  * a socket.
91  *
92  * Of course, you can't just ask the kernel for a socket of arbitrary
93  * type; you have to ask for one with a valid type.
94  *
95  */
96 #ifdef HAVE_NETINET_IN_H
97 #include <netinet/in.h>
98 #ifndef USE_AF
99 #define USE_AF AF_INET
100 #define USE_TYPE SOCK_DGRAM
101 #define USE_PROTO 0
102 #endif
103 #endif
104 
105 #ifdef KRB5_USE_NS
106 #include <netns/ns.h>
107 #ifndef USE_AF
108 #define USE_AF AF_NS
109 #define USE_TYPE SOCK_DGRAM
110 #define USE_PROTO 0             /* guess */
111 #endif
112 #endif
113 /*
114  * Add more address families here.
115  */
116 
117 
118 #if defined(__linux__) && !defined(HAVE_IFADDRS_H)
119 #define LINUX_IPV6_HACK
120 #endif
121 
122 #include <errno.h>
123 
124 /*
125  * Return all the protocol addresses of this host.
126  *
127  * We could kludge up something to return all addresses, assuming that
128  * they're valid kerberos protocol addresses, but we wouldn't know the
129  * real size of the sockaddr or know which part of it was actually the
130  * host part.
131  *
132  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
133  */
134 
135 /*
136  * BSD 4.4 defines the size of an ifreq to be
137  * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
138  * However, under earlier systems, sa_len isn't present, so the size is
139  * just sizeof(struct ifreq).
140  */
141 #ifdef HAVE_SA_LEN
142 #ifndef max
143 #define max(a,b) ((a) > (b) ? (a) : (b))
144 #endif
145 #define ifreq_size(i) max(sizeof(struct ifreq),                         \
146                           sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
147 #else
148 #define ifreq_size(i) sizeof(struct ifreq)
149 #endif /* HAVE_SA_LEN*/
150 
151 #if defined(DEBUG) || defined(TEST)
152 #include <netinet/in.h>
153 #include <net/if.h>
154 
155 #include "socket-utils.h"
156 #include "fake-addrinfo.h"
157 
158 void printaddr (struct sockaddr *);
159 
160 void
printaddr(struct sockaddr * sa)161 printaddr(struct sockaddr *sa)
162 /*@modifies fileSystem@*/
163 {
164     char buf[NI_MAXHOST];
165     int err;
166 
167     printf ("%p ", (void *) sa);
168     err = getnameinfo (sa, sa_socklen (sa), buf, sizeof (buf), 0, 0,
169                        NI_NUMERICHOST);
170     if (err)
171         printf ("<getnameinfo error %d: %s> family=%d",
172                 err, gai_strerror (err),
173                 sa->sa_family);
174     else
175         printf ("%s", buf);
176 }
177 #endif
178 
179 #if 0
180 static int
181 is_loopback_address(struct sockaddr *sa)
182 {
183     switch (sa->sa_family) {
184     case AF_INET:
185         return sa2sin(sa)->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
186     case AF_INET6:
187         return IN6_IS_ADDR_LOOPBACK(&sa2sin6(sa)->sin6_addr);
188     default:
189         return 0;
190     }
191 }
192 #endif
193 
194 #ifdef HAVE_IFADDRS_H
195 #include <ifaddrs.h>
196 
197 #ifdef DEBUG
198 void
printifaddr(struct ifaddrs * ifp)199 printifaddr(struct ifaddrs *ifp)
200 {
201     printf ("%p={\n", ifp);
202 /*  printf ("\tnext=%p\n", ifp->ifa_next); */
203     printf ("\tname=%s\n", ifp->ifa_name);
204     printf ("\tflags=");
205     {
206         int ch, flags = ifp->ifa_flags;
207         printf ("%x", flags);
208         ch = '<';
209 #define X(F) if (flags & IFF_##F) { printf ("%c%s", ch, #F); flags &= ~IFF_##F; ch = ','; }
210         X (UP); X (BROADCAST); X (DEBUG); X (LOOPBACK); X (POINTOPOINT);
211         X (NOTRAILERS); X (RUNNING); X (NOARP); X (PROMISC); X (ALLMULTI);
212 #ifdef IFF_OACTIVE
213         X (OACTIVE);
214 #endif
215 #ifdef IFF_SIMPLE
216         X (SIMPLEX);
217 #endif
218         X (MULTICAST);
219         printf (">");
220 #undef X
221     }
222     if (ifp->ifa_addr)
223         printf ("\n\taddr="), printaddr (ifp->ifa_addr);
224     if (ifp->ifa_netmask)
225         printf ("\n\tnetmask="), printaddr (ifp->ifa_netmask);
226     if (ifp->ifa_broadaddr)
227         printf ("\n\tbroadaddr="), printaddr (ifp->ifa_broadaddr);
228     if (ifp->ifa_dstaddr)
229         printf ("\n\tdstaddr="), printaddr (ifp->ifa_dstaddr);
230     if (ifp->ifa_data)
231         printf ("\n\tdata=%p", ifp->ifa_data);
232     printf ("\n}\n");
233 }
234 #endif /* DEBUG */
235 
236 #include <string.h>
237 #include <stdlib.h>
238 
239 static int
addr_eq(struct sockaddr * s1,struct sockaddr * s2)240 addr_eq (struct sockaddr *s1, struct sockaddr *s2)
241 {
242     if (s1->sa_family != s2->sa_family)
243         return 0;
244     switch (s1->sa_family) {
245     case AF_INET:
246         return !memcmp(&sa2sin(s1)->sin_addr, &sa2sin(s2)->sin_addr,
247                        sizeof(sa2sin(s1)->sin_addr));
248     case AF_INET6:
249         return !memcmp(&sa2sin6(s1)->sin6_addr, &sa2sin6(s2)->sin6_addr,
250                        sizeof(sa2sin6(s1)->sin6_addr));
251     default:
252         /* Err on side of duplicate listings.  */
253         return 0;
254     }
255 }
256 #endif
257 
258 #ifndef HAVE_IFADDRS_H
259 /*@-usereleased@*/ /* lclint doesn't understand realloc */
260 static /*@null@*/ void *
grow_or_free(void * ptr,size_t newsize)261 grow_or_free (/*@only@*/ void *ptr, size_t newsize)
262 /*@*/
263 {
264     void *newptr;
265     newptr = realloc (ptr, newsize);
266     if (newptr == NULL && newsize != 0) {
267         free (ptr);             /* lclint complains but this is right */
268         return NULL;
269     }
270     return newptr;
271 }
272 /*@=usereleased@*/
273 
274 static int
get_ifconf(int s,size_t * lenp,char * buf)275 get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf)
276 /*@modifies *buf,*lenp@*/
277 {
278     int ret;
279     struct ifconf ifc;
280 
281     /*@+matchanyintegral@*/
282     ifc.ifc_len = *lenp;
283     /*@=matchanyintegral@*/
284     ifc.ifc_buf = buf;
285     memset(buf, 0, *lenp);
286     /*@-moduncon@*/
287     ret = ioctl (s, SIOCGIFCONF, (char *)&ifc);
288     /*@=moduncon@*/
289     /*@+matchanyintegral@*/
290     *lenp = ifc.ifc_len;
291     /*@=matchanyintegral@*/
292     return ret;
293 }
294 
295 /* Solaris uses SIOCGLIFCONF to return struct lifconf which is just
296    an extended version of struct ifconf.
297 
298    HP-UX 11 also appears to have SIOCGLIFCONF, but uses struct
299    if_laddrconf, and struct if_laddrreq to be used with
300    SIOCGLIFADDR.  */
301 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_LIFCONF)
302 static int
get_lifconf(int af,int s,size_t * lenp,char * buf)303 get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
304 /*@modifies *buf,*lenp@*/
305 {
306     int ret;
307     struct lifconf lifc;
308 
309     lifc.lifc_family = af;
310     lifc.lifc_flags = 0;
311     /*@+matchanyintegral@*/
312     lifc.lifc_len = *lenp;
313     /*@=matchanyintegral@*/
314     lifc.lifc_buf = buf;
315     memset(buf, 0, *lenp);
316     /*@-moduncon@*/
317     ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc);
318     if (ret)
319         Tperror ("SIOCGLIFCONF");
320     /*@=moduncon@*/
321     /*@+matchanyintegral@*/
322     *lenp = lifc.lifc_len;
323     /*@=matchanyintegral@*/
324     return ret;
325 }
326 #endif
327 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0
328 /* I'm not sure if this is needed or if net/if.h will pull it in.  */
329 /* #include <net/if6.h> */
330 static int
get_if_laddrconf(int af,int s,size_t * lenp,char * buf)331 get_if_laddrconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
332 /*@modifies *buf,*lenp@*/
333 {
334     int ret;
335     struct if_laddrconf iflc;
336 
337     /*@+matchanyintegral@*/
338     iflc.iflc_len = *lenp;
339     /*@=matchanyintegral@*/
340     iflc.iflc_buf = buf;
341     memset(buf, 0, *lenp);
342     /*@-moduncon@*/
343     ret = ioctl (s, SIOCGLIFCONF, (char *)&iflc);
344     if (ret)
345         Tperror ("SIOCGLIFCONF");
346     /*@=moduncon@*/
347     /*@+matchanyintegral@*/
348     *lenp = iflc.iflc_len;
349     /*@=matchanyintegral@*/
350     return ret;
351 }
352 #endif
353 #endif /* ! HAVE_IFADDRS_H */
354 
355 #ifdef LINUX_IPV6_HACK
356 #include <stdio.h>
357 /* Read IPv6 addresses out of /proc/net/if_inet6, since there isn't
358    (currently) any ioctl to return them.  */
359 struct linux_ipv6_addr_list {
360     struct sockaddr_in6 addr;
361     struct linux_ipv6_addr_list *next;
362 };
363 static struct linux_ipv6_addr_list *
get_linux_ipv6_addrs(void)364 get_linux_ipv6_addrs (void)
365 {
366     struct linux_ipv6_addr_list *lst = 0;
367     FILE *f;
368 
369     /* _PATH_PROCNET_IFINET6 */
370     f = fopen("/proc/net/if_inet6", "r");
371     if (f) {
372         char ifname[21];
373         unsigned int idx, pfxlen, scope, dadstat;
374         struct in6_addr a6;
375         struct linux_ipv6_addr_list *nw;
376         int i;
377         unsigned int addrbyte[16];
378 
379         set_cloexec_file(f);
380         while (fscanf(f,
381                       "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x"
382                       " %2x %2x %2x %2x %20s\n",
383                       &addrbyte[0], &addrbyte[1], &addrbyte[2], &addrbyte[3],
384                       &addrbyte[4], &addrbyte[5], &addrbyte[6], &addrbyte[7],
385                       &addrbyte[8], &addrbyte[9], &addrbyte[10], &addrbyte[11],
386                       &addrbyte[12], &addrbyte[13], &addrbyte[14],
387                       &addrbyte[15],
388                       &idx, &pfxlen, &scope, &dadstat, ifname) != EOF) {
389             for (i = 0; i < 16; i++)
390                 a6.s6_addr[i] = addrbyte[i];
391             if (scope != 0)
392                 continue;
393             nw = calloc (1, sizeof (struct linux_ipv6_addr_list));
394             if (nw == 0)
395                 continue;
396             nw->addr.sin6_addr = a6;
397             nw->addr.sin6_family = AF_INET6;
398             /* Ignore other fields, we don't actually use them here.  */
399             nw->next = lst;
400             lst = nw;
401         }
402         fclose (f);
403     }
404     return lst;
405 }
406 #endif
407 
408 /* Return value is errno if internal stuff failed, otherwise zero,
409    even in the case where a called function terminated the iteration.
410 
411    If one of the callback functions wants to pass back an error
412    indication, it should do it via some field pointed to by the DATA
413    argument.  */
414 
415 #ifdef HAVE_IFADDRS_H
416 
417 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))418 foreach_localaddr (/*@null@*/ void *data,
419                    int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
420                    /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
421                    /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
422                                               struct sockaddr *) /*@*/)
423 #if defined(DEBUG) || defined(TEST)
424 /*@modifies fileSystem@*/
425 #endif
426 {
427     struct ifaddrs *ifp_head, *ifp, *ifp2;
428     int match;
429 
430     if (getifaddrs (&ifp_head) < 0)
431         return errno;
432     for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
433 #ifdef DEBUG
434         printifaddr (ifp);
435 #endif
436         if ((ifp->ifa_flags & IFF_UP) == 0)
437             continue;
438         if (ifp->ifa_addr == NULL) {
439             /* Can't use an interface without an address.  Linux
440                apparently does this sometimes.  [RT ticket 1770 from
441                Maurice Massar, also Debian bug 206851, shows the
442                problem with a PPP link on a newer kernel than I'm
443                running.]
444 
445                Pretend it's not up, so the second pass will skip
446                it.  */
447             ifp->ifa_flags &= ~IFF_UP;
448             continue;
449         }
450 #if 0
451         if (is_loopback_address(ifp->ifa_addr)) {
452             /* Pretend it's not up, so the second pass will skip
453                it.  */
454             ifp->ifa_flags &= ~IFF_UP;
455             continue;
456         }
457 #endif
458         /* If this address is a duplicate, punt.  */
459         match = 0;
460         for (ifp2 = ifp_head; ifp2 && ifp2 != ifp; ifp2 = ifp2->ifa_next) {
461             if ((ifp2->ifa_flags & IFF_UP) == 0)
462                 continue;
463             if (addr_eq (ifp->ifa_addr, ifp2->ifa_addr)) {
464                 match = 1;
465                 ifp->ifa_flags &= ~IFF_UP;
466                 break;
467             }
468         }
469         if (match)
470             continue;
471         if ((*pass1fn) (data, ifp->ifa_addr))
472             goto punt;
473     }
474     if (betweenfn && (*betweenfn)(data))
475         goto punt;
476     if (pass2fn)
477         for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
478             if (ifp->ifa_flags & IFF_UP)
479                 if ((*pass2fn) (data, ifp->ifa_addr))
480                     goto punt;
481         }
482 punt:
483     freeifaddrs (ifp_head);
484     return 0;
485 }
486 
487 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
488 
489 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))490 foreach_localaddr (/*@null@*/ void *data,
491                    int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
492                    /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
493                    /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
494                                               struct sockaddr *) /*@*/)
495 #if defined(DEBUG) || defined(TEST)
496 /*@modifies fileSystem@*/
497 #endif
498 {
499     /* Okay, this is kind of odd.  We have to use each of the address
500        families we care about, because with an AF_INET socket, extra
501        interfaces like hme0:1 that have only AF_INET6 addresses will
502        cause errors.  Similarly, if hme0 has more AF_INET addresses
503        than AF_INET6 addresses, we won't be able to retrieve all of
504        the AF_INET addresses if we use an AF_INET6 socket.  Since
505        neither family is guaranteed to have the greater number of
506        addresses, we should use both.
507 
508        If it weren't for this little quirk, we could use one socket of
509        any type, and ask for addresses of all types.  At least, it
510        seems to work that way.  */
511 
512     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
513 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
514     struct {
515         int af;
516         int sock;
517         void *buf;
518         size_t buf_size;
519         struct lifnum lifnum;
520     } afp[N_AFS];
521     int code, i, j;
522     int retval = 0, afidx;
523     krb5_error_code sock_err = 0;
524     struct lifreq *lifr, lifreq, *lifr2;
525 
526 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
527 #define P (afp[afidx])
528 
529     /* init */
530     FOREACH_AF () {
531         P.af = afs[afidx];
532         P.sock = -1;
533         P.buf = 0;
534     }
535 
536     /* first pass: get raw data, discard uninteresting addresses, callback */
537     FOREACH_AF () {
538         Tprintf (("trying af %d...\n", P.af));
539         P.sock = socket (P.af, USE_TYPE, USE_PROTO);
540         if (P.sock < 0) {
541             sock_err = SOCKET_ERROR;
542             Tperror ("socket");
543             continue;
544         }
545         set_cloexec_fd(P.sock);
546 
547         P.lifnum.lifn_family = P.af;
548         P.lifnum.lifn_flags = 0;
549         P.lifnum.lifn_count = 0;
550         code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum);
551         if (code) {
552             Tperror ("ioctl(SIOCGLIFNUM)");
553             retval = errno;
554             goto punt;
555         }
556 
557         P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2;
558         P.buf = malloc (P.buf_size);
559         if (P.buf == NULL) {
560             retval = ENOMEM;
561             goto punt;
562         }
563 
564         code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
565         if (code < 0) {
566             retval = errno;
567             goto punt;
568         }
569 
570         for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
571             lifr = (struct lifreq *)((caddr_t) P.buf+i);
572 
573             strncpy(lifreq.lifr_name, lifr->lifr_name,
574                     sizeof (lifreq.lifr_name));
575             Tprintf (("interface %s\n", lifreq.lifr_name));
576             /*@-moduncon@*/ /* ioctl unknown to lclint */
577             if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
578                 Tperror ("ioctl(SIOCGLIFFLAGS)");
579             skip:
580                 /* mark for next pass */
581                 lifr->lifr_name[0] = '\0';
582                 continue;
583             }
584             /*@=moduncon@*/
585 
586 #if 0
587             /* None of the current callers want loopback addresses.  */
588             if (is_loopback_address((struct sockaddr *)&lifr->lifr_addr)) {
589                 Tprintf (("  loopback\n"));
590                 goto skip;
591             }
592 #endif
593             /* Ignore interfaces that are down.  */
594             if ((lifreq.lifr_flags & IFF_UP) == 0) {
595                 Tprintf (("  down\n"));
596                 goto skip;
597             }
598 
599             /* Make sure we didn't process this address already.  */
600             for (j = 0; j < i; j += sizeof (*lifr2)) {
601                 lifr2 = (struct lifreq *)((caddr_t) P.buf+j);
602                 if (lifr2->lifr_name[0] == '\0')
603                     continue;
604                 if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family
605                     /* Compare address info.  If this isn't good enough --
606                        i.e., if random padding bytes turn out to differ
607                        when the addresses are the same -- then we'll have
608                        to do it on a per address family basis.  */
609                     && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr,
610                                 sizeof (*lifr))) {
611                     Tprintf (("  duplicate addr\n"));
612                     goto skip;
613                 }
614             }
615 
616             /*@-moduncon@*/
617             if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
618                 goto punt;
619             /*@=moduncon@*/
620         }
621     }
622 
623     /* Did we actually get any working sockets?  */
624     FOREACH_AF ()
625         if (P.sock != -1)
626             goto have_working_socket;
627     retval = sock_err;
628     goto punt;
629 have_working_socket:
630 
631     /*@-moduncon@*/
632     if (betweenfn != NULL && (*betweenfn)(data))
633         goto punt;
634     /*@=moduncon@*/
635 
636     if (pass2fn)
637         FOREACH_AF ()
638             if (P.sock >= 0) {
639                 for (i = 0; i + sizeof (*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
640                     lifr = (struct lifreq *)((caddr_t) P.buf+i);
641 
642                     if (lifr->lifr_name[0] == '\0')
643                         /* Marked in first pass to be ignored.  */
644                         continue;
645 
646                     /*@-moduncon@*/
647                     if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
648                         goto punt;
649                     /*@=moduncon@*/
650                 }
651             }
652 punt:
653     FOREACH_AF () {
654         /*@-moduncon@*/
655         closesocket(P.sock);
656         /*@=moduncon@*/
657         free (P.buf);
658     }
659 
660     return retval;
661 }
662 
663 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0 /* HP-UX 11 support being debugged */
664 
665 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))666 foreach_localaddr (/*@null@*/ void *data,
667                    int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
668                    /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
669                    /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
670                                               struct sockaddr *) /*@*/)
671 #if defined(DEBUG) || defined(TEST)
672 /*@modifies fileSystem@*/
673 #endif
674 {
675     /* Okay, this is kind of odd.  We have to use each of the address
676        families we care about, because with an AF_INET socket, extra
677        interfaces like hme0:1 that have only AF_INET6 addresses will
678        cause errors.  Similarly, if hme0 has more AF_INET addresses
679        than AF_INET6 addresses, we won't be able to retrieve all of
680        the AF_INET addresses if we use an AF_INET6 socket.  Since
681        neither family is guaranteed to have the greater number of
682        addresses, we should use both.
683 
684        If it weren't for this little quirk, we could use one socket of
685        any type, and ask for addresses of all types.  At least, it
686        seems to work that way.  */
687 
688     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
689 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
690     struct {
691         int af;
692         int sock;
693         void *buf;
694         size_t buf_size;
695         int if_num;
696     } afp[N_AFS];
697     int code, i, j;
698     int retval = 0, afidx;
699     krb5_error_code sock_err = 0;
700     struct if_laddrreq *lifr, lifreq, *lifr2;
701 
702 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
703 #define P (afp[afidx])
704 
705     /* init */
706     FOREACH_AF () {
707         P.af = afs[afidx];
708         P.sock = -1;
709         P.buf = 0;
710     }
711 
712     /* first pass: get raw data, discard uninteresting addresses, callback */
713     FOREACH_AF () {
714         Tprintf (("trying af %d...\n", P.af));
715         P.sock = socket (P.af, USE_TYPE, USE_PROTO);
716         if (P.sock < 0) {
717             sock_err = SOCKET_ERROR;
718             Tperror ("socket");
719             continue;
720         }
721         set_cloexec_fd(P.sock);
722 
723         code = ioctl (P.sock, SIOCGLIFNUM, &P.if_num);
724         if (code) {
725             Tperror ("ioctl(SIOCGLIFNUM)");
726             retval = errno;
727             goto punt;
728         }
729 
730         P.buf_size = P.if_num * sizeof (struct if_laddrreq) * 2;
731         P.buf = malloc (P.buf_size);
732         if (P.buf == NULL) {
733             retval = ENOMEM;
734             goto punt;
735         }
736 
737         code = get_if_laddrconf (P.af, P.sock, &P.buf_size, P.buf);
738         if (code < 0) {
739             retval = errno;
740             goto punt;
741         }
742 
743         for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
744             lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
745 
746             strncpy(lifreq.iflr_name, lifr->iflr_name,
747                     sizeof (lifreq.iflr_name));
748             Tprintf (("interface %s\n", lifreq.iflr_name));
749             /*@-moduncon@*/ /* ioctl unknown to lclint */
750             if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
751                 Tperror ("ioctl(SIOCGLIFFLAGS)");
752             skip:
753                 /* mark for next pass */
754                 lifr->iflr_name[0] = '\0';
755                 continue;
756             }
757             /*@=moduncon@*/
758 
759 #if 0
760             /* None of the current callers want loopback addresses.  */
761             if (is_loopback_address(&lifr->iflr_addr)) {
762                 Tprintf (("  loopback\n"));
763                 goto skip;
764             }
765 #endif
766             /* Ignore interfaces that are down.  */
767             if ((lifreq.iflr_flags & IFF_UP) == 0) {
768                 Tprintf (("  down\n"));
769                 goto skip;
770             }
771 
772             /* Make sure we didn't process this address already.  */
773             for (j = 0; j < i; j += sizeof (*lifr2)) {
774                 lifr2 = (struct if_laddrreq *)((caddr_t) P.buf+j);
775                 if (lifr2->iflr_name[0] == '\0')
776                     continue;
777                 if (lifr2->iflr_addr.sa_family == lifr->iflr_addr.sa_family
778                     /* Compare address info.  If this isn't good enough --
779                        i.e., if random padding bytes turn out to differ
780                        when the addresses are the same -- then we'll have
781                        to do it on a per address family basis.  */
782                     && !memcmp (&lifr2->iflr_addr, &lifr->iflr_addr,
783                                 sizeof (*lifr))) {
784                     Tprintf (("  duplicate addr\n"));
785                     goto skip;
786                 }
787             }
788 
789             /*@-moduncon@*/
790             if ((*pass1fn) (data, ss2sa (&lifr->iflr_addr)))
791                 goto punt;
792             /*@=moduncon@*/
793         }
794     }
795 
796     /* Did we actually get any working sockets?  */
797     FOREACH_AF ()
798         if (P.sock != -1)
799             goto have_working_socket;
800     retval = sock_err;
801     goto punt;
802 have_working_socket:
803 
804     /*@-moduncon@*/
805     if (betweenfn != NULL && (*betweenfn)(data))
806         goto punt;
807     /*@=moduncon@*/
808 
809     if (pass2fn)
810         FOREACH_AF ()
811             if (P.sock >= 0) {
812                 for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
813                     lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
814 
815                     if (lifr->iflr_name[0] == '\0')
816                         /* Marked in first pass to be ignored.  */
817                         continue;
818 
819                     /*@-moduncon@*/
820                     if ((*pass2fn) (data, ss2sa (&lifr->iflr_addr)))
821                         goto punt;
822                     /*@=moduncon@*/
823                 }
824             }
825 punt:
826     FOREACH_AF () {
827         /*@-moduncon@*/
828         closesocket(P.sock);
829         /*@=moduncon@*/
830         free (P.buf);
831     }
832 
833     return retval;
834 }
835 
836 #else /* not defined (SIOCGLIFNUM) */
837 
838 #define SLOP (sizeof (struct ifreq) + 128)
839 
840 static int
get_ifreq_array(char ** bufp,size_t * np,int s)841 get_ifreq_array(char **bufp, size_t *np, int s)
842 {
843     int code;
844     int est_if_count = 8;
845     size_t est_ifreq_size;
846     char *buf = 0;
847     size_t current_buf_size = 0, size, n;
848 #ifdef SIOCGSIZIFCONF
849     int ifconfsize = -1;
850 #endif
851 #ifdef SIOCGIFNUM
852     int numifs = -1;
853 #endif
854 
855     *bufp = NULL;
856     *np = 0;
857 
858     /* At least on NetBSD, an ifreq can hold an IPv4 address, but
859        isn't big enough for an IPv6 or ethernet address.  So add a
860        little more space.  */
861     est_ifreq_size = sizeof (struct ifreq) + 8;
862 #ifdef SIOCGSIZIFCONF
863     code = ioctl (s, SIOCGSIZIFCONF, &ifconfsize);
864     if (!code) {
865         current_buf_size = ifconfsize;
866         est_if_count = ifconfsize / est_ifreq_size;
867     }
868 #elif defined (SIOCGIFNUM)
869     code = ioctl (s, SIOCGIFNUM, &numifs);
870     if (!code && numifs > 0)
871         est_if_count = numifs;
872 #endif
873     if (current_buf_size == 0)
874         current_buf_size = est_ifreq_size * est_if_count + SLOP;
875     buf = malloc (current_buf_size);
876     if (buf == NULL)
877         return ENOMEM;
878 
879 ask_again:
880     size = current_buf_size;
881     code = get_ifconf (s, &size, buf);
882     if (code < 0) {
883         code = errno;
884         free (buf);
885         return code;
886     }
887     /* Test that the buffer was big enough that another ifreq could've
888        fit easily, if the OS wanted to provide one.  That seems to be
889        the only indication we get, complicated by the fact that the
890        associated address may make the required storage a little
891        bigger than the size of an ifreq.  */
892     if (current_buf_size - size < SLOP
893 #ifdef SIOCGSIZIFCONF
894         /* Unless we hear SIOCGSIZIFCONF is broken somewhere, let's
895            trust the value it returns.  */
896         && ifconfsize <= 0
897 #elif defined (SIOCGIFNUM)
898         && numifs <= 0
899 #endif
900         /* And we need *some* sort of bounds.  */
901         && current_buf_size <= 100000
902     ) {
903         size_t new_size;
904 
905         est_if_count *= 2;
906         new_size = est_ifreq_size * est_if_count + SLOP;
907         buf = grow_or_free (buf, new_size);
908         if (buf == 0)
909             return ENOMEM;
910         current_buf_size = new_size;
911         goto ask_again;
912     }
913 
914     n = size;
915     if (n > current_buf_size)
916         n = current_buf_size;
917 
918     *bufp = buf;
919     *np = n;
920     return 0;
921 }
922 
923 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))924 foreach_localaddr (/*@null@*/ void *data,
925                    int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
926                    /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
927                    /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
928                                               struct sockaddr *) /*@*/)
929 #if defined(DEBUG) || defined(TEST)
930 /*@modifies fileSystem@*/
931 #endif
932 {
933     struct ifreq *ifr, ifreq, *ifr2;
934     int s;
935     char *buf = 0;
936     size_t n, i, j;
937     int retval = 0;
938 #ifdef LINUX_IPV6_HACK
939     struct linux_ipv6_addr_list *linux_ipv6_addrs = get_linux_ipv6_addrs ();
940     struct linux_ipv6_addr_list *lx_v6;
941 #endif
942 
943     s = socket (USE_AF, USE_TYPE, USE_PROTO);
944     if (s < 0)
945         return SOCKET_ERRNO;
946     set_cloexec_fd(s);
947 
948     retval = get_ifreq_array(&buf, &n, s);
949     if (retval) {
950         /*@-moduncon@*/ /* close() unknown to lclint */
951         closesocket(s);
952         /*@=moduncon@*/
953         return retval;
954     }
955 
956     /* Note: Apparently some systems put the size (used or wanted?)
957        into the start of the buffer, just none that I'm actually
958        using.  Fix this when there's such a test system available.
959        The Samba mailing list archives mention that NTP looks for the
960        size on these systems: *-fujitsu-uxp* *-ncr-sysv4*
961        *-univel-sysv*.  */
962     for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
963         ifr = (struct ifreq *)((caddr_t) buf+i);
964         /* In case ifreq_size is more than sizeof().  */
965         if (i + ifreq_size(*ifr) > n)
966             break;
967 
968         strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
969         Tprintf (("interface %s\n", ifreq.ifr_name));
970         /*@-moduncon@*/ /* ioctl unknown to lclint */
971         if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
972         skip:
973             /* mark for next pass */
974             ifr->ifr_name[0] = '\0';
975             continue;
976         }
977         /*@=moduncon@*/
978 
979 #if 0
980         /* None of the current callers want loopback addresses.  */
981         if (is_loopback_address(&ifreq.ifr_addr)) {
982             Tprintf (("  loopback\n"));
983             goto skip;
984         }
985 #endif
986         /* Ignore interfaces that are down.  */
987         if ((ifreq.ifr_flags & IFF_UP) == 0) {
988             Tprintf (("  down\n"));
989             goto skip;
990         }
991 
992         /* Make sure we didn't process this address already.  */
993         for (j = 0; j < i; j += ifreq_size(*ifr2)) {
994             ifr2 = (struct ifreq *)((caddr_t) buf+j);
995             if (ifr2->ifr_name[0] == '\0')
996                 continue;
997             if (ifr2->ifr_addr.sa_family == ifr->ifr_addr.sa_family
998                 && ifreq_size (*ifr) == ifreq_size (*ifr2)
999                 /* Compare address info.  If this isn't good enough --
1000                    i.e., if random padding bytes turn out to differ
1001                    when the addresses are the same -- then we'll have
1002                    to do it on a per address family basis.  */
1003                 && !memcmp (&ifr2->ifr_addr.sa_data, &ifr->ifr_addr.sa_data,
1004                             (ifreq_size (*ifr)
1005                              - offsetof (struct ifreq, ifr_addr.sa_data)))) {
1006                 Tprintf (("  duplicate addr\n"));
1007                 goto skip;
1008             }
1009         }
1010 
1011         /*@-moduncon@*/
1012         if ((*pass1fn) (data, &ifr->ifr_addr))
1013             goto punt;
1014         /*@=moduncon@*/
1015     }
1016 
1017 #ifdef LINUX_IPV6_HACK
1018     for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1019         if ((*pass1fn) (data, (struct sockaddr *) &lx_v6->addr))
1020             goto punt;
1021 #endif
1022 
1023     /*@-moduncon@*/
1024     if (betweenfn != NULL && (*betweenfn)(data))
1025         goto punt;
1026     /*@=moduncon@*/
1027 
1028     if (pass2fn) {
1029         for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
1030             ifr = (struct ifreq *)((caddr_t) buf+i);
1031 
1032             if (ifr->ifr_name[0] == '\0')
1033                 /* Marked in first pass to be ignored.  */
1034                 continue;
1035 
1036             /*@-moduncon@*/
1037             if ((*pass2fn) (data, &ifr->ifr_addr))
1038                 goto punt;
1039             /*@=moduncon@*/
1040         }
1041 #ifdef LINUX_IPV6_HACK
1042         for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1043             if ((*pass2fn) (data, (struct sockaddr *) &lx_v6->addr))
1044                 goto punt;
1045 #endif
1046     }
1047 punt:
1048     /*@-moduncon@*/
1049     closesocket(s);
1050     /*@=moduncon@*/
1051     free (buf);
1052 #ifdef LINUX_IPV6_HACK
1053     while (linux_ipv6_addrs) {
1054         lx_v6 = linux_ipv6_addrs->next;
1055         free (linux_ipv6_addrs);
1056         linux_ipv6_addrs = lx_v6;
1057     }
1058 #endif
1059 
1060     return retval;
1061 }
1062 
1063 #endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */
1064 
1065 static krb5_error_code
1066 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile);
1067 
1068 #ifdef TEST
1069 
print_addr(void * dataptr,struct sockaddr * sa)1070 static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa)
1071 /*@modifies fileSystem@*/
1072 {
1073     char hostbuf[NI_MAXHOST];
1074     int err;
1075     socklen_t len;
1076 
1077     printf ("  --> family %2d ", sa->sa_family);
1078     len = sa_socklen (sa);
1079     err = getnameinfo (sa, len, hostbuf, (socklen_t) sizeof (hostbuf),
1080                        (char *) NULL, 0, NI_NUMERICHOST);
1081     if (err) {
1082         int e = errno;
1083         printf ("<getnameinfo error %d: %s>\n", err, gai_strerror (err));
1084         if (err == EAI_SYSTEM)
1085             printf ("\t\t<errno is %d: %s>\n", e, strerror(e));
1086     } else
1087         printf ("addr %s\n", hostbuf);
1088     return 0;
1089 }
1090 
main(void)1091 int main (void)
1092 {
1093     int r;
1094 
1095     (void) setvbuf (stdout, (char *)NULL, _IONBF, 0);
1096     r = foreach_localaddr (0, print_addr, NULL, NULL);
1097     printf ("return value = %d\n", r);
1098     return 0;
1099 }
1100 
1101 #else /* not TESTing */
1102 
1103 struct localaddr_data {
1104     size_t count, cur_idx, cur_size;
1105     int mem_err;
1106     krb5_address **addr_temp;
1107 };
1108 
1109 static int
count_addrs(void * P_data,struct sockaddr * a)1110 count_addrs (void *P_data, struct sockaddr *a)
1111 /*@*/
1112 {
1113     struct localaddr_data *data = P_data;
1114     switch (a->sa_family) {
1115     case AF_INET:
1116     case AF_INET6:
1117 #ifdef KRB5_USE_NS
1118     case AF_XNS:
1119 #endif
1120         data->count++;
1121         break;
1122     default:
1123         break;
1124     }
1125     return 0;
1126 }
1127 
1128 static int
allocate(void * P_data)1129 allocate (void *P_data)
1130 /*@*/
1131 {
1132     struct localaddr_data *data = P_data;
1133     size_t i;
1134     void *n;
1135 
1136     n = realloc (data->addr_temp,
1137                  (1 + data->count + data->cur_idx) * sizeof (krb5_address *));
1138     if (n == 0) {
1139         data->mem_err++;
1140         return 1;
1141     }
1142     data->addr_temp = n;
1143     data->cur_size = 1 + data->count + data->cur_idx;
1144     for (i = data->cur_idx; i <= data->count + data->cur_idx; i++)
1145         data->addr_temp[i] = 0;
1146     return 0;
1147 }
1148 
1149 static /*@null@*/ krb5_address *
make_addr(int type,size_t length,const void * contents)1150 make_addr (int type, size_t length, const void *contents)
1151 /*@*/
1152 {
1153     krb5_address *a;
1154     void *data;
1155 
1156     data = malloc (length);
1157     if (data == NULL)
1158         return NULL;
1159     a = malloc (sizeof (krb5_address));
1160     if (a == NULL) {
1161         free (data);
1162         return NULL;
1163     }
1164     memcpy (data, contents, length);
1165     a->magic = KV5M_ADDRESS;
1166     a->addrtype = type;
1167     a->length = length;
1168     a->contents = data;
1169     return a;
1170 }
1171 
1172 static int
add_addr(void * P_data,struct sockaddr * a)1173 add_addr (void *P_data, struct sockaddr *a)
1174 /*@modifies *P_data@*/
1175 {
1176     struct localaddr_data *data = P_data;
1177     /*@null@*/ krb5_address *address = 0;
1178 
1179     switch (a->sa_family) {
1180 #ifdef HAVE_NETINET_IN_H
1181     case AF_INET:
1182         address = make_addr (ADDRTYPE_INET, sizeof (struct in_addr),
1183                              &sa2sin(a)->sin_addr);
1184         if (address == NULL)
1185             data->mem_err++;
1186         break;
1187 
1188     case AF_INET6:
1189     {
1190         const struct sockaddr_in6 *in = sa2sin6(a);
1191 
1192         if (IN6_IS_ADDR_LINKLOCAL (&in->sin6_addr))
1193             break;
1194 
1195         address = make_addr (ADDRTYPE_INET6, sizeof (struct in6_addr),
1196                              &in->sin6_addr);
1197         if (address == NULL)
1198             data->mem_err++;
1199         break;
1200     }
1201 #endif /* netinet/in.h */
1202 
1203 #ifdef KRB5_USE_NS
1204     case AF_XNS:
1205         address = make_addr (ADDRTYPE_XNS, sizeof (struct ns_addr),
1206                              &((const struct sockaddr_ns *)a)->sns_addr);
1207         if (address == NULL)
1208             data->mem_err++;
1209         break;
1210 #endif
1211 
1212 #ifdef AF_LINK
1213         /* Some BSD-based systems (e.g. NetBSD 1.5) and AIX will
1214            include the ethernet address, but we don't want that, at
1215            least for now.  */
1216     case AF_LINK:
1217         break;
1218 #endif
1219         /*
1220          * Add more address families here..
1221          */
1222     default:
1223         break;
1224     }
1225 #ifdef __LCLINT__
1226     /* Redundant but unconditional store un-confuses lclint.  */
1227     data->addr_temp[data->cur_idx] = address;
1228 #endif
1229     if (address) {
1230         data->addr_temp[data->cur_idx++] = address;
1231     }
1232 
1233     return data->mem_err;
1234 }
1235 
1236 static krb5_error_code
krb5_os_localaddr_profile(krb5_context context,struct localaddr_data * datap)1237 krb5_os_localaddr_profile (krb5_context context, struct localaddr_data *datap)
1238 {
1239     krb5_error_code err;
1240     static const char *const profile_name[] = {
1241         KRB5_CONF_LIBDEFAULTS, KRB5_CONF_EXTRA_ADDRESSES, 0
1242     };
1243     char **values;
1244     char **iter;
1245     krb5_address **newaddrs;
1246 
1247 #ifdef DEBUG
1248     fprintf (stderr, "looking up extra_addresses foo\n");
1249 #endif
1250 
1251     err = profile_get_values (context->profile, profile_name, &values);
1252     /* Ignore all errors for now?  */
1253     if (err)
1254         return 0;
1255 
1256     for (iter = values; *iter; iter++) {
1257         char *cp = *iter, *next, *current;
1258         size_t i, count;
1259 
1260 #ifdef DEBUG
1261         fprintf (stderr, "  found line: '%s'\n", cp);
1262 #endif
1263 
1264         for (cp = *iter, next = 0; *cp; cp = next) {
1265             while (isspace ((int) *cp) || *cp == ',')
1266                 cp++;
1267             if (*cp == 0)
1268                 break;
1269             /* Start of an address.  */
1270 #ifdef DEBUG
1271             fprintf (stderr, "    addr found in '%s'\n", cp);
1272 #endif
1273             current = cp;
1274             while (*cp != 0 && !isspace((int) *cp) && *cp != ',')
1275                 cp++;
1276             if (*cp != 0) {
1277                 next = cp + 1;
1278                 *cp = 0;
1279             } else
1280                 next = cp;
1281             /* Got a single address, process it.  */
1282 #ifdef DEBUG
1283             fprintf (stderr, "    processing '%s'\n", current);
1284 #endif
1285             newaddrs = 0;
1286             err = k5_os_hostaddr (context, current, &newaddrs);
1287             if (err)
1288                 continue;
1289             for (i = 0; newaddrs[i]; i++) {
1290 #ifdef DEBUG
1291                 fprintf (stderr, "    %d: family %d", i,
1292                          newaddrs[i]->addrtype);
1293                 fprintf (stderr, "\n");
1294 #endif
1295             }
1296             count = i;
1297 #ifdef DEBUG
1298             fprintf (stderr, "    %d addresses\n", count);
1299 #endif
1300             if (datap->cur_idx + count >= datap->cur_size) {
1301                 krb5_address **bigger;
1302                 bigger = realloc (datap->addr_temp,
1303                                   sizeof (krb5_address *) * (datap->cur_idx + count));
1304                 if (bigger) {
1305                     datap->addr_temp = bigger;
1306                     datap->cur_size = datap->cur_idx + count;
1307                 }
1308             }
1309             for (i = 0; i < count; i++) {
1310                 if (datap->cur_idx < datap->cur_size)
1311                     datap->addr_temp[datap->cur_idx++] = newaddrs[i];
1312                 else
1313                     free (newaddrs[i]->contents), free (newaddrs[i]);
1314             }
1315             free (newaddrs);
1316         }
1317     }
1318     return 0;
1319 }
1320 
1321 krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)1322 krb5_os_localaddr(krb5_context context, krb5_address ***addr)
1323 {
1324     return get_localaddrs(context, addr, 1);
1325 }
1326 
1327 static krb5_error_code
get_localaddrs(krb5_context context,krb5_address *** addr,int use_profile)1328 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile)
1329 {
1330     struct localaddr_data data = { 0 };
1331     int r;
1332 
1333     /* Ignore errors for now. */
1334     if (use_profile)
1335         (void)krb5_os_localaddr_profile (context, &data);
1336 
1337     r = foreach_localaddr (&data, count_addrs, allocate, add_addr);
1338     if (r != 0) {
1339         size_t i;
1340         if (data.addr_temp) {
1341             for (i = 0; i < data.count; i++)
1342                 free (data.addr_temp[i]);
1343             free (data.addr_temp);
1344         }
1345         if (data.mem_err)
1346             return ENOMEM;
1347         else
1348             return r;
1349     }
1350 
1351     data.cur_idx++; /* null termination */
1352     if (data.mem_err)
1353         return ENOMEM;
1354     else if (data.cur_idx == data.count)
1355         *addr = data.addr_temp;
1356     else {
1357         /* This can easily happen if we have IPv6 link-local
1358            addresses.  Just shorten the array.  */
1359         *addr = (krb5_address **) realloc (data.addr_temp,
1360                                            (sizeof (krb5_address *)
1361                                             * data.cur_idx));
1362         if (*addr == 0)
1363             /* Okay, shortening failed, but the original should still
1364                be intact.  */
1365             *addr = data.addr_temp;
1366     }
1367 
1368 #ifdef DEBUG
1369     {
1370         size_t j;
1371         fprintf (stderr, "addresses:\n");
1372         for (j = 0; addr[0][j]; j++) {
1373             struct sockaddr_storage ss;
1374             int err2;
1375             char namebuf[NI_MAXHOST];
1376             void *addrp = 0;
1377 
1378             fprintf (stderr, "%2d: ", j);
1379             fprintf (stderr, "addrtype %2d, length %2d", addr[0][j]->addrtype,
1380                      addr[0][j]->length);
1381             memset (&ss, 0, sizeof (ss));
1382             switch (addr[0][j]->addrtype) {
1383             case ADDRTYPE_INET:
1384             {
1385                 struct sockaddr_in *sinp = ss2sin (&ss);
1386                 sinp->sin_family = AF_INET;
1387                 addrp = &sinp->sin_addr;
1388                 break;
1389             }
1390             case ADDRTYPE_INET6:
1391             {
1392                 struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
1393                 sin6p->sin6_family = AF_INET6;
1394                 addrp = &sin6p->sin6_addr;
1395                 break;
1396             }
1397             default:
1398                 ss2sa(&ss)->sa_family = 0;
1399                 break;
1400             }
1401             if (addrp)
1402                 memcpy (addrp, addr[0][j]->contents, addr[0][j]->length);
1403             err2 = getnameinfo (ss2sa(&ss), sa_socklen (ss2sa (&ss)),
1404                                 namebuf, sizeof (namebuf), 0, 0,
1405                                 NI_NUMERICHOST);
1406             if (err2 == 0)
1407                 fprintf (stderr, ": addr %s\n", namebuf);
1408             else
1409                 fprintf (stderr, ": getnameinfo error %d\n", err2);
1410         }
1411     }
1412 #endif
1413 
1414     return 0;
1415 }
1416 
1417 #endif /* not TESTing */
1418 
1419 #else /* Windows/Mac version */
1420 
1421 /*
1422  * Hold on to your lunch!  Backup kludge method of obtaining your
1423  * local IP address, courtesy of Windows Socket Network Programming,
1424  * by Robert Quinn
1425  */
1426 #if defined(_WIN32)
local_addr_fallback_kludge(void)1427 static struct hostent *local_addr_fallback_kludge(void)
1428 {
1429     static struct hostent   host;
1430     static SOCKADDR_IN      addr;
1431     static char *           ip_ptrs[2];
1432     SOCKET                  sock;
1433     int                     size = sizeof(SOCKADDR);
1434     int                     err;
1435 
1436     sock = socket(AF_INET, SOCK_DGRAM, 0);
1437     if (sock == INVALID_SOCKET)
1438         return NULL;
1439     set_cloexec_fd(sock);
1440 
1441     /* connect to arbitrary port and address (NOT loopback) */
1442     addr.sin_family = AF_INET;
1443     addr.sin_port = htons(IPPORT_ECHO);
1444     addr.sin_addr.s_addr = inet_addr("204.137.220.51");
1445 
1446     err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
1447     if (err == SOCKET_ERROR)
1448         return NULL;
1449 
1450     err = getsockname(sock, (LPSOCKADDR) &addr, &size);
1451     if (err == SOCKET_ERROR)
1452         return NULL;
1453 
1454     closesocket(sock);
1455 
1456     host.h_name = 0;
1457     host.h_aliases = 0;
1458     host.h_addrtype = AF_INET;
1459     host.h_length = 4;
1460     host.h_addr_list = ip_ptrs;
1461     ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
1462     ip_ptrs[1] = NULL;
1463 
1464     return &host;
1465 }
1466 #endif
1467 
1468 /* No ioctls in winsock so we just assume there is only one networking
1469  * card per machine, so gethostent is good enough.
1470  */
1471 krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)1472 krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
1473     char host[64];                              /* Name of local machine */
1474     struct hostent *hostrec;
1475     size_t count, i;
1476     int err;
1477     krb5_address ** paddr;
1478 
1479     *addr = 0;
1480     paddr = 0;
1481     err = 0;
1482 
1483     if (gethostname (host, sizeof(host))) {
1484         err = SOCKET_ERRNO;
1485     }
1486 
1487     if (!err) {
1488         hostrec = gethostbyname (host);
1489         if (hostrec == NULL) {
1490             err = SOCKET_ERRNO;
1491         }
1492     }
1493 
1494     if (err) {
1495         hostrec = local_addr_fallback_kludge();
1496         if (!hostrec)
1497             return err;
1498         else
1499             err = 0;  /* otherwise we will die at cleanup */
1500     }
1501 
1502     for (count = 0; hostrec->h_addr_list[count]; count++);
1503 
1504 
1505     paddr = (krb5_address **)calloc(count+1, sizeof(krb5_address *));
1506     if (!paddr) {
1507         err = ENOMEM;
1508         goto cleanup;
1509     }
1510 
1511     for (i = 0; i < count; i++) {
1512         paddr[i] = (krb5_address *)malloc(sizeof(krb5_address));
1513         if (paddr[i] == NULL) {
1514             err = ENOMEM;
1515             goto cleanup;
1516         }
1517 
1518         paddr[i]->magic = KV5M_ADDRESS;
1519         paddr[i]->addrtype = hostrec->h_addrtype;
1520         paddr[i]->length = hostrec->h_length;
1521         paddr[i]->contents = (unsigned char *)malloc(paddr[i]->length);
1522         if (!paddr[i]->contents) {
1523             err = ENOMEM;
1524             goto cleanup;
1525         }
1526         memcpy(paddr[i]->contents,
1527                hostrec->h_addr_list[i],
1528                paddr[i]->length);
1529     }
1530 
1531 cleanup:
1532     if (err) {
1533         if (paddr) {
1534             for (i = 0; i < count; i++)
1535             {
1536                 if (paddr[i]) {
1537                     if (paddr[i]->contents)
1538                         free(paddr[i]->contents);
1539                     free(paddr[i]);
1540                 }
1541             }
1542             free(paddr);
1543         }
1544     }
1545     else
1546         *addr = paddr;
1547 
1548     return(err);
1549 }
1550 #endif
1551