Delete definitions of unused macros NEED_SOCKETS and NEED_LOWLEVEL_IO
[krb5.git] / src / lib / krb5 / os / locate_kdc.c
1 /*
2  * lib/krb5/os/locate_kdc.c
3  *
4  * Copyright 1990,2000,2001,2002 by the Massachusetts Institute of Technology.
5  * 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  * get socket addresses for KDC.
28  */
29
30 #include "fake-addrinfo.h"
31 #include "k5-int.h"
32 #include "os-proto.h"
33 #include <stdio.h>
34 #ifdef KRB5_DNS_LOOKUP
35 #ifdef WSHELPER
36 #include <wshelper.h>
37 #else /* WSHELPER */
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
41 #include <resolv.h>
42 #include <netdb.h>
43 #endif /* WSHELPER */
44 #ifndef T_SRV
45 #define T_SRV 33
46 #endif /* T_SRV */
47
48 /* for old Unixes and friends ... */
49 #ifndef MAXHOSTNAMELEN
50 #define MAXHOSTNAMELEN 64
51 #endif
52
53 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
54
55 #if KRB5_DNS_LOOKUP_KDC
56 #define DEFAULT_LOOKUP_KDC 1
57 #else
58 #define DEFAULT_LOOKUP_KDC 0
59 #endif
60 #if KRB5_DNS_LOOKUP_REALM
61 #define DEFAULT_LOOKUP_REALM 1
62 #else
63 #define DEFAULT_LOOKUP_REALM 0
64 #endif
65
66 static int
67 maybe_use_dns (krb5_context context, const char *name, int defalt)
68 {
69     krb5_error_code code;
70     char * value = NULL;
71     int use_dns = 0;
72
73     code = profile_get_string(context->profile, "libdefaults",
74                               name, 0, 0, &value);
75     if (value == 0 && code == 0)
76         code = profile_get_string(context->profile, "libdefaults",
77                                   "dns_fallback", 0, 0, &value);
78     if (code)
79         return defalt;
80
81     if (value == 0)
82         return defalt;
83
84     use_dns = _krb5_conf_boolean(value);
85     profile_release_string(value);
86     return use_dns;
87 }
88
89 int
90 _krb5_use_dns_kdc(krb5_context context)
91 {
92     return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
93 }
94
95 int
96 _krb5_use_dns_realm(krb5_context context)
97 {
98     return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
99 }
100
101 #endif /* KRB5_DNS_LOOKUP */
102
103 static int get_port (const char *service, int stream, int defalt)
104 {
105 #if 0 /* Only used for "kerberos" and "kerberos-sec", and we want the
106          right port numbers even on the OSes that botch the entries in
107          /etc/services.  So don't bother with the lookup, except maybe
108          to produce a warning.  */
109     struct addrinfo hints = { 0 };
110     struct addrinfo *ai;
111     int err;
112
113     hints.ai_family = PF_INET;
114     hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
115     err = getaddrinfo (NULL, service, &hints, &ai);
116     if (err == 0 && ai != 0) {
117         if (ai->ai_addr->sa_family == AF_INET) {
118             int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
119             freeaddrinfo (ai);
120             return port;
121         }
122         freeaddrinfo (ai);
123     }
124 #endif
125     /* Any error - don't complain, just use default.  */
126     return htons (defalt);
127 }
128
129 int
130 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
131 {
132     int i;
133     int newspace = lp->space + nmore;
134     size_t newsize = newspace * sizeof (struct addrlist);
135     struct addrinfo **newaddrs;
136
137     /* NULL check a concession to SunOS4 compatibility for now; not
138        required for pure ANSI support.  */
139     if (lp->addrs)
140         newaddrs = realloc (lp->addrs, newsize);
141     else
142         newaddrs = malloc (newsize);
143
144     if (newaddrs == NULL)
145         return errno;
146     for (i = lp->space; i < newspace; i++)
147         newaddrs[i] = NULL;
148     lp->addrs = newaddrs;
149     lp->space = newspace;
150     return 0;
151 }
152 #define grow_list krb5int_grow_addrlist
153
154 /* Free up everything pointed to by the addrlist structure, but don't
155    free the structure itself.  */
156 void
157 krb5int_free_addrlist (struct addrlist *lp)
158 {
159     int i;
160     for (i = 0; i < lp->naddrs; i++)
161         freeaddrinfo (lp->addrs[i]);
162     free (lp->addrs);
163     lp->addrs = NULL;
164     lp->naddrs = lp->space = 0;
165 }
166 #define free_list krb5int_free_addrlist
167
168 static int translate_ai_error (int err)
169 {
170     switch (err) {
171     case 0:
172         return 0;
173     case EAI_BADFLAGS:
174     case EAI_FAMILY:
175     case EAI_SOCKTYPE:
176     case EAI_SERVICE:
177         /* All of these indicate bad inputs to getaddrinfo.  */
178         return EINVAL;
179     case EAI_AGAIN:
180         /* Translate to standard errno code.  */
181         return EAGAIN;
182     case EAI_MEMORY:
183         /* Translate to standard errno code.  */
184         return ENOMEM;
185 #ifdef EAI_ADDRFAMILY
186     case EAI_ADDRFAMILY:
187 #endif
188 #if EAI_NODATA != EAI_NONAME
189     case EAI_NODATA:
190 #endif
191     case EAI_NONAME:
192         /* Name not known or no address data, but no error.  Do
193            nothing more.  */
194         return 0;
195 #ifdef EAI_SYSTEM
196     case EAI_SYSTEM:
197         /* System error, obviously.  */
198         return errno;
199 #endif
200     default:
201         /* An error code we haven't handled?  */
202         return EINVAL;
203     }
204 }
205
206 #include <stdarg.h>
207 static inline void Tprintf(const char *fmt, ...)
208 {
209 #ifdef TEST
210     va_list ap;
211     va_start(ap, fmt);
212     vfprintf(stderr, fmt, ap);
213     va_end(ap);
214 #endif
215 }
216
217 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
218 {
219     int err;
220
221     switch (a->ai_socktype) {
222     case SOCK_DGRAM:
223         Tprintf("\tdgram\n");
224         break;
225     case SOCK_STREAM:
226         Tprintf("\tstream\n");
227         break;
228     case SOCK_RAW:
229         Tprintf("\traw\n");
230         break;
231     case 0:
232         break;
233     default:
234         Tprintf("\tsocket type %d\n", a->ai_socktype);
235         break;
236     }
237
238     if (lp->naddrs == lp->space) {
239         err = grow_list (lp, 1);
240         if (err) {
241             Tprintf ("grow_list failed %d\n", err);
242             return err;
243         }
244     }
245     lp->addrs[lp->naddrs++] = a;
246     a->ai_next = 0;
247     Tprintf ("count is now %d\n", lp->naddrs);
248     return 0;
249 }
250
251 #define add_host_to_list krb5int_add_host_to_list
252
253 int
254 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
255                           int port, int secport,
256                           int socktype, int family)
257 {
258     struct addrinfo *addrs, *a, *anext, hint;
259     int err;
260     char portbuf[10], secportbuf[10];
261
262     Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
263              hostname, ntohs (port), ntohs (secport),
264              family, socktype);
265
266     memset(&hint, 0, sizeof(hint));
267     hint.ai_family = family;
268     hint.ai_socktype = socktype;
269 #ifdef AI_NUMERICSERV
270     hint.ai_flags = AI_NUMERICSERV;
271 #endif
272     sprintf(portbuf, "%d", ntohs(port));
273     sprintf(secportbuf, "%d", ntohs(secport));
274     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
275     if (err) {
276         Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
277                  hostname, portbuf, err, gai_strerror (err));
278         return translate_ai_error (err);
279     }
280     anext = 0;
281     for (a = addrs; a != 0 && err == 0; a = anext) {
282         anext = a->ai_next;
283         err = add_addrinfo_to_list (lp, a);
284     }
285     if (err || secport == 0)
286         goto egress;
287     if (socktype == 0)
288         socktype = SOCK_DGRAM;
289     else if (socktype != SOCK_DGRAM)
290         goto egress;
291     hint.ai_family = AF_INET;
292     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
293     if (err) {
294         err = translate_ai_error (err);
295         goto egress;
296     }
297     for (a = addrs; a != 0 && err == 0; a = anext) {
298         anext = a->ai_next;
299         err = add_addrinfo_to_list (lp, a);
300     }
301 egress:
302     if (anext)
303         freeaddrinfo (anext);
304     return err;
305 }
306
307 /*
308  * returns count of number of addresses found
309  * if master is non-NULL, it is filled in with the index of
310  * the master kdc
311  */
312
313 static krb5_error_code
314 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
315                        const char * name, struct addrlist *addrlist,
316                        int get_masters, int socktype,
317                        int udpport, int sec_udpport, int family)
318 {
319     const char  *realm_srv_names[4];
320     char **masterlist, **hostlist, *host, *port, *cp;
321     krb5_error_code code;
322     int i, j, count, ismaster;
323
324     Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
325              realm->data, name, ntohs (udpport), ntohs (sec_udpport));
326
327     if ((host = malloc(realm->length + 1)) == NULL) 
328         return ENOMEM;
329
330     strncpy(host, realm->data, realm->length);
331     host[realm->length] = '\0';
332     hostlist = 0;
333
334     masterlist = NULL;
335
336     realm_srv_names[0] = "realms";
337     realm_srv_names[1] = host;
338     realm_srv_names[2] = name;
339     realm_srv_names[3] = 0;
340
341     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
342
343     if (code) {
344         Tprintf ("config file lookup failed: %s\n",
345                  error_message(code));
346         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
347             code = KRB5_REALM_UNKNOWN;
348         krb5_xfree(host);
349         return code;
350      }
351
352     count = 0;
353     while (hostlist && hostlist[count])
354             count++;
355     Tprintf ("found %d entries under 'kdc'\n", count);
356     
357     if (count == 0) {
358         profile_free_list(hostlist);
359         krb5_xfree(host);
360         addrlist->naddrs = 0;
361         return 0;
362     }
363     
364     if (get_masters) {
365         realm_srv_names[0] = "realms";
366         realm_srv_names[1] = host;
367         realm_srv_names[2] = "admin_server";
368         realm_srv_names[3] = 0;
369
370         code = profile_get_values(context->profile, realm_srv_names,
371                                   &masterlist);
372
373         krb5_xfree(host);
374
375         if (code == 0) {
376             for (i=0; masterlist[i]; i++) {
377                 host = masterlist[i];
378
379                 /*
380                  * Strip off excess whitespace
381                  */
382                 cp = strchr(host, ' ');
383                 if (cp)
384                     *cp = 0;
385                 cp = strchr(host, '\t');
386                 if (cp)
387                     *cp = 0;
388                 cp = strchr(host, ':');
389                 if (cp)
390                     *cp = 0;
391             }
392         }
393     } else {
394         krb5_xfree(host);
395     }
396
397     /* at this point, if master is non-NULL, then either the master kdc
398        is required, and there is one, or the master kdc is not required,
399        and there may or may not be one. */
400
401 #ifdef HAVE_NETINET_IN_H
402     if (sec_udpport)
403             count = count * 2;
404 #endif
405
406     for (i=0; hostlist[i]; i++) {
407         int p1, p2;
408
409         host = hostlist[i];
410         Tprintf ("entry %d is '%s'\n", i, host);
411         /*
412          * Strip off excess whitespace
413          */
414         cp = strchr(host, ' ');
415         if (cp)
416             *cp = 0;
417         cp = strchr(host, '\t');
418         if (cp)
419             *cp = 0;
420         port = strchr(host, ':');
421         if (port) {
422             *port = 0;
423             port++;
424         }
425
426         ismaster = 0;
427         if (masterlist) {
428             for (j=0; masterlist[j]; j++) {
429                 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
430                     ismaster = 1;
431                 }
432             }
433         }
434
435         if (get_masters && !ismaster)
436             continue;
437
438         if (port) {
439             unsigned long l;
440 #ifdef HAVE_STROUL
441             char *endptr;
442             l = strtoul (port, &endptr, 10);
443             if (endptr == NULL || *endptr != 0)
444                 return EINVAL;
445 #else
446             l = atoi (port);
447 #endif
448             /* L is unsigned, don't need to check <0.  */
449             if (l > 65535)
450                 return EINVAL;
451             p1 = htons (l);
452             p2 = 0;
453         } else {
454             p1 = udpport;
455             p2 = sec_udpport;
456         }
457
458         if (socktype != 0)
459             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
460                                      socktype, family);
461         else {
462             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
463                                      SOCK_DGRAM, family);
464             if (code == 0)
465                 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
466                                          SOCK_STREAM, family);
467         }
468         if (code) {
469             Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
470                      error_message (code));
471             if (hostlist)
472                 profile_free_list (hostlist);
473             if (masterlist)
474                 profile_free_list (masterlist);
475             return code;
476         }
477     }
478
479     if (hostlist)
480         profile_free_list(hostlist);
481     if (masterlist)
482         profile_free_list(masterlist);
483
484     return 0;
485 }
486
487 #ifdef TEST
488 static krb5_error_code
489 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
490                      const char *name, struct addrlist *al, int get_masters,
491                      int udpport, int sec_udpport)
492 {
493     krb5_error_code ret;
494
495     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
496                                   get_masters, 0, udpport, sec_udpport, 0);
497     if (ret)
498         return ret;
499     if (al->naddrs == 0)        /* Couldn't resolve any KDC names */
500         return KRB5_REALM_CANT_RESOLVE;
501     return 0;
502 }
503 #endif
504
505 #ifdef KRB5_DNS_LOOKUP
506 static krb5_error_code
507 krb5_locate_srv_dns_1 (const krb5_data *realm,
508                        const char *service,
509                        const char *protocol,
510                        struct addrlist *addrlist,
511                        int family)
512 {
513     struct srv_dns_entry *head = NULL;
514     struct srv_dns_entry *entry = NULL, *next;
515     krb5_error_code code = 0;
516
517     code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
518     if (code)
519         return 0;
520
521     /*
522      * Okay!  Now we've got a linked list of entries sorted by
523      * priority.  Start looking up A records and returning
524      * addresses.
525      */
526
527     if (head == NULL)
528         return 0;
529
530     /* Check for the "." case indicating no support.  */
531     if (head->next == 0 && head->host[0] == 0) {
532         free(head->host);
533         free(head);
534         return KRB5_ERR_NO_SERVICE;
535     }
536
537     Tprintf ("walking answer list:\n");
538     for (entry = head; entry != NULL; entry = next) {
539         Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
540         next = entry->next;
541         code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
542                                  (strcmp("_tcp", protocol)
543                                   ? SOCK_DGRAM
544                                   : SOCK_STREAM), family);
545         if (code) {
546             break;
547         }
548         if (entry == head) {
549             free(entry->host);
550             free(entry);
551             head = next;
552             entry = 0;
553         }
554     }
555     Tprintf ("[end]\n");
556
557     krb5int_free_srv_dns_data(head);
558     return code;
559 }
560 #endif
561
562 /*
563  * Wrapper function for the two backends
564  */
565
566 krb5_error_code
567 krb5int_locate_server (krb5_context context, const krb5_data *realm,
568                        struct addrlist *addrlist,
569                        int get_masters,
570                        const char *profname, const char *dnsname,
571                        int socktype,
572                        /* network order port numbers! */
573                        int dflport1, int dflport2,
574                        int family)
575 {
576     krb5_error_code code;
577     struct addrlist al = ADDRLIST_INIT;
578
579     *addrlist = al;
580
581     /*
582      * We always try the local file first
583      */
584
585     code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
586                                   socktype, dflport1, dflport2, family);
587
588 #ifdef KRB5_DNS_LOOKUP
589     if (code && dnsname != 0) {
590         int use_dns = _krb5_use_dns_kdc(context);
591         if (use_dns) {
592             code = 0;
593             if (socktype == SOCK_DGRAM || socktype == 0) {
594                 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
595                                              &al, family);
596                 if (code)
597                     Tprintf("dns udp lookup returned error %d\n", code);
598             }
599             if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
600                 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
601                                              &al, family);
602                 if (code)
603                     Tprintf("dns tcp lookup returned error %d\n", code);
604             }
605         }
606     }
607 #endif /* KRB5_DNS_LOOKUP */
608     if (code == 0)
609         Tprintf ("krb5int_locate_server found %d addresses\n",
610                  al.naddrs);
611     else
612         Tprintf ("krb5int_locate_server returning error code %d\n",
613                  code);
614     if (code != 0) {
615         if (al.space)
616             free_list (&al);
617         return code;
618     }
619     if (al.naddrs == 0) {       /* No good servers */
620         if (al.space)
621             free_list (&al);
622         return KRB5_REALM_CANT_RESOLVE;
623     }
624     *addrlist = al;
625     return 0;
626 }
627
628 krb5_error_code
629 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
630                 struct addrlist *addrlist,
631                 int get_masters, int socktype, int family)
632 {
633     int udpport, sec_udpport;
634
635     udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
636     if (socktype == SOCK_STREAM)
637         sec_udpport = 0;
638     else {
639         sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
640                                 (udpport == htons (KRB5_DEFAULT_PORT)
641                                  ? KRB5_DEFAULT_SEC_PORT
642                                  : KRB5_DEFAULT_PORT));
643         if (sec_udpport == udpport)
644             sec_udpport = 0;
645     }
646
647     return krb5int_locate_server(context, realm, addrlist, 0,
648                                  get_masters ? "master_kdc" : "kdc",
649                                  (get_masters
650                                   ? "_kerberos-master"
651                                   : "_kerberos"),
652                                  socktype, udpport, sec_udpport, family);
653 }