1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * lib/krb5/os/dnsglue.c
8 *
9 * Copyright 2004 by the Massachusetts Institute of Technology.
10 * All Rights Reserved.
11 *
12 * Export of this software from the United States of America may
13 * require a specific license from the United States Government.
14 * It is the responsibility of any person or organization contemplating
15 * export to obtain such a license before exporting.
16 *
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of M.I.T. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission. Furthermore if you modify this software you must label
25 * your software as modified software and not distribute it in such a
26 * fashion that it might be confused with the original M.I.T. software.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose. It is provided "as is" without express
29 * or implied warranty.
30 *
31 */
32 #include "autoconf.h"
33 #ifdef KRB5_DNS_LOOKUP
34
35 #include "dnsglue.h"
36
37 /*
38 * Only use res_ninit() if there's also a res_ndestroy(), to avoid
39 * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
40 * 5.x). While we're at it, make sure res_nsearch() is there too.
41 *
42 * In any case, it is probable that platforms having broken
43 * res_ninit() will have thread safety hacks for res_init() and _res.
44 */
45 #if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH
46 #define USE_RES_NINIT 1
47 #endif
48
49 /*
50 * Opaque handle
51 */
52 struct krb5int_dns_state {
53 int nclass;
54 int ntype;
55 void *ansp;
56 int anslen;
57 int ansmax;
58 #if HAVE_NS_INITPARSE
59 int cur_ans;
60 ns_msg msg;
61 #else
62 unsigned char *ptr;
63 unsigned short nanswers;
64 #endif
65 };
66
67 #if !HAVE_NS_INITPARSE
68 static int initparse(struct krb5int_dns_state *);
69 #endif
70
71 /*
72 * krb5int_dns_init()
73 *
74 * Initialize an opaque handle. Do name lookup and initial parsing of
75 * reply, skipping question section. Prepare to iterate over answer
76 * section. Returns -1 on error, 0 on success.
77 */
78 int
krb5int_dns_init(struct krb5int_dns_state ** dsp,char * host,int nclass,int ntype)79 krb5int_dns_init(struct krb5int_dns_state **dsp,
80 char *host, int nclass, int ntype)
81 {
82 #if USE_RES_NINIT
83 struct __res_state statbuf;
84 #endif
85 struct krb5int_dns_state *ds;
86 int len, ret;
87 size_t nextincr, maxincr;
88 unsigned char *p;
89
90 *dsp = ds = malloc(sizeof(*ds));
91 if (ds == NULL)
92 return -1;
93
94 ret = -1;
95 ds->nclass = nclass;
96 ds->ntype = ntype;
97 ds->ansp = NULL;
98 ds->anslen = 0;
99 ds->ansmax = 0;
100 nextincr = 2048;
101 maxincr = INT_MAX;
102
103 #if HAVE_NS_INITPARSE
104 ds->cur_ans = 0;
105 #endif
106
107 #if USE_RES_NINIT
108 memset(&statbuf, 0, sizeof(statbuf));
109 ret = res_ninit(&statbuf);
110 #else
111 ret = res_init();
112 #endif
113 if (ret < 0)
114 return -1;
115
116 do {
117 p = (ds->ansp == NULL)
118 ? malloc(nextincr) : realloc(ds->ansp, nextincr);
119
120 if (p == NULL && ds->ansp != NULL) {
121 ret = -1;
122 goto errout;
123 }
124 ds->ansp = p;
125 ds->ansmax = nextincr;
126
127 #if USE_RES_NINIT
128 len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
129 ds->ansp, ds->ansmax);
130 #else
131 len = res_search(host, ds->nclass, ds->ntype,
132 ds->ansp, ds->ansmax);
133 #endif
134 if (len > maxincr) {
135 ret = -1;
136 goto errout;
137 }
138 while (nextincr < len)
139 nextincr *= 2;
140 if (len < 0 || nextincr > maxincr) {
141 ret = -1;
142 goto errout;
143 }
144 } while (len > ds->ansmax);
145
146 ds->anslen = len;
147 #if HAVE_NS_INITPARSE
148 ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
149 #else
150 ret = initparse(ds);
151 #endif
152 if (ret < 0)
153 goto errout;
154
155 ret = 0;
156
157 errout:
158 #if USE_RES_NINIT
159 res_ndestroy(&statbuf);
160 #endif
161 if (ret < 0) {
162 if (ds->ansp != NULL) {
163 free(ds->ansp);
164 ds->ansp = NULL;
165 }
166 }
167
168 return ret;
169 }
170
171 #if HAVE_NS_INITPARSE
172 /*
173 * krb5int_dns_nextans - get next matching answer record
174 *
175 * Sets pp to NULL if no more records. Returns -1 on error, 0 on
176 * success.
177 */
178 int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)179 krb5int_dns_nextans(struct krb5int_dns_state *ds,
180 const unsigned char **pp, int *lenp)
181 {
182 int len;
183 ns_rr rr;
184
185 *pp = NULL;
186 *lenp = 0;
187 while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
188 len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
189 if (len < 0)
190 return -1;
191 ds->cur_ans++;
192 if (ds->nclass == ns_rr_class(rr)
193 && ds->ntype == ns_rr_type(rr)) {
194 *pp = ns_rr_rdata(rr);
195 *lenp = ns_rr_rdlen(rr);
196 return 0;
197 }
198 }
199 return 0;
200 }
201 #endif
202
203 /*
204 * krb5int_dns_expand - wrapper for dn_expand()
205 */
krb5int_dns_expand(struct krb5int_dns_state * ds,const unsigned char * p,char * buf,int len)206 int krb5int_dns_expand(struct krb5int_dns_state *ds,
207 const unsigned char *p,
208 char *buf, int len)
209 {
210
211 #if HAVE_NS_NAME_UNCOMPRESS
212 return ns_name_uncompress(ds->ansp,
213 (unsigned char *)ds->ansp + ds->anslen,
214 p, buf, (size_t)len);
215 #else
216 return dn_expand(ds->ansp,
217 (unsigned char *)ds->ansp + ds->anslen,
218 p, buf, len);
219 #endif
220 }
221
222 /*
223 * Free stuff.
224 */
225 void
krb5int_dns_fini(struct krb5int_dns_state * ds)226 krb5int_dns_fini(struct krb5int_dns_state *ds)
227 {
228 if (ds == NULL)
229 return;
230 if (ds->ansp != NULL)
231 free(ds->ansp);
232 free(ds);
233 }
234
235 /*
236 * Compat routines for BIND 4
237 */
238 #if !HAVE_NS_INITPARSE
239
240 /*
241 * initparse
242 *
243 * Skip header and question section of reply. Set a pointer to the
244 * beginning of the answer section, and prepare to iterate over
245 * answer records.
246 */
247 static int
initparse(struct krb5int_dns_state * ds)248 initparse(struct krb5int_dns_state *ds)
249 {
250 HEADER *hdr;
251 unsigned char *p;
252 unsigned short nqueries, nanswers;
253 int len;
254 #if !HAVE_DN_SKIPNAME
255 char host[MAXDNAME];
256 #endif
257
258 if (ds->anslen < sizeof(HEADER))
259 return -1;
260
261 hdr = (HEADER *)ds->ansp;
262 p = ds->ansp;
263 nqueries = ntohs((unsigned short)hdr->qdcount);
264 nanswers = ntohs((unsigned short)hdr->ancount);
265 p += sizeof(HEADER);
266
267 /*
268 * Skip query records.
269 */
270 while (nqueries--) {
271 #if HAVE_DN_SKIPNAME
272 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
273 #else
274 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
275 p, host, sizeof(host));
276 #endif
277 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
278 return -1;
279 p += len + 4;
280 }
281 ds->ptr = p;
282 ds->nanswers = nanswers;
283 return 0;
284 }
285
286 /*
287 * krb5int_dns_nextans() - get next answer record
288 *
289 * Sets pp to NULL if no more records.
290 */
291 int
krb5int_dns_nextans(struct krb5int_dns_state * ds,const unsigned char ** pp,int * lenp)292 krb5int_dns_nextans(struct krb5int_dns_state *ds,
293 const unsigned char **pp, int *lenp)
294 {
295 int len;
296 unsigned char *p;
297 unsigned short ntype, nclass, rdlen;
298 #if !HAVE_DN_SKIPNAME
299 char host[MAXDNAME];
300 #endif
301
302 *pp = NULL;
303 *lenp = 0;
304 p = ds->ptr;
305
306 while (ds->nanswers--) {
307 #if HAVE_DN_SKIPNAME
308 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
309 #else
310 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
311 p, host, sizeof(host));
312 #endif
313 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
314 return -1;
315 p += len;
316 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
317 /* Also skip 4 bytes of TTL */
318 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
319 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
320
321 if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
322 return -1;
323 /* Solaris Kerberos - resync */
324 #if 0
325 if (rdlen > INT_MAX)
326 return -1;
327 #endif
328 if (nclass == ds->nclass && ntype == ds->ntype) {
329 *pp = p;
330 *lenp = rdlen;
331 ds->ptr = p + rdlen;
332 return 0;
333 }
334 p += rdlen;
335 }
336 return 0;
337 out:
338 return -1;
339 }
340
341 #endif
342
343 #endif /* KRB5_DNS_LOOKUP */
344