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