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