xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/os/dnssrv.c (revision 7934:6aeeafc994de)
1781Sgtb /*
2781Sgtb  * lib/krb5/os/dnssrv.c
3781Sgtb  *
4781Sgtb  * Copyright 1990,2000,2001,2002,2003 by the Massachusetts Institute of Technology.
5781Sgtb  * All Rights Reserved.
6781Sgtb  *
7781Sgtb  * Export of this software from the United States of America may
8781Sgtb  *   require a specific license from the United States Government.
9781Sgtb  *   It is the responsibility of any person or organization contemplating
10781Sgtb  *   export to obtain such a license before exporting.
11781Sgtb  *
12781Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13781Sgtb  * distribute this software and its documentation for any purpose and
14781Sgtb  * without fee is hereby granted, provided that the above copyright
15781Sgtb  * notice appear in all copies and that both that copyright notice and
16781Sgtb  * this permission notice appear in supporting documentation, and that
17781Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
18781Sgtb  * to distribution of the software without specific, written prior
19781Sgtb  * permission.  Furthermore if you modify this software you must label
20781Sgtb  * your software as modified software and not distribute it in such a
21781Sgtb  * fashion that it might be confused with the original M.I.T. software.
22781Sgtb  * M.I.T. makes no representations about the suitability of
23781Sgtb  * this software for any purpose.  It is provided "as is" without express
24781Sgtb  * or implied warranty.
25781Sgtb  *
26781Sgtb  *
27781Sgtb  * do DNS SRV RR queries
28781Sgtb  */
29781Sgtb 
30*7934SMark.Phalan@Sun.COM #include "autoconf.h"
31781Sgtb #ifdef KRB5_DNS_LOOKUP
32781Sgtb 
33781Sgtb #include "dnsglue.h"
34781Sgtb 
35781Sgtb /*
36781Sgtb  * Lookup a KDC via DNS SRV records
37781Sgtb  */
38781Sgtb 
krb5int_free_srv_dns_data(struct srv_dns_entry * p)39781Sgtb void krb5int_free_srv_dns_data (struct srv_dns_entry *p)
40781Sgtb {
41781Sgtb     struct srv_dns_entry *next;
42781Sgtb     while (p) {
43781Sgtb 	next = p->next;
44781Sgtb 	free(p->host);
45781Sgtb 	free(p);
46781Sgtb 	p = next;
47781Sgtb     }
48781Sgtb }
49781Sgtb 
50781Sgtb /* Do DNS SRV query, return results in *answers.
51781Sgtb 
52781Sgtb    Make best effort to return all the data we can.  On memory or
53781Sgtb    decoding errors, just return what we've got.  Always return 0,
54781Sgtb    currently.  */
55781Sgtb 
56781Sgtb krb5_error_code
krb5int_make_srv_query_realm(const krb5_data * realm,const char * service,const char * protocol,struct srv_dns_entry ** answers)57781Sgtb krb5int_make_srv_query_realm(const krb5_data *realm,
58781Sgtb 			     const char *service,
59781Sgtb 			     const char *protocol,
60781Sgtb 			     struct srv_dns_entry **answers)
61781Sgtb {
62781Sgtb     const unsigned char *p = NULL, *base = NULL;
63781Sgtb     char host[MAXDNAME], *h;
64781Sgtb     int size, ret, rdlen, nlen;
65781Sgtb     unsigned short priority, weight, port;
66781Sgtb     struct krb5int_dns_state *ds = NULL;
67781Sgtb 
68781Sgtb     struct srv_dns_entry *head = NULL;
69781Sgtb     struct srv_dns_entry *srv = NULL, *entry = NULL;
70781Sgtb 
71781Sgtb     /*
72781Sgtb      * First off, build a query of the form:
73781Sgtb      *
74781Sgtb      * service.protocol.realm
75781Sgtb      *
76781Sgtb      * which will most likely be something like:
77781Sgtb      *
78781Sgtb      * _kerberos._udp.REALM
79781Sgtb      *
80781Sgtb      */
81781Sgtb 
82781Sgtb     if (memchr(realm->data, 0, realm->length))
83781Sgtb 	return 0;
84781Sgtb     if ( strlen(service) + strlen(protocol) + realm->length + 6
85781Sgtb          > MAXDNAME )
86781Sgtb 	return 0;
87781Sgtb     sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
88781Sgtb 	    realm->data);
89781Sgtb 
90781Sgtb     /* Realm names don't (normally) end with ".", but if the query
91781Sgtb        doesn't end with "." and doesn't get an answer as is, the
92781Sgtb        resolv code will try appending the local domain.  Since the
93781Sgtb        realm names are absolutes, let's stop that.
94781Sgtb 
95781Sgtb        But only if a name has been specified.  If we are performing
96781Sgtb        a search on the prefix alone then the intention is to allow
97781Sgtb        the local domain or domain search lists to be expanded.  */
98781Sgtb 
99781Sgtb     h = host + strlen (host);
100781Sgtb     if ((h[-1] != '.') && ((h - host + 1) < sizeof(host)))
101781Sgtb         strcpy (h, ".");
102781Sgtb 
103781Sgtb #ifdef TEST
104781Sgtb     fprintf (stderr, "sending DNS SRV query for %s\n", host);
105781Sgtb #endif
106781Sgtb 
107781Sgtb     size = krb5int_dns_init(&ds, host, C_IN, T_SRV);
108781Sgtb     if (size < 0)
109781Sgtb 	goto out;
110781Sgtb 
111781Sgtb     for (;;) {
112781Sgtb 	ret = krb5int_dns_nextans(ds, &base, &rdlen);
113781Sgtb 	if (ret < 0 || base == NULL)
114781Sgtb 	    goto out;
115781Sgtb 
116781Sgtb 	p = base;
117781Sgtb 
118781Sgtb 	SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
119781Sgtb 	SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
120781Sgtb 	SAFE_GETUINT16(base, rdlen, p, 2, port, out);
121781Sgtb 
122781Sgtb 	/*
123781Sgtb 	 * RFC 2782 says the target is never compressed in the reply;
124781Sgtb 	 * do we believe that?  We need to flatten it anyway, though.
125781Sgtb 	 */
126781Sgtb 	nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
127781Sgtb 	if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
128781Sgtb 	    goto out;
129781Sgtb 
130781Sgtb 	/*
131781Sgtb 	 * We got everything!  Insert it into our list, but make sure
132781Sgtb 	 * it's in the right order.  Right now we don't do anything
133781Sgtb 	 * with the weight field
134781Sgtb 	 */
135781Sgtb 
136781Sgtb 	srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
137781Sgtb 	if (srv == NULL)
138781Sgtb 	    goto out;
139781Sgtb 
140781Sgtb 	srv->priority = priority;
141781Sgtb 	srv->weight = weight;
142781Sgtb 	srv->port = port;
143781Sgtb 	/* The returned names are fully qualified.  Don't let the
144781Sgtb 	   local resolver code do domain search path stuff.  */
145781Sgtb 	if (strlen(host) + 2 < sizeof(host))
146781Sgtb 	    strcat(host, ".");
147781Sgtb 	srv->host = strdup(host);
148781Sgtb 	if (srv->host == NULL) {
149781Sgtb 	    free(srv);
150781Sgtb 	    goto out;
151781Sgtb 	}
152781Sgtb 
153781Sgtb 	if (head == NULL || head->priority > srv->priority) {
154781Sgtb 	    srv->next = head;
155781Sgtb 	    head = srv;
156781Sgtb 	} else {
157781Sgtb 	    /*
158781Sgtb 	     * This is confusing.  Only insert an entry into this
159781Sgtb 	     * spot if:
160781Sgtb 	     * The next person has a higher priority (lower priorities
161781Sgtb 	     * are preferred).
162781Sgtb 	     * Or
163781Sgtb 	     * There is no next entry (we're at the end)
164781Sgtb 	     */
165781Sgtb 	    for (entry = head; entry != NULL; entry = entry->next) {
166781Sgtb 		if ((entry->next &&
167781Sgtb 		     entry->next->priority > srv->priority) ||
168781Sgtb 		    entry->next == NULL) {
169781Sgtb 		    srv->next = entry->next;
170781Sgtb 		    entry->next = srv;
171781Sgtb 		    break;
172781Sgtb 		}
173781Sgtb 	    }
174781Sgtb 	}
175781Sgtb     }
176781Sgtb 
177781Sgtb out:
178781Sgtb     if (ds != NULL) {
179781Sgtb 	krb5int_dns_fini(ds);
180781Sgtb 	ds = NULL;
181781Sgtb     }
182781Sgtb     *answers = head;
183781Sgtb     return 0;
184781Sgtb }
185781Sgtb #endif
186