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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include "mt.h" 31 #include <stdlib.h> 32 #include <string.h> 33 #include <rpcsvc/nis.h> 34 #include <ctype.h> 35 #include <malloc.h> 36 #include "nis_local.h" 37 38 #ifdef ORGDIR 39 static void append_orgdir(nis_name nname); 40 #endif 41 static void check_dup_dnames(nis_name *namelist, int count); 42 43 /* Counts and returns the number of dots in the given string. */ 44 static int 45 dots_in_name(char *name) 46 { 47 int i, ndots; 48 size_t len = strlen(name); 49 50 #ifdef QUOTES 51 int in_quotes = 0; 52 /* Commented out the quoted quotes stuff for 1202807 */ 53 #endif 54 55 for (ndots = i = 0; i < len; i++) { 56 57 #ifdef QUOTES 58 /* Commented out the quoted quotes stuff for 1202807 */ 59 60 /* Entering a quotation? */ 61 if (name[i] == '"') { 62 63 if (name[i+1] == '"') { 64 /* No. This is "", i.e. a quoted quote */ 65 66 i++; /* skip the next quote, else will */ 67 continue; /* think it's only one quote */ 68 69 } else { 70 71 /* Entering quotes. Ignore all dots in here. */ 72 73 in_quotes = (in_quotes ? 0 : 1); 74 } 75 } 76 77 /* If not in quotes, then increment counter */ 78 if ((!in_quotes) && (name[i] == '.')) 79 #endif 80 81 /* Increment counter */ 82 if (name[i] == '.') 83 ++ndots; 84 } 85 return (ndots); 86 } 87 88 89 /* 90 * parse_default() 91 * 92 * This function takes the default domain and creates a possible set of 93 * candidate names from it. (similar to the DNS server) 94 */ 95 static int 96 parse_default( 97 nis_name name, /* name given by user */ 98 nis_name local, /* nis_local_dir() */ 99 nis_name *result, /* array of pointers */ 100 int rmax) /* max array value */ 101 { 102 char buf[NIS_MAXSRCHLEN]; /* working name buffer */ 103 char tmpbuf[NIS_MAXSRCHLEN]; /* temp name buffer */ 104 nis_name top; /* ptr to free memory */ 105 nis_name dom, tmpdom; /* temp domain names */ 106 nis_name tmpnam; /* temp domain names */ 107 nis_name mkr = NULL; /* name in domain flag */ 108 int comps = 0; /* # of new components */ 109 110 /* There is a trailing dot, we're done */ 111 if (name[strlen(name)-1] == '.') { 112 char *temp = strdup(name); /* very temporary */ 113 if (!temp) 114 return (0); /* out-of-memory or something */ 115 result[comps++] = temp; 116 return (comps); /* return how many we made */ 117 } 118 119 top = dom = strdup(local); /* local is never NULL */ 120 if (!dom) 121 return (0); /* out-of-memory or something */ 122 123 result[0] = NULL; /* clear result list */ 124 buf[0] = '\0'; /* clear name buffer */ 125 tmpnam = name; /* set tmpnam = name */ 126 tmpdom = dom; /* set tmpdom = dom */ 127 128 /* 129 * The main reason for the following if statement is to make an 130 * intelligent "guess" as to what the user/application intended 131 * as the fully-qualified (FQ) object pathname, given a partially- 132 * qualified (PQ) one. If there is a successful search found, a 133 * single entry, result[0], is initialized with that object name 134 * before proceeding with the normal method of creating names by 135 * continuously stripping off parts of the domain name until we've 136 * exhausted all possibilities. If there is no resulting "guess," 137 * the program proceeds as normal. 138 * 139 * If 'name' is not a substring of domain, then keep stripping off 140 * the labels to check for substrings of the domain. If we've 141 * reached the end with no matches, that's okay, we just proceed 142 * as normal down below, otherwise, if there is a match, then make 143 * the assign to the first result what we think the user intended 144 * as the valid object. Proceed as normal below. 145 * 146 * Examples: PQ OBJ (dynam) DOMAIN NAME (static) ACTION 147 * -------------- -------------------- ------ 148 * 1) org_dir.foo foo.bar.sun.com. (continue) 149 * foo " (done) 150 * (name is a substring of domainname, so 151 * guess is org_dir.foo.bar.sun.com.) 152 * 153 * 2) x.foo.eng foo.bar.sun.com. (continue) 154 * foo.eng " (done) 155 * (guess is x.foo.bar.sun.com.) 156 * 157 * 3) hosts foo.bar.sun.com. (continue) 158 * " (done) 159 * (reached end of name, no guess) 160 */ 161 if (!(mkr = strstr(dom, name))) { 162 163 /* 164 * Don't go past end of 'name'! The rest of this 165 * routine incrementally takes labels from 'name' 166 * and stores them in a buffer until what remains 167 * of name is a substring of the domain name. 168 * 169 * 'tmpnam' is the temporary name pointer, and the 170 * condition in the while statement checks whether 171 * the address has passed the end of 'name'. 172 */ 173 while (tmpnam < (name + strlen(name))) { 174 175 /* save first label */ 176 (void) nis_leaf_of_r(tmpnam, tmpbuf, NIS_MAXSRCHLEN); 177 if (strlcat(buf, tmpbuf, sizeof (buf)) >= 178 sizeof (buf)) { 179 free(top); 180 return (0); 181 } 182 if (strlcat(buf, ".", sizeof (buf)) >= sizeof (buf)) { 183 free(top); 184 return (0); 185 } 186 187 /* strip first label from rest of name */ 188 if (!(tmpnam = (nis_name) __nis_nextsep_of(tmpnam))) 189 break; 190 191 /* if there's a '.' skip past it for next label */ 192 if (*tmpnam == '.') 193 tmpnam++; 194 195 /* reached end without a dot, i.e. "hosts" */ 196 if (*tmpnam == '\0') 197 break; 198 199 /* 200 * If remainder is a substring of domain, break out. 201 * Move the domain pointer up to just past where the 202 * match from 'name' ends, and copy all of what 203 * matches into 'name'. 204 * 205 * Example: (see example 1 in comments above) 206 * 207 * buf = org_dir 208 * tmpnam = foo ('org_dir' stripped off to buf) 209 * tmpdom = foo.bar.sun.com. 210 * ^ 211 * mkr 212 * 213 * We check to make sure that 'mkr' points to a 214 * '.', otherwise, the match isn't correct, i.e. 215 * "foo" is not a label of "foo500" though 216 * it's a substring, hence not-a-match here. 217 */ 218 if ((mkr = strstr(tmpdom, tmpnam)) != 0 && 219 *(mkr+strlen(tmpnam)) == '.') { 220 char *temp; /* very temporary */ 221 222 tmpdom = mkr + strlen(tmpnam); 223 224 /* add label: buf = buf + tmpnam */ 225 if (strlcat(buf, tmpnam, sizeof (buf)) >= 226 sizeof (buf)) { 227 free(top); 228 return (0); 229 } 230 231 /* replace name: name = buf */ 232 (void) strcpy(name, buf); 233 234 /* add domain: buf = name + tmpdom */ 235 if (snprintf(buf, sizeof (buf), "%s%s", name, 236 tmpdom) >= sizeof (buf)) { 237 free(top); 238 return (0); 239 } 240 241 /* write guess: result[0] = buf */ 242 temp = strdup(buf); 243 if (!temp) { 244 free(top); 245 return (0); /* out-of-memory */ 246 } 247 result[comps++] = temp; 248 break; 249 } 250 } 251 } else { 252 253 /* 254 * If 'name' is a proper substring of the current domain name, 255 * chop off the entire name from the domain name up to the point 256 * where they do not match. Store the item as the first result. 257 * 258 * Ex: PQ OBJ (static) DOMAIN NAME (dynamic) ACTION 259 * --------------- --------------------- ------ 260 * foo.bar.sun foo.bar.sun.com. (continue) 261 * com. (done) 262 * (guess is foo.bar.sun.com.) 263 */ 264 if (*(tmpnam = mkr + strlen(name)) == '.') 265 tmpdom = tmpnam; 266 267 /* 268 * Assign this one only if we've actually moved up the tmpdom 269 * past the match *and* that the tail part of name was really 270 * a label, i.e. "eng", instead of a substring, "en". If it 271 * was really a label, then the next character would be a '.'. 272 */ 273 if (*tmpdom == '.') { 274 char *temp; /* very temporary */ 275 if (snprintf(buf, sizeof (buf), "%s%s", name, tmpdom) >= 276 sizeof (buf)) { 277 free(top); 278 return (0); 279 } 280 temp = strdup(buf); 281 if (!temp) { 282 free(top); 283 return (0); /* out-of-memory or something */ 284 } 285 result[comps++] = temp; 286 } 287 } 288 289 /* build the list */ 290 for (; *dom && (comps < rmax); comps++) { 291 292 /* bail if domain name has less than 2 labels */ 293 if (dots_in_name(dom) < 2) 294 break; 295 296 /* put (name + '.' + domain name) together */ 297 if (snprintf(buf, sizeof (buf), "%s.%s", name, dom) >= 298 sizeof (buf)) { 299 free(top); 300 return (0); 301 } 302 303 /* bail if total name has less than 3 labels */ 304 if (dots_in_name(buf) < 3) 305 break; 306 307 /* assign slot if it doesn't match the first elmt */ 308 if ((result[0]) && (strcmp(result[0], buf) == 0)) { 309 comps--; 310 } else { 311 char *temp = strdup(buf); /* very temporary */ 312 if (!temp) { 313 free(top); 314 return (0); /* out-of-memory or something */ 315 } 316 result[comps] = temp; 317 } 318 319 /* out of memory or something */ 320 if (!result[comps]) 321 break; 322 323 /* truncate prefix domain, return rest with '.' in front */ 324 dom = (nis_name) __nis_nextsep_of(dom); 325 326 /* skip past the "." in the new domain name */ 327 if (*dom == '.') 328 dom++; 329 } 330 free(top); /* free locally-allocated domain string */ 331 return (comps); /* return how many we made */ 332 } 333 334 335 /* 336 * __nis_parse_path() 337 * 338 * This function consumes "path" and parses it into a list of 339 * names. Pointers to those names are stored in the array of nis_names 340 * passed as 'list'. 'max' is the length of the array 'list'. 341 * 342 * It malloc's no memory, it only uses the array passed and the string 343 * in path. 344 */ 345 int 346 __nis_parse_path(char *path, nis_name *list, int max) 347 { 348 char *s; 349 int cur; 350 351 /* parse a colon separated list into individual table names */ 352 for (s = path, cur = 0; (*s != '\0') && (cur < max); cur++) { 353 list[cur] = s; 354 /* walk through s until EOS or ':' */ 355 while ((*s != ':') && (*s != '\0')) { 356 s++; 357 #ifdef QUOTES 358 /* Commented out the quoted quotes stuff for 1202807 */ 359 if (*s == '"') { 360 if (*(s+1) == '"') { /* escaped quote */ 361 s += 2; 362 } else { 363 /* skip quoted string */ 364 s++; 365 while (1) { 366 if (*s == '\0') 367 break; 368 /* embedded quote quote */ 369 if ((*s == '"') && 370 (*(s+1) == '"')) { 371 s = s+2; 372 continue; 373 } 374 if (*s == '"') 375 break; 376 s++; 377 } 378 if (*s == '"') 379 s++; 380 } 381 } else 382 s++; 383 #endif 384 } 385 if (*s == ':') { 386 *s = '\0'; 387 s++; 388 } 389 } 390 return (cur); 391 } 392 393 394 /* 395 * parse_path() 396 * 397 * This function returns the number of names it parsed out 398 * of the string. 399 */ 400 static int 401 parse_path(const nis_name name, const char *path, const nis_name local, 402 nis_name *result, int rmax) 403 { 404 int i, comps, cur; 405 size_t len, len1; 406 nis_name list[NIS_MAXPATHDEPTH]; 407 char buf[NIS_MAXSRCHLEN], pbuf[NIS_MAXPATHLEN]; 408 409 /* parse a colon separated list into individual path names */ 410 (void) strncpy(pbuf, path, NIS_MAXPATHLEN); /* local copy of path */ 411 comps = __nis_parse_path(pbuf, list, NIS_MAXPATHDEPTH); 412 413 /* expand "special" names in the path based on $ and + */ 414 for (i = 0, cur = 0; (i < comps) && (cur < rmax); i++) { 415 416 /* if path element is just '$' by itself... */ 417 if ((*(list[i]) == '$') && (*(list[i]+1) == '\0')) { 418 cur += parse_default(name, local, &result[cur], 419 rmax - cur); 420 if (cur > 0 && (!result[cur-1])) 421 break; /* finish early */ 422 423 /* otherwise it's something like org_dir.$ or groups_dir.$ */ 424 } else { 425 len = strlen((char *)(list[i])); 426 /* is last character a $? */ 427 if (*(list[i] + (len - 1)) == '$') { 428 *(list[i] + (len - 1)) = '\0'; 429 len1 = snprintf(buf, sizeof (buf), "%s.%s%s", 430 name, list[i], local); 431 } else 432 len1 = snprintf(buf, sizeof (buf), "%s.%s", 433 name, list[i]); 434 435 /* force ending dot */ 436 if (buf[len1 - 1] != '.') { 437 if (len1 < sizeof (buf) - 1) 438 (void) strcat(buf, "."); 439 else 440 break; /* finish early */ 441 } else if (len1 >= sizeof (buf)) 442 break; /* finish early */ 443 444 result[cur++] = (nis_name) strdup(buf); 445 if (!result[cur-1]) 446 break; /* finish early */ 447 } 448 } 449 return (cur); 450 } 451 452 453 #ifdef ORGDIR 454 /* 455 * This function will append "org_dir" to the standard tables 456 * if already not present ie standard table name is not preceded 457 * by ANY directory name. 458 */ 459 static void 460 append_orgdir(nis_name nname) 461 { 462 char *p1 = nname; 463 int append = 0; 464 465 if (strchr(nname, '.')) 466 return; 467 468 switch (*p1) { 469 470 /* auto_* tables */ 471 case 'a': 472 p1++; 473 474 if (strcmp(p1, "uto_") == 0) 475 append = 1; 476 break; 477 478 /* bootparams */ 479 case 'b': 480 p1++; 481 if (strcmp(p1, "ootparams") == 0) 482 append = 1; 483 break; 484 485 /* cred */ 486 case 'c': 487 p1++; 488 if (strcmp(p1, "red") == 0) 489 append = 1; 490 break; 491 492 /* ethers */ 493 case 'e': 494 p1++; 495 if (strcmp(p1, "thers") == 0) 496 append = 1; 497 break; 498 499 /* group */ 500 case 'g': 501 p1++; 502 if (strcmp(p1, "roup") == 0) 503 append = 1; 504 break; 505 506 /* hosts */ 507 case 'h': 508 p1++; 509 if (strcmp(p1, "osts") == 0) 510 append = 1; 511 break; 512 513 case 'm': 514 p1++; 515 if (strcmp(p1, "ail_aliases") == 0) 516 append = 1; 517 break; 518 519 /* netgroup, netmasks, networks */ 520 case 'n': 521 p1++; 522 if (strcmp(p1, "et") == 0) { 523 p1 = p1 + 2; 524 switch (*p1) { 525 case 'g': 526 p1++; 527 if (strcmp(p1, "roup") == 0) 528 append = 1; 529 break; 530 531 case 'm': 532 p1++; 533 if (strcmp(p1, "asks") == 0) 534 append = 1; 535 break; 536 537 case 'w': 538 p1++; 539 if (strcmp(p1, "orks") == 0) 540 append = 1; 541 break; 542 } 543 } 544 break; 545 546 /* passwd, protocols */ 547 case 'p': 548 p1++; 549 switch (*p1) { 550 case 'a': 551 p1++; 552 if (strcmp(p1, "sswd") == 0) 553 append = 1; 554 break; 555 556 case 'r': 557 p1++; 558 if (strcmp(p1, "otocols") == 0) 559 append = 1; 560 break; 561 } 562 break; 563 564 /* rpc */ 565 case 'r': 566 p1++; 567 if (strcmp(p1, "pc") == 0) 568 append = 1; 569 break; 570 571 /* services */ 572 case 's': 573 p1++; 574 if (strcmp(p1, "ervices") == 0) 575 append = 1; 576 break; 577 578 /* timezone */ 579 case 't': 580 p1++; 581 if (strcmp(p1, "imezone") == 0) 582 append = 1; 583 break; 584 585 } 586 587 if (append) 588 (void) strlcat(nname, ".org_dir", NIS_MAXSRCHLEN); 589 } 590 #endif 591 592 593 /* 594 * This function is called only when the NIS_PATH is set. 595 * It checks for org_dir.org_dir in the pathname. If "org_dir.org_dir" 596 * is present it either removes that entry from the name list( 597 * because if count > 1 there will be a duplicate entry) 598 * otherwise substitute org_dir in place of org_dir.org_dir. 599 * In case the count is 1 we cannot remove the entry, rather 600 * use substitution. This would happen if NIS_PATH = org_dir.$. 601 */ 602 static void 603 check_dup_dnames(nis_name *namelist, int count) 604 { 605 char *p1, *p2; 606 int i; 607 608 for (i = 0; i < count; i++) { 609 if (p1 = strstr(namelist[i], "org_dir.org_dir")) { 610 for (; i < count; i++) { 611 if (count > 1) { 612 namelist[i] = namelist[i+1]; 613 } else { 614 p2 = strchr(p1, '.'); 615 p2++; 616 while (*p1++ = *p2++); 617 *p2 = 0; 618 } 619 } 620 } 621 } 622 } 623 624 /* 625 * __nis_getnames(nis_name name, nis_error *nis_err) 626 * 627 * This function extends the functionality as nis_getnames() to 628 * return the appropriate error in the case of failure. nis_err 629 * is undefined when a non-NULL value is returned. 630 */ 631 nis_name * 632 __nis_getnames(nis_name name, nis_error *nis_err) 633 { 634 int i = 0; 635 nis_name *result; 636 char *local = NULL, /* The local directory */ 637 *path = NULL, /* The search path */ 638 *tmp_path = NULL; /* The search path */ 639 char buf[NIS_MAXSRCHLEN]; 640 641 if (!name) { 642 *nis_err = NIS_BADNAME; 643 return (NULL); 644 } 645 646 if (name[strlen(name)-1] != '.') { 647 result = malloc(NIS_MAXPATHDEPTH * sizeof (nis_name)); 648 if (result == NULL) { 649 *nis_err = NIS_NOMEMORY; 650 return (NULL); 651 } 652 653 tmp_path = path = (char *)getenv("NIS_PATH"); 654 655 if (strlcpy(buf, name, sizeof (buf)) >= sizeof (buf)) { 656 *nis_err = NIS_BADNAME; 657 return (NULL); 658 } 659 #ifdef ORGDIR 660 append_orgdir(buf); 661 #endif 662 663 if (!path) 664 path = "$"; /* default path */ 665 666 /* if can't get local_dir, no need to continue */ 667 if (!(local = nis_local_directory())) { 668 *nis_err = NIS_NOMEMORY; 669 return (NULL); 670 } 671 672 /* parse the path into segments */ 673 i = parse_path(buf, path, local, result, NIS_MAXPATHDEPTH); 674 675 /* check case were name is "near" the root. */ 676 if (i == 0) { 677 if (strlcat(buf, ".", sizeof (buf)) >= sizeof (buf)) { 678 *nis_err = NIS_BADNAME; 679 return (NULL); 680 } 681 if (strlcat(buf, local, sizeof (buf)) >= sizeof (buf)) { 682 *nis_err = NIS_BADNAME; 683 return (NULL); 684 } 685 result[i++] = (nis_name) strdup(buf); 686 } 687 result[i] = NULL; 688 689 /* only if NIS_PATH is set */ 690 if (tmp_path) 691 check_dup_dnames(result, i); 692 693 } else { 694 /* the simple case for a fully-qualified path */ 695 result = malloc(2 * sizeof (nis_name)); 696 if (!result) { 697 *nis_err = NIS_NOMEMORY; 698 return (NULL); 699 } 700 result[0] = (nis_name) strdup((char *)(name)); 701 result[1] = NULL; 702 } 703 704 *nis_err = NIS_SUCCESS; 705 return (result); 706 } 707 708 709 /* 710 * nis_getnames(name, nis_error *nis_err) 711 * 712 * This function returns a list of candidate NIS+ names given an 713 * non fully qualified NIS name. Note it is HOST RFC compliant 714 * in that it stops generating names when the resulting name would 715 * have 2 or fewer dots in it. This helps avoid banging on the root 716 * name servers. 717 */ 718 nis_name * 719 nis_getnames(nis_name name) 720 { 721 nis_error nis_err; 722 723 return (__nis_getnames(name, &nis_err)); 724 } 725 726 727 /* free an entire list, one at a time */ 728 void 729 nis_freenames(nis_name *namelist) 730 { 731 int i = 0; 732 733 while (namelist[i]) 734 free(namelist[i++]); 735 free(namelist); 736 } 737