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