xref: /illumos-gate/usr/src/lib/nsswitch/dns/common/dns_common.c (revision cdf9f8c9206117516a14f3c70f86510326c3d5fa)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  *	dns_common.c
30  */
31 
32 #include "dns_common.h"
33 
34 #pragma weak	dn_expand
35 #pragma weak	res_ninit
36 #pragma weak	res_nsearch
37 #pragma weak	res_nclose
38 #pragma weak	ns_get16
39 #pragma weak	ns_get32
40 #pragma weak	__ns_get16
41 #pragma weak	__ns_get32
42 
43 #define	DNS_ALIASES	0
44 #define	DNS_ADDRLIST	1
45 #define	DNS_MAPDLIST	2
46 
47 static int
48 dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type)
49 	char	**from_list, **to_list,	**aliaspp;
50 	int	type, *count, af_type;
51 {
52 	char		*fstr;
53 	int		cnt = 0;
54 	size_t		len;
55 
56 	*count = 0;
57 	if ((char *)to_list >= *aliaspp)
58 		return (NSS_STR_PARSE_ERANGE);
59 
60 	for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
61 		if (type == DNS_ALIASES)
62 			len = strlen(fstr) + 1;
63 		else
64 			len = (af_type == AF_INET) ? sizeof (struct in_addr)
65 						: sizeof (struct in6_addr);
66 		*aliaspp -= len;
67 		to_list[cnt] = *aliaspp;
68 		if (*aliaspp <= (char *)&to_list[cnt+1])
69 			return (NSS_STR_PARSE_ERANGE);
70 		if (type == DNS_MAPDLIST) {
71 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
72 			struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
73 
74 			(void) memset(addr6p, '\0', sizeof (struct in6_addr));
75 			(void) memcpy(&addr6p->s6_addr[12], fstr,
76 					sizeof (struct in_addr));
77 			addr6p->s6_addr[10] = 0xffU;
78 			addr6p->s6_addr[11] = 0xffU;
79 			++cnt;
80 		} else {
81 			(void) memcpy (*aliaspp, fstr, len);
82 			++cnt;
83 		}
84 	}
85 	to_list[cnt] = NULL;
86 
87 	*count = cnt;
88 	if (cnt == 0)
89 		return (NSS_STR_PARSE_PARSE);
90 
91 	return (NSS_STR_PARSE_SUCCESS);
92 }
93 
94 
95 int
96 ent2result(he, argp, af_type)
97 	struct hostent		*he;
98 	nss_XbyY_args_t		*argp;
99 	int			af_type;
100 {
101 	char		*buffer, *limit;
102 	int		buflen = argp->buf.buflen;
103 	int		ret, count;
104 	size_t len;
105 	struct hostent 	*host;
106 	struct in_addr	*addrp;
107 	struct in6_addr	*addrp6;
108 
109 	limit = argp->buf.buffer + buflen;
110 	host = (struct hostent *)argp->buf.result;
111 	buffer = argp->buf.buffer;
112 
113 	/* h_addrtype and h_length */
114 	host->h_addrtype = af_type;
115 	host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr)
116 					: sizeof (struct in6_addr);
117 
118 	/* h_name */
119 	len = strlen(he->h_name) + 1;
120 	host->h_name = buffer;
121 	if (host->h_name + len >= limit)
122 		return (NSS_STR_PARSE_ERANGE);
123 	(void) memcpy(host->h_name, he->h_name, len);
124 	buffer += len;
125 
126 	/* h_addr_list */
127 	if (af_type == AF_INET) {
128 		addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
129 		host->h_addr_list = (char **)
130 				ROUND_UP(buffer, sizeof (char **));
131 		ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
132 			(char **)&addrp, DNS_ADDRLIST, &count, af_type);
133 		if (ret != NSS_STR_PARSE_SUCCESS)
134 			return (ret);
135 		/* h_aliases */
136 		host->h_aliases = host->h_addr_list + count + 1;
137 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
138 			(char **)&addrp, DNS_ALIASES, &count, af_type);
139 	} else {
140 		addrp6 = (struct in6_addr *)
141 			ROUND_DOWN(limit, sizeof (*addrp6));
142 		host->h_addr_list = (char **)
143 			ROUND_UP(buffer, sizeof (char **));
144 		if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
145 			ret = dns_netdb_aliases(he->h_addr_list,
146 				host->h_addr_list, (char **)&addrp6,
147 				DNS_MAPDLIST, &count, af_type);
148 		} else {
149 			ret = dns_netdb_aliases(he->h_addr_list,
150 				host->h_addr_list, (char **)&addrp6,
151 				DNS_ADDRLIST, &count, af_type);
152 		}
153 		if (ret != NSS_STR_PARSE_SUCCESS)
154 			return (ret);
155 		/* h_aliases */
156 		host->h_aliases = host->h_addr_list + count + 1;
157 		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
158 			(char **)&addrp6, DNS_ALIASES, &count, af_type);
159 	}
160 	if (ret == NSS_STR_PARSE_PARSE)
161 		ret = NSS_STR_PARSE_SUCCESS;
162 
163 	return (ret);
164 }
165 
166 /*
167  * Convert the hostent structure into string in the following
168  * format:
169  *
170  * IP-address official-host-name nicknames ...
171  *
172  * If more than one IP-addresses matches the official-host-name,
173  * the above line will be followed by:
174  * IP-address-1 official-host-name
175  * IP-address-2 official-host-name
176  * ...
177  *
178  * This is so that the str2hostent function in libnsl
179  * can convert the string back to the original hostent
180  * data.
181  */
182 int
183 ent2str(
184 	struct hostent	*hp,
185 	nss_XbyY_args_t *ap,
186 	int		af_type)
187 {
188 	char		**p;
189 	char		obuf[INET6_ADDRSTRLEN];
190 	void		*addr;
191 	struct in_addr	in4;
192 	int		af;
193 	int		n;
194 	const char	*res;
195 	char		**q;
196 	int		l = ap->buf.buflen;
197 	char		*s = ap->buf.buffer;
198 
199 	/*
200 	 * for "hosts" lookup, we only want address type of
201 	 * AF_INET. For "ipnodes", we can have both AF_INET
202 	 * and AF_INET6.
203 	 */
204 	if (af_type == AF_INET && hp->h_addrtype != AF_INET)
205 		return (NSS_STR_PARSE_PARSE);
206 
207 	for (p = hp->h_addr_list; *p != 0; p++) {
208 
209 		if (p != hp->h_addr_list) {
210 			*s = '\n';
211 			s++;
212 			l--;
213 		}
214 
215 		if (hp->h_addrtype == AF_INET6) {
216 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
217 			if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
218 				/* LINTED: E_BAD_PTR_CAST_ALIGN */
219 				IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
220 				    &in4);
221 				af = AF_INET;
222 				addr = &in4;
223 			} else {
224 				af = AF_INET6;
225 				addr = *p;
226 			}
227 		} else {
228 			af = AF_INET;
229 			addr = *p;
230 		}
231 		res = inet_ntop(af, addr, obuf, sizeof (obuf));
232 		if (res == NULL)
233 			return (NSS_STR_PARSE_PARSE);
234 
235 		if ((n = snprintf(s, l, "%s", res)) >= l)
236 			return (NSS_STR_PARSE_ERANGE);
237 		l -= n;
238 		s += n;
239 		if (hp->h_name != NULL && *hp->h_name != '\0') {
240 			if ((n = snprintf(s, l, " %s", hp->h_name)) >= l)
241 				return (NSS_STR_PARSE_ERANGE);
242 			l -= n;
243 			s += n;
244 		}
245 		if (p == hp->h_addr_list) {
246 			for (q = hp->h_aliases; q && *q; q++) {
247 				if ((n = snprintf(s, l, " %s", *q)) >= l)
248 					return (NSS_STR_PARSE_ERANGE);
249 				l -= n;
250 				s += n;
251 			}
252 		}
253 	}
254 
255 	ap->returnlen = s - ap->buf.buffer;
256 	return (NSS_STR_PARSE_SUCCESS);
257 }
258 
259 nss_backend_t *
260 _nss_dns_constr(dns_backend_op_t ops[], int n_ops)
261 {
262 	dns_backend_ptr_t	be;
263 
264 	if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
265 		return (0);
266 
267 	be->ops = ops;
268 	be->n_ops = n_ops;
269 	return ((nss_backend_t *)be);
270 }
271 
272 /*
273  * __res_ndestroy is a simplified version of the non-public function
274  * res_ndestroy in libresolv.so.2. Before res_ndestroy can be made
275  * public, __res_ndestroy will be used to make sure the memory pointed
276  * by statp->_u._ext.ext is freed after res_nclose() is called.
277  */
278 static void
279 __res_ndestroy(res_state statp) {
280 	res_nclose(statp);
281 	if (statp->_u._ext.ext != NULL)
282 		free(statp->_u._ext.ext);
283 }
284 
285 /*
286  * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
287  *      nss2 get hosts/ipnodes with ttl backend DNS search engine.
288  *
289  * This API is given a pointer to a packed buffer, and the buffer size
290  * It's job is to perform the appropriate res_nsearch, extract the
291  * results and build a unmarshalled hosts/ipnodes result buffer.
292  * Additionally in the extended results a nssuint_t ttl is placed.
293  * This ttl is the lessor of the ttl's extracted from the result.
294  *
295  * ***Currently the first version of this API only performs simple
296  *    single res_nsearch lookups for with T_A or T_AAAA results.
297  *    Other searches are deferred to the generic API w/t ttls.
298  *
299  *    This function is not a generic res_* operation.  It only performs
300  *    a single T_A or T_AAAA lookups***
301  *
302  * RETURNS:  NSS_SUCCESS or NSS_ERROR
303  *	If an NSS_ERROR result is returned, nscd is expected
304  *	to resubmit the gethosts request using the old style
305  *	nsswitch lookup format.
306  */
307 
308 nss_status_t
309 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
310 {
311 	/* nss buffer variables */
312 	nss_pheader_t	*pbuf = (nss_pheader_t *)buffer;
313 	nss_XbyY_args_t	arg;
314 	char		*dbname;
315 	int		dbop;
316 	nss_status_t	sret;
317 	size_t		bsize, blen;
318 	char		*bptr;
319 	/* resolver query variables */
320 	struct __res_state stat, *statp;	/* dns state block */
321 	union msg {
322 		uchar_t	buf[NS_MAXMSG];		/* max legal DNS answer size */
323 		HEADER	h;
324 	} resbuf;
325 	char aliases[NS_MAXMSG];		/* set of aliases */
326 	const char	*name;
327 	int		qtype;
328 	/* answer parsing variables */
329 	HEADER		*hp;
330 	uchar_t		*cp;	/* current location in message */
331 	uchar_t		*bom;	/* start of message */
332 	uchar_t		*eom;	/* end of message */
333 	uchar_t		*eor;	/* end of record */
334 	int		ancount, qdcount;
335 	int		type, class;
336 	nssuint_t	nttl, ttl, *pttl;	/* The purpose of this API */
337 	int		n, ret;
338 	const char	*np;
339 	/* temporary buffers */
340 	char		nbuf[INET6_ADDRSTRLEN];	/* address parser */
341 	char		host[MAXHOSTNAMELEN];	/* result host name */
342 	char		ans[MAXHOSTNAMELEN];	/* record name */
343 	char		aname[MAXHOSTNAMELEN];	/* alias result (C_NAME) */
344 	/* misc variables */
345 	int		af;
346 	char		*ap, *apc;
347 	int		hlen = 0, alen, iplen, len;
348 
349 	statp = &stat;
350 	(void) memset(statp, '\0', sizeof (struct __res_state));
351 	if (res_ninit(statp) == -1)
352 		return (NSS_ERROR);
353 
354 	ap = apc = (char *)aliases;
355 	alen = 0;
356 	ttl = (nssuint_t)0xFFFFFFF;		/* start w/max, find smaller */
357 
358 	/* save space for ttl otherwise, why bother... */
359 	bsize = pbuf->data_len - sizeof (nssuint_t);
360 	bptr = (char *)buffer + pbuf->data_off;
361 	blen = 0;
362 	sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
363 	if (sret != NSS_SUCCESS) {
364 		__res_ndestroy(statp);
365 		return (NSS_ERROR);
366 	}
367 
368 	if (ipnode) {
369 		/* initially only handle the simple cases */
370 		if (arg.key.ipnode.flags != 0) {
371 			__res_ndestroy(statp);
372 			return (NSS_ERROR);
373 		}
374 		name = arg.key.ipnode.name;
375 		if (arg.key.ipnode.af_family == AF_INET6)
376 			qtype = T_AAAA;
377 		else
378 			qtype = T_A;
379 	} else {
380 		name = arg.key.name;
381 		qtype = T_A;
382 	}
383 	ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG);
384 	if (ret == -1) {
385 		if (statp->res_h_errno == HOST_NOT_FOUND) {
386 			pbuf->p_herrno = HOST_NOT_FOUND;
387 			pbuf->p_status = NSS_NOTFOUND;
388 			pbuf->data_len = 0;
389 			__res_ndestroy(statp);
390 			return (NSS_NOTFOUND);
391 		}
392 		/* else lookup error - handle in general code */
393 		__res_ndestroy(statp);
394 		return (NSS_ERROR);
395 	}
396 
397 	cp = resbuf.buf;
398 	hp = (HEADER *)&resbuf.h;
399 	bom = cp;
400 	eom = cp + ret;
401 
402 	ancount = ntohs(hp->ancount);
403 	qdcount = ntohs(hp->qdcount);
404 	cp += HFIXEDSZ;
405 	if (qdcount != 1) {
406 		__res_ndestroy(statp);
407 		return (NSS_ERROR);
408 	}
409 	n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
410 	if (n < 0) {
411 		__res_ndestroy(statp);
412 		return (NSS_ERROR);
413 	} else
414 		hlen = strlen(host);
415 	/* no host name is an error, return */
416 	if (hlen <= 0) {
417 		__res_ndestroy(statp);
418 		return (NSS_ERROR);
419 	}
420 	cp += n + QFIXEDSZ;
421 	if (cp > eom) {
422 		__res_ndestroy(statp);
423 		return (NSS_ERROR);
424 	}
425 	while (ancount-- > 0 && cp < eom && blen < bsize) {
426 		n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
427 		if (n > 0) {
428 			if (strncasecmp(host, ans, hlen) != 0) {
429 				__res_ndestroy(statp);
430 				return (NSS_ERROR);	/* spoof? */
431 			}
432 		}
433 		cp += n;
434 		/* bounds check */
435 		type = ns_get16(cp);			/* type */
436 		cp += INT16SZ;
437 		class = ns_get16(cp);			/* class */
438 		cp += INT16SZ;
439 		nttl = (nssuint_t)ns_get32(cp);	/* ttl in sec */
440 		if (nttl < ttl)
441 			ttl = nttl;
442 		cp += INT32SZ;
443 		n = ns_get16(cp);			/* len */
444 		cp += INT16SZ;
445 		if (class != C_IN) {
446 			cp += n;
447 			continue;
448 		}
449 		eor = cp + n;
450 		if (type == T_CNAME) {
451 			/* add an alias to the alias list */
452 			n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
453 			if (n > 0) {
454 				len = strlen(aname);
455 				if (len > 0) {
456 					/*
457 					 * Just error out if there is an
458 					 * attempted buffer overflow exploit
459 					 * generic code will do a syslog
460 					 */
461 					if (alen + len + 2 > NS_MAXMSG) {
462 						__res_ndestroy(statp);
463 						return (NSS_ERROR);
464 					}
465 					*apc++ = ' ';
466 					alen++;
467 					(void) strlcpy(apc, aname, len + 1);
468 					alen += len;
469 					apc += len;
470 				}
471 			}
472 			cp += n;
473 			continue;
474 		}
475 		if (type != qtype) {
476 			cp += n;
477 			continue;
478 		}
479 		/* check data size */
480 		if ((type == T_A && n != INADDRSZ) ||
481 		    (type == T_AAAA && n != IN6ADDRSZ)) {
482 			cp += n;
483 			continue;
484 		}
485 		af = (type == T_A ? AF_INET : AF_INET6);
486 		np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
487 		if (np == NULL) {
488 			__res_ndestroy(statp);
489 			return (NSS_ERROR);
490 		}
491 		cp += n;
492 		/* append IP host aliases to results */
493 		iplen = strlen(np);
494 		/* ip <SP> hostname [<SP>][aliases] */
495 		len = iplen + 2 + hlen + alen;
496 		if (alen > 0)
497 			len++;
498 		if (blen + len > bsize) {
499 			__res_ndestroy(statp);
500 			return (NSS_ERROR);
501 		}
502 		(void) strlcpy(bptr, np, bsize - blen);
503 		blen += iplen;
504 		bptr += iplen;
505 		*bptr++ = ' ';
506 		blen++;
507 		(void) strlcpy(bptr, host, bsize - blen);
508 		blen += hlen;
509 		bptr += hlen;
510 		if (alen > 0) {
511 			*bptr++ = ' ';
512 			blen++;
513 			(void) strlcpy(bptr, ap, bsize - blen);
514 			blen += alen;
515 			bptr += alen;
516 		}
517 		*bptr++ = '\n';
518 		blen++;
519 	}
520 	/* Presumably the buffer is now filled. */
521 	len = ROUND_UP(blen, sizeof (nssuint_t));
522 	/* still room? */
523 	if (len + sizeof (nssuint_t) > pbuf->data_len) {
524 		/* sigh, no, what happened? */
525 		__res_ndestroy(statp);
526 		return (NSS_ERROR);
527 	}
528 	pbuf->ext_off = pbuf->data_off + len;
529 	pbuf->ext_len = sizeof (nssuint_t);
530 	pbuf->data_len = blen;
531 	pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
532 	*pttl = ttl;
533 	__res_ndestroy(statp);
534 	return (NSS_SUCCESS);
535 }
536