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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Common code and structures used by name-service-switch "compat" backends. 26 * 27 * Most of the code in the "compat" backend is a perverted form of code from 28 * the "files" backend; this file is no exception. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <bsm/libbsm.h> 38 #include <user_attr.h> 39 #include "compat_common.h" 40 41 /* 42 * This should be in a header. 43 */ 44 45 extern int yp_get_default_domain(char **domain); 46 47 /* 48 * Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current 49 * implementation is completely moronic; we use a linked list. But then 50 * that's what it's always done in 4.x... 51 */ 52 53 struct setofstrings { 54 char *name; 55 struct setofstrings *next; 56 /* 57 * === Should get smart and malloc the string and pointer as one 58 * object rather than two. 59 */ 60 }; 61 typedef struct setofstrings *strset_t; 62 63 static void 64 strset_free(ssp) 65 strset_t *ssp; 66 { 67 strset_t cur, nxt; 68 69 for (cur = *ssp; cur != 0; cur = nxt) { 70 nxt = cur->next; 71 free(cur->name); 72 free(cur); 73 } 74 *ssp = 0; 75 } 76 77 static boolean_t 78 strset_add(ssp, nam) 79 strset_t *ssp; 80 const char *nam; 81 { 82 strset_t new; 83 84 if (0 == (new = (strset_t)malloc(sizeof (*new)))) { 85 return (B_FALSE); 86 } 87 if (0 == (new->name = malloc(strlen(nam) + 1))) { 88 free(new); 89 return (B_FALSE); 90 } 91 strcpy(new->name, nam); 92 new->next = *ssp; 93 *ssp = new; 94 return (B_TRUE); 95 } 96 97 static boolean_t 98 strset_in(ssp, nam) 99 const strset_t *ssp; 100 const char *nam; 101 { 102 strset_t cur; 103 104 for (cur = *ssp; cur != 0; cur = cur->next) { 105 if (strcmp(cur->name, nam) == 0) { 106 return (B_TRUE); 107 } 108 } 109 return (B_FALSE); 110 } 111 112 113 struct compat_backend { 114 compat_backend_op_t *ops; 115 int n_ops; 116 const char *filename; 117 FILE *f; 118 int minbuf; 119 char *buf; 120 int linelen; /* <== Explain use, lifetime */ 121 122 nss_db_initf_t db_initf; 123 nss_db_root_t *db_rootp; /* Shared between instances */ 124 nss_getent_t db_context; /* Per-instance enumeration */ 125 126 compat_get_name getnamef; 127 compat_merge_func mergef; 128 129 /* We wouldn't need all this hokey state stuff if we */ 130 /* used another thread to implement a coroutine... */ 131 enum { 132 GETENT_FILE, 133 GETENT_NETGROUP, 134 GETENT_ATTRDB, 135 GETENT_ALL, 136 GETENT_DONE 137 } state; 138 strset_t minuses; 139 140 int permit_netgroups; 141 const char *yp_domain; 142 nss_backend_t *getnetgrent_backend; 143 char *netgr_buffer; 144 }; 145 146 147 /* 148 * Lookup and enumeration routines for +@group and -@group. 149 * 150 * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than 151 * is really healthy. The set/get/end routines below duplicate code 152 * from that file, but keep the state information per-backend-instance 153 * instead of just per-process. 154 */ 155 156 extern void _nss_initf_netgroup(nss_db_params_t *); 157 /* 158 * Should really share the db_root in getnetgrent.c in order to get the 159 * resource-management quotas right, but this will have to do. 160 */ 161 static DEFINE_NSS_DB_ROOT(netgr_db_root); 162 163 static boolean_t 164 netgr_in(compat_backend_ptr_t be, const char *group, const char *user) 165 { 166 if (be->yp_domain == 0) { 167 if (yp_get_default_domain((char **)&be->yp_domain) != 0) { 168 return (B_FALSE); 169 } 170 } 171 return (innetgr(group, 0, user, be->yp_domain)); 172 } 173 174 static boolean_t 175 netgr_all_in(compat_backend_ptr_t be, const char *group) 176 { 177 /* 178 * 4.x does this; ours not to reason why... 179 */ 180 return (netgr_in(be, group, "*")); 181 } 182 183 static void 184 netgr_set(be, netgroup) 185 compat_backend_ptr_t be; 186 const char *netgroup; 187 { 188 /* 189 * ===> Need comment to explain that this first "if" is optimizing 190 * for the same-netgroup-as-last-time case 191 */ 192 if (be->getnetgrent_backend != 0 && 193 NSS_INVOKE_DBOP(be->getnetgrent_backend, 194 NSS_DBOP_SETENT, 195 (void *) netgroup) != NSS_SUCCESS) { 196 NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR, 197 0); 198 be->getnetgrent_backend = 0; 199 } 200 if (be->getnetgrent_backend == 0) { 201 struct nss_setnetgrent_args args; 202 203 args.netgroup = netgroup; 204 args.iterator = 0; 205 nss_search(&netgr_db_root, _nss_initf_netgroup, 206 NSS_DBOP_NETGROUP_SET, &args); 207 be->getnetgrent_backend = args.iterator; 208 } 209 } 210 211 static boolean_t 212 netgr_next_u(be, up) 213 compat_backend_ptr_t be; 214 char **up; 215 { 216 if (be->netgr_buffer == 0 && 217 (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) { 218 /* Out of memory */ 219 return (B_FALSE); 220 } 221 222 do { 223 struct nss_getnetgrent_args args; 224 225 args.buffer = be->netgr_buffer; 226 args.buflen = NSS_BUFLEN_NETGROUP; 227 args.status = NSS_NETGR_NO; 228 229 if (be->getnetgrent_backend != 0) { 230 NSS_INVOKE_DBOP(be->getnetgrent_backend, 231 NSS_DBOP_GETENT, &args); 232 } 233 234 if (args.status == NSS_NETGR_FOUND) { 235 *up = args.retp[NSS_NETGR_USER]; 236 } else { 237 return (B_FALSE); 238 } 239 } while (*up == 0); 240 return (B_TRUE); 241 } 242 243 static void 244 netgr_end(be) 245 compat_backend_ptr_t be; 246 { 247 if (be->getnetgrent_backend != 0) { 248 NSS_INVOKE_DBOP(be->getnetgrent_backend, 249 NSS_DBOP_DESTRUCTOR, 0); 250 be->getnetgrent_backend = 0; 251 } 252 if (be->netgr_buffer != 0) { 253 free(be->netgr_buffer); 254 be->netgr_buffer = 0; 255 } 256 } 257 258 259 #define MAXFIELDS 9 /* Sufficient for passwd (7), shadow (9), group (4) */ 260 261 static nss_status_t 262 do_merge(be, args, instr, linelen) 263 compat_backend_ptr_t be; 264 nss_XbyY_args_t *args; 265 const char *instr; 266 int linelen; 267 { 268 char *fields[MAXFIELDS]; 269 int i; 270 int overrides; 271 const char *p; 272 const char *end = instr + linelen; 273 nss_status_t res; 274 275 /* 276 * Potential optimization: only perform the field-splitting nonsense 277 * once per input line (at present, "+" and "+@netgroup" entries 278 * will cause us to do this multiple times in getent() requests). 279 */ 280 281 for (i = 0; i < MAXFIELDS; i++) { 282 fields[i] = 0; 283 } 284 for (p = instr, overrides = 0, i = 0; /* no test */; i++) { 285 const char *q = memchr(p, ':', end - p); 286 const char *r = (q == 0) ? end : q; 287 ssize_t len = r - p; 288 289 if (len > 0) { 290 char *s = malloc(len + 1); 291 if (s == 0) { 292 overrides = -1; /* Indicates "you lose" */ 293 break; 294 } 295 memcpy(s, p, len); 296 s[len] = '\0'; 297 fields[i] = s; 298 overrides++; 299 } 300 if (q == 0) { 301 /* End of line */ 302 break; 303 } else { 304 /* Skip the colon at (*q) */ 305 p = q + 1; 306 } 307 } 308 if (overrides == 1) { 309 /* No real overrides, return (*args) intact */ 310 res = NSS_SUCCESS; 311 } else if (overrides > 1) { 312 /* 313 * The zero'th field is always nonempty (+/-...), but at least 314 * one other field was also nonempty, i.e. wants to override 315 */ 316 switch ((*be->mergef)(be, args, (const char **)fields)) { 317 case NSS_STR_PARSE_SUCCESS: 318 args->returnval = args->buf.result; 319 args->erange = 0; 320 res = NSS_SUCCESS; 321 break; 322 case NSS_STR_PARSE_ERANGE: 323 args->returnval = 0; 324 args->erange = 1; 325 res = NSS_NOTFOUND; 326 break; 327 case NSS_STR_PARSE_PARSE: 328 args->returnval = 0; 329 args->erange = 0; 330 /* ===> Very likely the wrong thing to do... */ 331 res = NSS_NOTFOUND; 332 break; 333 } 334 } else { 335 args->returnval = 0; 336 args->erange = 0; 337 res = NSS_UNAVAIL; /* ==> Right? */ 338 } 339 340 for (i = 0; i < MAXFIELDS; i++) { 341 if (fields[i] != 0) { 342 free(fields[i]); 343 } 344 } 345 346 return (res); 347 } 348 349 /*ARGSUSED*/ 350 nss_status_t 351 _nss_compat_setent(be, dummy) 352 compat_backend_ptr_t be; 353 void *dummy; 354 { 355 if (be->f == 0) { 356 if (be->filename == 0) { 357 /* Backend isn't initialized properly? */ 358 return (NSS_UNAVAIL); 359 } 360 if ((be->f = fopen(be->filename, "rF")) == 0) { 361 return (NSS_UNAVAIL); 362 } 363 } else { 364 rewind(be->f); 365 } 366 strset_free(&be->minuses); 367 /* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */ 368 369 if ((strcmp(be->filename, USERATTR_FILENAME) == 0) || 370 (strcmp(be->filename, AUDITUSER_FILENAME) == 0)) 371 be->state = GETENT_ATTRDB; 372 else 373 be->state = GETENT_FILE; 374 375 /* ===> ?? netgroup stuff? */ 376 return (NSS_SUCCESS); 377 } 378 379 /*ARGSUSED*/ 380 nss_status_t 381 _nss_compat_endent(be, dummy) 382 compat_backend_ptr_t be; 383 void *dummy; 384 { 385 if (be->f != 0) { 386 fclose(be->f); 387 be->f = 0; 388 } 389 if (be->buf != 0) { 390 free(be->buf); 391 be->buf = 0; 392 } 393 nss_endent(be->db_rootp, be->db_initf, &be->db_context); 394 395 be->state = GETENT_FILE; /* Probably superfluous but comforting */ 396 strset_free(&be->minuses); 397 netgr_end(be); 398 399 /* 400 * Question: from the point of view of resource-freeing vs. time to 401 * start up again, how much should we do in endent() and how much 402 * in the destructor? 403 */ 404 return (NSS_SUCCESS); 405 } 406 407 /*ARGSUSED*/ 408 nss_status_t 409 _nss_compat_destr(be, dummy) 410 compat_backend_ptr_t be; 411 void *dummy; 412 { 413 if (be != 0) { 414 if (be->f != 0) { 415 _nss_compat_endent(be, 0); 416 } 417 nss_delete(be->db_rootp); 418 nss_delete(&netgr_db_root); 419 free(be); 420 } 421 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 422 } 423 424 static int 425 read_line(f, buffer, buflen) 426 FILE *f; 427 char *buffer; 428 int buflen; 429 { 430 /*CONSTCOND*/ 431 while (1) { 432 int linelen; 433 434 if (fgets(buffer, buflen, f) == 0) { 435 /* End of file */ 436 return (-1); 437 } 438 linelen = strlen(buffer); 439 /* linelen >= 1 (since fgets didn't return 0) */ 440 441 if (buffer[linelen - 1] == '\n') { 442 /* 443 * ===> The code below that calls read_line() doesn't 444 * play by the rules; it assumes in places that 445 * the line is null-terminated. For now we'll 446 * humour it. 447 */ 448 buffer[--linelen] = '\0'; 449 return (linelen); 450 } 451 if (feof(f)) { 452 /* Line is last line in file, and has no newline */ 453 return (linelen); 454 } 455 /* Line too long for buffer; toss it and loop for next line */ 456 /* ===== should syslog() in cases where previous code did */ 457 while (fgets(buffer, buflen, f) != 0 && 458 buffer[strlen(buffer) - 1] != '\n') { 459 ; 460 } 461 } 462 } 463 464 static int 465 is_nss_lookup_by_name(int attrdb, nss_dbop_t op) 466 { 467 int result = 0; 468 469 if ((attrdb != 0) && 470 ((op == NSS_DBOP_AUDITUSER_BYNAME) || 471 (op == NSS_DBOP_USERATTR_BYNAME))) { 472 result = 1; 473 } else if ((attrdb == 0) && 474 ((op == NSS_DBOP_GROUP_BYNAME) || 475 (op == NSS_DBOP_PASSWD_BYNAME) || 476 (op == NSS_DBOP_SHADOW_BYNAME))) { 477 result = 1; 478 } 479 480 return (result); 481 } 482 483 /*ARGSUSED*/ 484 nss_status_t 485 _attrdb_compat_XY_all(be, argp, netdb, check, op_num) 486 compat_backend_ptr_t be; 487 nss_XbyY_args_t *argp; 488 int netdb; 489 compat_XY_check_func check; 490 nss_dbop_t op_num; 491 { 492 int parsestat; 493 int (*func)(); 494 const char *filter = argp->key.name; 495 nss_status_t res; 496 497 #ifdef DEBUG 498 (void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n"); 499 #endif /* DEBUG */ 500 501 if (be->buf == 0 && 502 (be->buf = malloc(be->minbuf)) == 0) { 503 return (NSS_UNAVAIL); 504 } 505 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { 506 return (res); 507 } 508 res = NSS_NOTFOUND; 509 510 /*CONSTCOND*/ 511 while (1) { 512 int linelen; 513 char *instr = be->buf; 514 515 if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) { 516 /* End of file */ 517 argp->returnval = 0; 518 argp->erange = 0; 519 break; 520 } 521 if (filter != 0 && strstr(instr, filter) == 0) { 522 /* 523 * Optimization: if the entry doesn't contain the 524 * filter string then it can't be the entry we want, 525 * so don't bother looking more closely at it. 526 */ 527 continue; 528 } 529 if (netdb) { 530 char *first; 531 char *last; 532 533 if ((last = strchr(instr, '#')) == 0) { 534 last = instr + linelen; 535 } 536 *last-- = '\0'; /* Nuke '\n' or #comment */ 537 538 /* 539 * Skip leading whitespace. Normally there isn't 540 * any, so it's not worth calling strspn(). 541 */ 542 for (first = instr; isspace(*first); first++) { 543 ; 544 } 545 if (*first == '\0') { 546 continue; 547 } 548 /* 549 * Found something non-blank on the line. Skip back 550 * over any trailing whitespace; since we know 551 * there's non-whitespace earlier in the line, 552 * checking for termination is easy. 553 */ 554 while (isspace(*last)) { 555 --last; 556 } 557 linelen = last - first + 1; 558 if (first != instr) { 559 instr = first; 560 } 561 } 562 argp->returnval = 0; 563 func = argp->str2ent; 564 parsestat = (*func)(instr, linelen, argp->buf.result, 565 argp->buf.buffer, argp->buf.buflen); 566 if (parsestat == NSS_STR_PARSE_SUCCESS) { 567 argp->returnval = argp->buf.result; 568 if (check == 0 || (*check)(argp)) { 569 res = NSS_SUCCESS; 570 break; 571 } 572 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 573 argp->erange = 1; 574 break; 575 } 576 } 577 /* 578 * stayopen is set to 0 by default in order to close the opened 579 * file. Some applications may break if it is set to 1. 580 */ 581 if (check != 0 && !argp->stayopen) { 582 (void) _nss_compat_endent(be, 0); 583 } 584 585 if (res != NSS_SUCCESS) { 586 if ((op_num == NSS_DBOP_USERATTR_BYNAME) || 587 (op_num == NSS_DBOP_AUDITUSER_BYNAME)) { 588 res = nss_search(be->db_rootp, 589 be->db_initf, 590 op_num, 591 argp); 592 } else { 593 res = nss_getent(be->db_rootp, 594 be->db_initf, &be->db_context, argp); 595 } 596 if (res != NSS_SUCCESS) { 597 argp->returnval = 0; 598 argp->erange = 0; 599 } 600 } 601 602 return (res); 603 } 604 605 nss_status_t 606 _nss_compat_XY_all(be, args, check, op_num) 607 compat_backend_ptr_t be; 608 nss_XbyY_args_t *args; 609 compat_XY_check_func check; 610 nss_dbop_t op_num; 611 { 612 nss_status_t res; 613 int parsestat; 614 615 if (be->buf == 0 && 616 (be->buf = malloc(be->minbuf)) == 0) { 617 return (NSS_UNAVAIL); /* really panic, malloc failed */ 618 } 619 620 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { 621 return (res); 622 } 623 624 res = NSS_NOTFOUND; 625 626 /*CONSTCOND*/ 627 while (1) { 628 int linelen; 629 char *instr = be->buf; 630 char *colon; 631 632 linelen = read_line(be->f, instr, be->minbuf); 633 if (linelen < 0) { 634 /* End of file */ 635 args->returnval = 0; 636 args->erange = 0; 637 break; 638 } 639 640 args->returnval = 0; /* reset for both types of entries */ 641 642 if (instr[0] != '+' && instr[0] != '-') { 643 /* Simple, wholesome, God-fearing entry */ 644 parsestat = (*args->str2ent)(instr, linelen, 645 args->buf.result, 646 args->buf.buffer, 647 args->buf.buflen); 648 if (parsestat == NSS_STR_PARSE_SUCCESS) { 649 args->returnval = args->buf.result; 650 if ((*check)(args) != 0) { 651 res = NSS_SUCCESS; 652 break; 653 } 654 655 /* ===> Check the Dani logic here... */ 656 657 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 658 args->erange = 1; 659 res = NSS_NOTFOUND; 660 break; 661 /* should we just skip this one long line ? */ 662 } /* else if (parsestat == NSS_STR_PARSE_PARSE) */ 663 /* don't care ! */ 664 665 /* ==> ?? */ continue; 666 } 667 668 /* 669 * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup" 670 * 671 * This code is optimized for lookups by name. 672 * 673 * For lookups by identifier search key cannot be matched with 674 * the name of the "+" or "-" entry. So nss_search() is to be 675 * called before extracting the name i.e. via (*be->getnamef)(). 676 * 677 * But for lookups by name, search key is compared with the name 678 * of the "+" or "-" entry to acquire a match and thus 679 * unnesessary calls to nss_search() is eliminated. Also for 680 * matching "-" entries, calls to nss_search() is eliminated. 681 */ 682 683 if ((colon = strchr(instr, ':')) != 0) { 684 *colon = '\0'; /* terminate field to extract name */ 685 } 686 687 if (instr[1] == '@') { 688 /* 689 * Case 1: 690 * The entry is of the form "+@netgroup" or 691 * "-@netgroup". If we're performing a lookup by name, 692 * we can simply extract the name from the search key 693 * (i.e. args->key.name). If not, then we must call 694 * nss_search() before extracting the name via the 695 * get_XXname() function. i.e. (*be->getnamef)(args). 696 */ 697 if (is_nss_lookup_by_name(0, op_num) != 0) { 698 /* compare then search */ 699 if (!be->permit_netgroups || 700 !netgr_in(be, instr + 2, args->key.name)) 701 continue; 702 if (instr[0] == '+') { 703 /* need to search for "+" entry */ 704 nss_search(be->db_rootp, be->db_initf, 705 op_num, args); 706 if (args->returnval == 0) 707 continue; 708 } 709 } else { 710 /* search then compare */ 711 nss_search(be->db_rootp, be->db_initf, op_num, 712 args); 713 if (args->returnval == 0) 714 continue; 715 if (!be->permit_netgroups || 716 !netgr_in(be, instr + 2, 717 (*be->getnamef)(args))) 718 continue; 719 } 720 } else if (instr[1] == '\0') { 721 /* 722 * Case 2: 723 * The entry is of the form "+" or "-". The former 724 * allows all entries from name services. The latter 725 * is illegal and ought to be ignored. 726 */ 727 if (instr[0] == '-') 728 continue; 729 /* need to search for "+" entry */ 730 nss_search(be->db_rootp, be->db_initf, op_num, args); 731 if (args->returnval == 0) 732 continue; 733 } else { 734 /* 735 * Case 3: 736 * The entry is of the form "+name" or "-name". 737 * If we're performing a lookup by name, we can simply 738 * extract the name from the search key 739 * (i.e. args->key.name). If not, then we must call 740 * nss_search() before extracting the name via the 741 * get_XXname() function. i.e. (*be->getnamef)(args). 742 */ 743 if (is_nss_lookup_by_name(0, op_num) != 0) { 744 /* compare then search */ 745 if (strcmp(instr + 1, args->key.name) != 0) 746 continue; 747 if (instr[0] == '+') { 748 /* need to search for "+" entry */ 749 nss_search(be->db_rootp, be->db_initf, 750 op_num, args); 751 if (args->returnval == 0) 752 continue; 753 } 754 } else { 755 /* search then compare */ 756 nss_search(be->db_rootp, be->db_initf, op_num, 757 args); 758 if (args->returnval == 0) 759 continue; 760 if (strcmp(instr + 1, (*be->getnamef)(args)) 761 != 0) 762 continue; 763 } 764 } 765 if (instr[0] == '-') { 766 /* no need to search for "-" entry */ 767 args->returnval = 0; 768 args->erange = 0; 769 res = NSS_NOTFOUND; 770 } else { 771 if (colon != 0) 772 *colon = ':'; /* restoration */ 773 res = do_merge(be, args, instr, linelen); 774 } 775 break; 776 } 777 778 /* 779 * stayopen is set to 0 by default in order to close the opened 780 * file. Some applications may break if it is set to 1. 781 */ 782 if (!args->stayopen) { 783 (void) _nss_compat_endent(be, 0); 784 } 785 786 return (res); 787 } 788 789 nss_status_t 790 _nss_compat_getent(be, a) 791 compat_backend_ptr_t be; 792 void *a; 793 { 794 nss_XbyY_args_t *args = (nss_XbyY_args_t *)a; 795 nss_status_t res; 796 char *colon = 0; /* <=== need comment re lifetime */ 797 798 if (be->f == 0) { 799 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { 800 return (res); 801 } 802 } 803 804 if (be->buf == 0 && 805 (be->buf = malloc(be->minbuf)) == 0) { 806 return (NSS_UNAVAIL); /* really panic, malloc failed */ 807 } 808 809 /*CONSTCOND*/ 810 while (1) { 811 char *instr = be->buf; 812 int linelen; 813 char *name; /* === Need more distinctive label */ 814 const char *savename; 815 816 /* 817 * In the code below... 818 * break means "I found one, I think" (i.e. goto the 819 * code after the end of the switch statement), 820 * continue means "Next candidate" 821 * (i.e. loop around to the switch statement), 822 * return means "I'm quite sure" (either Yes or No). 823 */ 824 switch (be->state) { 825 826 case GETENT_DONE: 827 args->returnval = 0; 828 args->erange = 0; 829 return (NSS_NOTFOUND); 830 831 case GETENT_ATTRDB: 832 res = _attrdb_compat_XY_all(be, 833 args, 1, (compat_XY_check_func)NULL, 0); 834 return (res); 835 836 case GETENT_FILE: 837 linelen = read_line(be->f, instr, be->minbuf); 838 if (linelen < 0) { 839 /* End of file */ 840 be->state = GETENT_DONE; 841 continue; 842 } 843 if ((colon = strchr(instr, ':')) != 0) { 844 *colon = '\0'; 845 } 846 if (instr[0] == '-') { 847 if (instr[1] != '@') { 848 strset_add(&be->minuses, instr + 1); 849 } else if (be->permit_netgroups) { 850 netgr_set(be, instr + 2); 851 while (netgr_next_u(be, &name)) { 852 strset_add(&be->minuses, 853 name); 854 } 855 netgr_end(be); 856 } /* Else (silently) ignore the entry */ 857 continue; 858 } else if (instr[0] != '+') { 859 int parsestat; 860 /* 861 * Normal entry, no +/- nonsense 862 */ 863 if (colon != 0) { 864 *colon = ':'; 865 } 866 args->returnval = 0; 867 parsestat = (*args->str2ent)(instr, linelen, 868 args->buf.result, 869 args->buf.buffer, 870 args->buf.buflen); 871 if (parsestat == NSS_STR_PARSE_SUCCESS) { 872 args->returnval = args->buf.result; 873 return (NSS_SUCCESS); 874 } 875 /* ==> ?? Treat ERANGE differently ?? */ 876 if (parsestat == NSS_STR_PARSE_ERANGE) { 877 args->returnval = 0; 878 args->erange = 1; 879 return (NSS_NOTFOUND); 880 } 881 /* Skip the offending entry, get next */ 882 continue; 883 } else if (instr[1] == '\0') { 884 /* Plain "+" */ 885 nss_setent(be->db_rootp, be->db_initf, 886 &be->db_context); 887 be->state = GETENT_ALL; 888 be->linelen = linelen; 889 continue; 890 } else if (instr[1] == '@') { 891 /* "+@netgroup" */ 892 netgr_set(be, instr + 2); 893 be->state = GETENT_NETGROUP; 894 be->linelen = linelen; 895 continue; 896 } else { 897 /* "+name" */ 898 name = instr + 1; 899 break; 900 } 901 /* NOTREACHED */ 902 903 case GETENT_ALL: 904 linelen = be->linelen; 905 args->returnval = 0; 906 nss_getent(be->db_rootp, be->db_initf, 907 &be->db_context, args); 908 if (args->returnval == 0) { 909 /* ==> ?? Treat ERANGE differently ?? */ 910 nss_endent(be->db_rootp, be->db_initf, 911 &be->db_context); 912 be->state = GETENT_FILE; 913 continue; 914 } 915 if (strset_in(&be->minuses, (*be->getnamef)(args))) { 916 continue; 917 } 918 name = 0; /* tell code below we've done the lookup */ 919 break; 920 921 case GETENT_NETGROUP: 922 linelen = be->linelen; 923 if (!netgr_next_u(be, &name)) { 924 netgr_end(be); 925 be->state = GETENT_FILE; 926 continue; 927 } 928 /* pass "name" variable to code below... */ 929 break; 930 } 931 932 if (name != 0) { 933 if (strset_in(&be->minuses, name)) { 934 continue; 935 } 936 /* 937 * Do a getXXXnam(name). If we were being pure, 938 * we'd introduce yet another function-pointer 939 * that the database-specific code had to supply 940 * to us. Instead we'll be grotty and hard-code 941 * the knowledge that 942 * (a) The username is always passwd in key.name, 943 * (b) NSS_DBOP_PASSWD_BYNAME == 944 * NSS_DBOP_SHADOW_BYNAME == 945 * NSS_DBOP_next_iter. 946 */ 947 savename = args->key.name; 948 args->key.name = name; 949 args->returnval = 0; 950 nss_search(be->db_rootp, be->db_initf, 951 NSS_DBOP_next_iter, args); 952 args->key.name = savename; /* In case anyone cares */ 953 } 954 /* 955 * Found one via "+", "+name" or "@netgroup". 956 * Override some fields if the /etc file says to do so. 957 */ 958 if (args->returnval == 0) { 959 /* ==> ?? Should treat erange differently? */ 960 continue; 961 } 962 /* 'colon' was set umpteen iterations ago in GETENT_FILE */ 963 if (colon != 0) { 964 *colon = ':'; 965 colon = 0; 966 } 967 return (do_merge(be, args, instr, linelen)); 968 } 969 } 970 971 /* We don't use this directly; we just copy the bits when we want to */ 972 /* initialize the variable (in the compat_backend struct) that we do use */ 973 static DEFINE_NSS_GETENT(context_initval); 974 975 nss_backend_t * 976 _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups, 977 getname_func, merge_func) 978 compat_backend_op_t ops[]; 979 int n_ops; 980 const char *filename; 981 int min_bufsize; 982 nss_db_root_t *rootp; 983 nss_db_initf_t initf; 984 int netgroups; 985 compat_get_name getname_func; 986 compat_merge_func merge_func; 987 { 988 compat_backend_ptr_t be; 989 990 if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) { 991 return (0); 992 } 993 be->ops = ops; 994 be->n_ops = n_ops; 995 be->filename = filename; 996 be->f = 0; 997 be->minbuf = min_bufsize; 998 be->buf = 0; 999 1000 be->db_rootp = rootp; 1001 be->db_initf = initf; 1002 be->db_context = context_initval; 1003 1004 be->getnamef = getname_func; 1005 be->mergef = merge_func; 1006 1007 if ((strcmp(be->filename, USERATTR_FILENAME) == 0) || 1008 (strcmp(be->filename, AUDITUSER_FILENAME) == 0)) 1009 be->state = GETENT_ATTRDB; 1010 else 1011 be->state = GETENT_FILE; /* i.e. do Automatic setent(); */ 1012 1013 be->minuses = 0; 1014 1015 be->permit_netgroups = netgroups; 1016 be->yp_domain = 0; 1017 be->getnetgrent_backend = 0; 1018 be->netgr_buffer = 0; 1019 1020 return ((nss_backend_t *)be); 1021 } 1022