1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * dns_common.c
28 */
29
30 #include "dns_common.h"
31
32 #pragma weak dn_expand
33 #pragma weak res_ninit
34 #pragma weak res_ndestroy
35 #pragma weak res_nsearch
36 #pragma weak res_nclose
37 #pragma weak ns_get16
38 #pragma weak ns_get32
39 #pragma weak __ns_get16
40 #pragma weak __ns_get32
41
42 #define DNS_ALIASES 0
43 #define DNS_ADDRLIST 1
44 #define DNS_MAPDLIST 2
45
46 #ifndef tolower
47 #define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c))
48 #endif
49
50 static int
dns_netdb_aliases(from_list,to_list,aliaspp,type,count,af_type)51 dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type)
52 char **from_list, **to_list, **aliaspp;
53 int type, *count, af_type;
54 {
55 char *fstr;
56 int cnt = 0;
57 size_t len;
58
59 *count = 0;
60 if ((char *)to_list >= *aliaspp)
61 return (NSS_STR_PARSE_ERANGE);
62
63 for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
64 if (type == DNS_ALIASES)
65 len = strlen(fstr) + 1;
66 else
67 len = (af_type == AF_INET) ? sizeof (struct in_addr)
68 : sizeof (struct in6_addr);
69 *aliaspp -= len;
70 to_list[cnt] = *aliaspp;
71 if (*aliaspp <= (char *)&to_list[cnt+1])
72 return (NSS_STR_PARSE_ERANGE);
73 if (type == DNS_MAPDLIST) {
74 /* LINTED: E_BAD_PTR_CAST_ALIGN */
75 struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
76
77 (void) memset(addr6p, '\0', sizeof (struct in6_addr));
78 (void) memcpy(&addr6p->s6_addr[12], fstr,
79 sizeof (struct in_addr));
80 addr6p->s6_addr[10] = 0xffU;
81 addr6p->s6_addr[11] = 0xffU;
82 ++cnt;
83 } else {
84 (void) memcpy (*aliaspp, fstr, len);
85 ++cnt;
86 }
87 }
88 to_list[cnt] = NULL;
89
90 *count = cnt;
91 if (cnt == 0)
92 return (NSS_STR_PARSE_PARSE);
93
94 return (NSS_STR_PARSE_SUCCESS);
95 }
96
97
98 int
ent2result(he,argp,af_type)99 ent2result(he, argp, af_type)
100 struct hostent *he;
101 nss_XbyY_args_t *argp;
102 int af_type;
103 {
104 char *buffer, *limit;
105 int buflen = argp->buf.buflen;
106 int ret, count;
107 size_t len;
108 struct hostent *host;
109 struct in_addr *addrp;
110 struct in6_addr *addrp6;
111
112 limit = argp->buf.buffer + buflen;
113 host = (struct hostent *)argp->buf.result;
114 buffer = argp->buf.buffer;
115
116 /* h_addrtype and h_length */
117 host->h_addrtype = af_type;
118 host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr)
119 : sizeof (struct in6_addr);
120
121 /* h_name */
122 len = strlen(he->h_name) + 1;
123 host->h_name = buffer;
124 if (host->h_name + len >= limit)
125 return (NSS_STR_PARSE_ERANGE);
126 (void) memcpy(host->h_name, he->h_name, len);
127 buffer += len;
128
129 /* h_addr_list */
130 if (af_type == AF_INET) {
131 addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
132 host->h_addr_list = (char **)
133 ROUND_UP(buffer, sizeof (char **));
134 ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
135 (char **)&addrp, DNS_ADDRLIST, &count, af_type);
136 if (ret != NSS_STR_PARSE_SUCCESS)
137 return (ret);
138 /* h_aliases */
139 host->h_aliases = host->h_addr_list + count + 1;
140 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
141 (char **)&addrp, DNS_ALIASES, &count, af_type);
142 } else {
143 addrp6 = (struct in6_addr *)
144 ROUND_DOWN(limit, sizeof (*addrp6));
145 host->h_addr_list = (char **)
146 ROUND_UP(buffer, sizeof (char **));
147 if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
148 ret = dns_netdb_aliases(he->h_addr_list,
149 host->h_addr_list, (char **)&addrp6,
150 DNS_MAPDLIST, &count, af_type);
151 } else {
152 ret = dns_netdb_aliases(he->h_addr_list,
153 host->h_addr_list, (char **)&addrp6,
154 DNS_ADDRLIST, &count, af_type);
155 }
156 if (ret != NSS_STR_PARSE_SUCCESS)
157 return (ret);
158 /* h_aliases */
159 host->h_aliases = host->h_addr_list + count + 1;
160 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
161 (char **)&addrp6, DNS_ALIASES, &count, af_type);
162 }
163 if (ret == NSS_STR_PARSE_PARSE)
164 ret = NSS_STR_PARSE_SUCCESS;
165
166 return (ret);
167 }
168
169 /*
170 * Convert the hostent structure into string in the following
171 * format:
172 *
173 * IP-address official-host-name nicknames ...
174 *
175 * If more than one IP-addresses matches the official-host-name,
176 * the above line will be followed by:
177 * IP-address-1 official-host-name
178 * IP-address-2 official-host-name
179 * ...
180 *
181 * This is so that the str2hostent function in libnsl
182 * can convert the string back to the original hostent
183 * data.
184 */
185 int
ent2str(struct hostent * hp,nss_XbyY_args_t * ap,int af_type)186 ent2str(
187 struct hostent *hp,
188 nss_XbyY_args_t *ap,
189 int af_type)
190 {
191 char **p;
192 char obuf[INET6_ADDRSTRLEN];
193 void *addr;
194 struct in_addr in4;
195 int af;
196 int n;
197 const char *res;
198 char **q;
199 int l = ap->buf.buflen;
200 char *s = ap->buf.buffer;
201
202 /*
203 * for "hosts" lookup, we only want address type of
204 * AF_INET. For "ipnodes", we can have both AF_INET
205 * and AF_INET6.
206 */
207 if (af_type == AF_INET && hp->h_addrtype != AF_INET)
208 return (NSS_STR_PARSE_PARSE);
209
210 for (p = hp->h_addr_list; *p != 0; p++) {
211
212 if (p != hp->h_addr_list) {
213 *s = '\n';
214 s++;
215 l--;
216 }
217
218 if (hp->h_addrtype == AF_INET6) {
219 /* LINTED: E_BAD_PTR_CAST_ALIGN */
220 if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
221 /* LINTED: E_BAD_PTR_CAST_ALIGN */
222 IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
223 &in4);
224 af = AF_INET;
225 addr = &in4;
226 } else {
227 af = AF_INET6;
228 addr = *p;
229 }
230 } else {
231 af = AF_INET;
232 addr = *p;
233 }
234 res = inet_ntop(af, addr, obuf, sizeof (obuf));
235 if (res == NULL)
236 return (NSS_STR_PARSE_PARSE);
237
238 if ((n = snprintf(s, l, "%s", res)) >= l)
239 return (NSS_STR_PARSE_ERANGE);
240 l -= n;
241 s += n;
242 if (hp->h_name != NULL && *hp->h_name != '\0') {
243 if ((n = snprintf(s, l, " %s", hp->h_name)) >= l)
244 return (NSS_STR_PARSE_ERANGE);
245 l -= n;
246 s += n;
247 }
248 if (p == hp->h_addr_list) {
249 for (q = hp->h_aliases; q && *q; q++) {
250 if ((n = snprintf(s, l, " %s", *q)) >= l)
251 return (NSS_STR_PARSE_ERANGE);
252 l -= n;
253 s += n;
254 }
255 }
256 }
257
258 ap->returnlen = s - ap->buf.buffer;
259 return (NSS_STR_PARSE_SUCCESS);
260 }
261
262 nss_backend_t *
_nss_dns_constr(dns_backend_op_t ops[],int n_ops)263 _nss_dns_constr(dns_backend_op_t ops[], int n_ops)
264 {
265 dns_backend_ptr_t be;
266
267 if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
268 return (0);
269
270 be->ops = ops;
271 be->n_ops = n_ops;
272 return ((nss_backend_t *)be);
273 }
274
275 /*
276 * name_is_alias(aliases_ptr, name_ptr)
277 * Verify name matches an alias in the provided aliases list.
278 *
279 * Within DNS there should be only one canonical name, aliases should
280 * all refer to the one canonical. However alias chains do occur and
281 * pre BIND 9 servers may also respond with multiple CNAMEs. This
282 * routine checks if a given name has been provided as a CNAME in the
283 * response. This assumes that the chains have been sent in-order.
284 *
285 * INPUT:
286 * aliases_ptr: space separated list of alias names.
287 * name_ptr: name to look for in aliases_ptr list.
288 * RETURNS: NSS_SUCCESS or NSS_NOTFOUND
289 * NSS_SUCCESS indicates that the name is listed in the collected aliases.
290 */
291 static nss_status_t
name_is_alias(char * aliases_ptr,char * name_ptr)292 name_is_alias(char *aliases_ptr, char *name_ptr) {
293 char *host_ptr;
294 /* Loop through alias string and compare it against host string. */
295 while (*aliases_ptr != '\0') {
296 host_ptr = name_ptr;
297
298 /* Compare name with alias. */
299 while (tolower(*host_ptr) == tolower(*aliases_ptr) &&
300 *host_ptr != '\0') {
301 host_ptr++;
302 aliases_ptr++;
303 }
304
305 /*
306 * If name was exhausted and the next character in the
307 * alias is either the end-of-string or space
308 * character then we have a match.
309 */
310 if (*host_ptr == '\0' &&
311 (*aliases_ptr == '\0' || *aliases_ptr == ' ')) {
312 return (NSS_SUCCESS);
313 }
314
315 /* Alias did not match, step over remainder of alias. */
316 while (*aliases_ptr != ' ' && *aliases_ptr != '\0')
317 aliases_ptr++;
318 /* Step over separator character. */
319 while (*aliases_ptr == ' ') aliases_ptr++;
320 }
321 return (NSS_NOTFOUND);
322 }
323
324 /*
325 * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
326 * nss2 get hosts/ipnodes with ttl backend DNS search engine.
327 *
328 * This API is given a pointer to a packed buffer, and the buffer size
329 * It's job is to perform the appropriate res_nsearch, extract the
330 * results and build a unmarshalled hosts/ipnodes result buffer.
331 * Additionally in the extended results a nssuint_t ttl is placed.
332 * This ttl is the lessor of the ttl's extracted from the result.
333 *
334 * ***Currently the first version of this API only performs simple
335 * single res_nsearch lookups for with T_A or T_AAAA results.
336 * Other searches are deferred to the generic API w/t ttls.
337 *
338 * This function is not a generic res_* operation. It only performs
339 * a single T_A or T_AAAA lookups***
340 *
341 * RETURNS: NSS_SUCCESS or NSS_ERROR
342 * If an NSS_ERROR result is returned, nscd is expected
343 * to resubmit the gethosts request using the old style
344 * nsswitch lookup format.
345 */
346
347 nss_status_t
_nss_dns_gethost_withttl(void * buffer,size_t bufsize,int ipnode)348 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
349 {
350 /* nss buffer variables */
351 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
352 nss_XbyY_args_t arg;
353 char *dbname;
354 int dbop;
355 nss_status_t sret;
356 size_t bsize, blen;
357 char *bptr;
358 /* resolver query variables */
359 struct __res_state stat, *statp; /* dns state block */
360 union msg {
361 uchar_t buf[NS_MAXMSG]; /* max legal DNS answer size */
362 HEADER h;
363 } resbuf;
364 char aliases[NS_MAXMSG]; /* set of aliases */
365 const char *name;
366 int qtype;
367 /* answer parsing variables */
368 HEADER *hp;
369 uchar_t *cp; /* current location in message */
370 uchar_t *bom; /* start of message */
371 uchar_t *eom; /* end of message */
372 uchar_t *eor; /* end of record */
373 int ancount, qdcount;
374 int type, class;
375 nssuint_t nttl, ttl, *pttl; /* The purpose of this API */
376 int n, ret;
377 const char *np;
378 /* temporary buffers */
379 char nbuf[INET6_ADDRSTRLEN]; /* address parser */
380 char host[MAXHOSTNAMELEN]; /* result host name */
381 char ans[MAXHOSTNAMELEN]; /* record name */
382 char aname[MAXHOSTNAMELEN]; /* alias result (C_NAME) */
383 /* misc variables */
384 int af;
385 char *ap, *apc;
386 int hlen = 0, alen, iplen, len, isans;
387
388 statp = &stat;
389 (void) memset(statp, '\0', sizeof (struct __res_state));
390 if (res_ninit(statp) == -1)
391 return (NSS_ERROR);
392
393 ap = apc = (char *)aliases;
394 alen = 0;
395 ttl = (nssuint_t)0xFFFFFFF; /* start w/max, find smaller */
396
397 /* save space for ttl otherwise, why bother... */
398 bsize = pbuf->data_len - sizeof (nssuint_t);
399 bptr = (char *)buffer + pbuf->data_off;
400 blen = 0;
401 sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
402 if (sret != NSS_SUCCESS) {
403 res_ndestroy(statp);
404 return (NSS_ERROR);
405 }
406
407 if (ipnode) {
408 /* initially only handle the simple cases */
409 if (arg.key.ipnode.flags != 0) {
410 res_ndestroy(statp);
411 return (NSS_ERROR);
412 }
413 name = arg.key.ipnode.name;
414 if (arg.key.ipnode.af_family == AF_INET6)
415 qtype = T_AAAA;
416 else
417 qtype = T_A;
418 } else {
419 name = arg.key.name;
420 qtype = T_A;
421 }
422 ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG);
423 if (ret == -1) {
424 if (statp->res_h_errno == HOST_NOT_FOUND) {
425 pbuf->p_herrno = HOST_NOT_FOUND;
426 pbuf->p_status = NSS_NOTFOUND;
427 pbuf->data_len = 0;
428 res_ndestroy(statp);
429 return (NSS_NOTFOUND);
430 }
431 /* else lookup error - handle in general code */
432 res_ndestroy(statp);
433 return (NSS_ERROR);
434 }
435
436 cp = resbuf.buf;
437 hp = (HEADER *)&resbuf.h;
438 bom = cp;
439 eom = cp + ret;
440
441 ancount = ntohs(hp->ancount);
442 qdcount = ntohs(hp->qdcount);
443 cp += HFIXEDSZ;
444 if (qdcount != 1) {
445 res_ndestroy(statp);
446 return (NSS_ERROR);
447 }
448 n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
449 if (n < 0) {
450 res_ndestroy(statp);
451 return (NSS_ERROR);
452 } else
453 hlen = strlen(host);
454 /* no host name is an error, return */
455 if (hlen <= 0) {
456 res_ndestroy(statp);
457 return (NSS_ERROR);
458 }
459 cp += n + QFIXEDSZ;
460 if (cp > eom) {
461 res_ndestroy(statp);
462 return (NSS_ERROR);
463 }
464 while (ancount-- > 0 && cp < eom && blen < bsize) {
465 n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
466 if (n > 0) {
467 /*
468 * Check that the expanded name is either the
469 * name we asked for or a learned alias.
470 */
471 if ((isans = strncasecmp(host, ans, hlen)) != 0 &&
472 (alen == 0 || name_is_alias(aliases, ans)
473 == NSS_NOTFOUND)) {
474 res_ndestroy(statp);
475 return (NSS_ERROR); /* spoof? */
476 }
477 }
478 cp += n;
479 /* bounds check */
480 type = ns_get16(cp); /* type */
481 cp += INT16SZ;
482 class = ns_get16(cp); /* class */
483 cp += INT16SZ;
484 nttl = (nssuint_t)ns_get32(cp); /* ttl in sec */
485 if (nttl < ttl)
486 ttl = nttl;
487 cp += INT32SZ;
488 n = ns_get16(cp); /* len */
489 cp += INT16SZ;
490 if (class != C_IN) {
491 cp += n;
492 continue;
493 }
494 eor = cp + n;
495 if (type == T_CNAME) {
496 /*
497 * The name looked up is really an alias and the
498 * canonical name should be in the RDATA.
499 * A canonical name may have several aliases but an
500 * alias should only have one canonical name.
501 * However multiple CNAMEs and CNAME chains do exist!
502 *
503 * Just error out on attempted buffer overflow exploit,
504 * generic code will syslog.
505 *
506 */
507 n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
508 if (n > 0 && (len = strlen(aname)) > 0) {
509 if (isans == 0) { /* host matched ans. */
510 /*
511 * Append host to alias list.
512 */
513 if (alen + hlen + 2 > NS_MAXMSG) {
514 res_ndestroy(statp);
515 return (NSS_ERROR);
516 }
517 *apc++ = ' ';
518 alen++;
519 (void) strlcpy(apc, host,
520 NS_MAXMSG - alen);
521 alen += hlen;
522 apc += hlen;
523 }
524 /*
525 * Overwrite host with canonical name.
526 */
527 if (strlcpy(host, aname, MAXHOSTNAMELEN) >=
528 MAXHOSTNAMELEN) {
529 res_ndestroy(statp);
530 return (NSS_ERROR);
531 }
532 hlen = len;
533 }
534 cp += n;
535 continue;
536 }
537 if (type != qtype) {
538 cp += n;
539 continue;
540 }
541 /* check data size */
542 if ((type == T_A && n != INADDRSZ) ||
543 (type == T_AAAA && n != IN6ADDRSZ)) {
544 cp += n;
545 continue;
546 }
547 af = (type == T_A ? AF_INET : AF_INET6);
548 np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
549 if (np == NULL) {
550 res_ndestroy(statp);
551 return (NSS_ERROR);
552 }
553 cp += n;
554 /* append IP host aliases to results */
555 iplen = strlen(np);
556 /* ip <SP> hostname [<SP>][aliases] */
557 len = iplen + 2 + hlen + alen;
558 if (alen > 0)
559 len++;
560 if (blen + len > bsize) {
561 res_ndestroy(statp);
562 return (NSS_ERROR);
563 }
564 (void) strlcpy(bptr, np, bsize - blen);
565 blen += iplen;
566 bptr += iplen;
567 *bptr++ = ' ';
568 blen++;
569 (void) strlcpy(bptr, host, bsize - blen);
570 blen += hlen;
571 bptr += hlen;
572 if (alen > 0) {
573 *bptr++ = ' ';
574 blen++;
575 (void) strlcpy(bptr, ap, bsize - blen);
576 blen += alen;
577 bptr += alen;
578 }
579 *bptr++ = '\n';
580 blen++;
581 }
582 /* Presumably the buffer is now filled. */
583 len = ROUND_UP(blen, sizeof (nssuint_t));
584 /* still room? */
585 if (len + sizeof (nssuint_t) > pbuf->data_len) {
586 /* sigh, no, what happened? */
587 res_ndestroy(statp);
588 return (NSS_ERROR);
589 }
590 pbuf->ext_off = pbuf->data_off + len;
591 pbuf->ext_len = sizeof (nssuint_t);
592 pbuf->data_len = blen;
593 pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
594 *pttl = ttl;
595 res_ndestroy(statp);
596 return (NSS_SUCCESS);
597 }
598