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