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 _is_nss_lookup_by_name(int attrdb, nss_dbop_t op) { 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, op_num, 705 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, args); 712 if (args->returnval == 0) 713 continue; 714 if (!be->permit_netgroups || 715 !netgr_in(be, instr + 2, (*be->getnamef)(args))) 716 continue; 717 } 718 } /* end of case 1 */ 719 else if (instr[1] == '\0') { 720 /* 721 * Case 2: 722 * The entry is of the form "+" or "-". The former 723 * allows all entries from name services. The latter 724 * is illegal and ought to be ignored. 725 */ 726 if (instr[0] == '-') 727 continue; 728 /* need to search for "+" entry */ 729 nss_search(be->db_rootp, be->db_initf, op_num, args); 730 if (args->returnval == 0) 731 continue; 732 } /* end of case 2 */ 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 } /* end of case 3 */ 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