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 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 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 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 * 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 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 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