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 2012 Nexenta Systems, Inc. All rights reserved. 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* 29 * files/getnetgrent.c -- "files" backend for nsswitch "netgroup" database 30 * 31 * The API for netgroups differs sufficiently from that for the average 32 * getXXXbyYYY function that we use very few of the support routines in 33 * files_common.h. 34 * 35 * The implementation of setnetgrent()/getnetgrent() here follows the 36 * the 4.x code, inasmuch as the setnetgrent() routine does all the work 37 * of traversing the netgroup graph and building a (potentially large) 38 * list in memory, and getnetgrent() just steps down the list. 39 * 40 * An alternative, and probably better, implementation would lazy-eval 41 * the netgroup graph in response to getnetgrent() calls (though 42 * setnetgrent() should still check for the top-level netgroup name 43 * and return NSS_SUCCESS / NSS_NOTFOUND). 44 */ 45 46 #include "files_common.h" 47 #include <ctype.h> 48 #include <rpcsvc/ypclnt.h> 49 #include <malloc.h> 50 #include <string.h> 51 #include <ctype.h> 52 #include <sys/sysmacros.h> 53 54 /* 55 * Tricky debug support 56 */ 57 58 #pragma weak __nss_files_netgr_debug 59 #pragma weak __nss_files_netgr_error 60 extern void __nss_files_netgr_debug(const char *, ...); 61 extern void __nss_files_netgr_error(const char *, ...); 62 63 /* 64 * Start of stuff borrowed from getgrent.c 65 */ 66 static uint_t 67 hash_netgrname(nss_XbyY_args_t *argp, int keyhash, const char *line, 68 int linelen) 69 { 70 const char *name; 71 uint_t namelen, i; 72 uint_t hash = 0; 73 74 if (keyhash) { 75 name = argp->key.name; 76 namelen = strlen(name); 77 } else { 78 name = line; 79 namelen = 0; 80 while (linelen-- && !isspace(*line)) { 81 line++; 82 namelen++; 83 } 84 } 85 86 for (i = 0; i < namelen; i++) 87 hash = hash * 15 + name[i]; 88 return (hash); 89 } 90 91 static files_hash_func hash_netgr[1] = { hash_netgrname }; 92 93 static files_hash_t hashinfo = { 94 DEFAULTMUTEX, 95 sizeof (struct nss_netgrent), 96 NSS_LINELEN_NETGROUP, 97 1, 98 hash_netgr 99 }; 100 101 static int 102 check_netgrname(nss_XbyY_args_t *argp, const char *line, int linelen) 103 { 104 const char *linep, *limit; 105 const char *keyp = argp->key.name; 106 107 linep = line; 108 limit = line + linelen; 109 110 /* +/- entries valid for compat source only */ 111 if (linelen == 0 || *line == '+' || *line == '-') 112 return (0); 113 while (*keyp && linep < limit && *keyp == *linep) { 114 keyp++; 115 linep++; 116 } 117 return (linep < limit && *keyp == '\0' && isspace(*linep)); 118 } 119 120 static nss_status_t 121 getbyname(files_backend_ptr_t be, void *a) 122 { 123 return (_nss_files_XY_hash(be, a, 1, &hashinfo, 0, check_netgrname)); 124 } 125 126 /* 127 * End of stuff borrowed from getgrent.c 128 * 129 * Now some "glue" functions based loosely on 130 * lib/libc/port/gen/getgrnam_r.c 131 */ 132 133 134 /* 135 * This is a special purpose str2ent (parse) function used only in 136 * the _nss_files_getbyname() below. A general-purpose version of 137 * this parser would copy the incoming line buffer to the passed 138 * temporary buffer, and fill in the passed struct nss_netgrent with 139 * pointers into that temporary buffer. Our caller only needs the 140 * list of members of this netgroup, and since that string already 141 * exists in ready-to-use form in the incoming line buffer, we just 142 * use that. Also special here is the fact that we allocate a copy 143 * of the member list, both because the caller wants it allocated, 144 * and because the buffer at *instr will change after we return. 145 * The caller passes null for a temporary buffer, which we ignore. 146 * 147 * See the test program: cmd/nsstest/netgr_get.c 148 * for a more generic version of this function. 149 */ 150 static int 151 str2netgr(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 152 { 153 const char sep[] = " \t\n"; 154 struct nss_netgrent *netgr = ent; 155 const char *p; 156 157 /* skip leading space */ 158 p = instr; 159 while (isspace(*p)) 160 p++; 161 162 /* should be at the key */ 163 if (*p == '\0') 164 return (NSS_STR_PARSE_PARSE); 165 /* Full parser would set netgr_name = p here. */ 166 167 /* skip the key ... */ 168 p = strpbrk(p, sep); 169 if (p == NULL) 170 return (NSS_STR_PARSE_PARSE); 171 /* Full parser would store a null at *p here. */ 172 173 /* skip separators */ 174 while (isspace(*p)) 175 p++; 176 177 /* 178 * Should be at the members list, which is the 179 * rest of the input line. 180 */ 181 if (*p == '\0') 182 return (NSS_STR_PARSE_PARSE); 183 184 /* 185 * Caller wants this allocated. Do it now, 186 * before the inbuf gets re-used. 187 */ 188 netgr->netgr_members = strdup(p); 189 if (netgr->netgr_members == NULL) 190 return (NSS_STR_PARSE_PARSE); 191 192 return (NSS_STR_PARSE_SUCCESS); 193 } 194 195 /* 196 * This is a compatibility "shim" used by top_down() to get 197 * the list of members for some netgroup. On success, the 198 * list of members is returned in allocated memory via valp. 199 */ 200 static nss_status_t 201 netgr_get_members(struct files_backend *be, 202 const char *name, char **valp) 203 { 204 struct nss_netgrent netgr; 205 nss_XbyY_args_t args; 206 nss_status_t result; 207 208 if (name == (const char *)NULL) 209 return (NSS_ERROR); 210 211 (void) memset(&netgr, '\0', sizeof (netgr)); 212 (void) memset(&args, '\0', sizeof (args)); 213 args.buf.result = &netgr; 214 args.str2ent = str2netgr; 215 args.key.name = name; 216 result = getbyname(be, &args); 217 218 if (result == NSS_SUCCESS) { 219 /* Note: allocated memory. */ 220 *valp = netgr.netgr_members; 221 if (*valp == NULL) 222 result = NSS_UNAVAIL; 223 } 224 225 return (result); 226 } 227 228 229 /* 230 * End "glue" functions 231 * 232 * The rest of this is based on: 233 * lib/nsswitch/nis/common/getnetgrent.c 234 */ 235 236 237 /* 238 * The nss_backend_t for a getnetgrent() sequence; we actually give the 239 * netgroup frontend a pointer to one of these structures in response to 240 * a (successful) setnetgrent() call on the files_backend backend 241 * described further down in this file. 242 */ 243 244 struct files_getnetgr_be; 245 typedef nss_status_t (*files_getnetgr_op_t)( 246 struct files_getnetgr_be *, void *); 247 248 struct files_getnetgr_be { 249 files_getnetgr_op_t *ops; 250 nss_dbop_t n_ops; 251 /* 252 * State for set/get/endnetgrent() 253 */ 254 char *netgroup; 255 struct grouplist *all_members; 256 struct grouplist *next_member; 257 }; 258 259 struct grouplist { /* One element of the list generated by a setnetgrent() */ 260 char *triple[NSS_NETGR_N]; 261 struct grouplist *gl_nxt; 262 }; 263 264 static nss_status_t 265 getnetgr_set(struct files_getnetgr_be *be, void *a) 266 { 267 const char *netgroup = (const char *) a; 268 269 if (be->netgroup != NULL && 270 strcmp(be->netgroup, netgroup) == 0) { 271 /* We already have the member-list; regurgitate it */ 272 be->next_member = be->all_members; 273 return (NSS_SUCCESS); 274 } 275 return (NSS_NOTFOUND); 276 } 277 278 static nss_status_t 279 getnetgr_get(struct files_getnetgr_be *be, void *a) 280 { 281 struct nss_getnetgrent_args *args = (struct nss_getnetgrent_args *)a; 282 struct grouplist *mem; 283 284 if ((mem = be->next_member) == 0) { 285 args->status = NSS_NETGR_NO; 286 } else { 287 char *buffer = args->buffer; 288 int buflen = args->buflen; 289 enum nss_netgr_argn i; 290 291 args->status = NSS_NETGR_FOUND; 292 293 for (i = 0; i < NSS_NETGR_N; i++) { 294 const char *str; 295 ssize_t len; 296 297 if ((str = mem->triple[i]) == 0) { 298 args->retp[i] = NULL; 299 } else if ((len = strlen(str) + 1) <= buflen) { 300 args->retp[i] = buffer; 301 (void) memcpy(buffer, str, len); 302 buffer += len; 303 buflen -= len; 304 } else { 305 args->status = NSS_NETGR_NOMEM; 306 break; 307 } 308 } 309 be->next_member = mem->gl_nxt; 310 } 311 return (NSS_SUCCESS); /* Yup, even for end-of-list, i.e. */ 312 /* do NOT advance to next backend. */ 313 } 314 315 static nss_status_t 316 getnetgr_end(struct files_getnetgr_be *be, void *dummy) 317 { 318 struct grouplist *gl; 319 struct grouplist *next; 320 321 for (gl = be->all_members; gl != NULL; gl = next) { 322 enum nss_netgr_argn i; 323 324 next = gl->gl_nxt; 325 for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) { 326 free(gl->triple[i]); 327 } 328 free(gl); 329 } 330 be->all_members = NULL; 331 be->next_member = NULL; 332 free(be->netgroup); 333 be->netgroup = NULL; 334 return (NSS_SUCCESS); 335 } 336 337 static nss_status_t 338 getnetgr_destr(struct files_getnetgr_be *be, void *dummy) 339 { 340 if (be != NULL) { 341 (void) getnetgr_end(be, NULL); 342 free(be); 343 } 344 return (NSS_SUCCESS); 345 } 346 347 static files_getnetgr_op_t getnetgr_ops[] = { 348 getnetgr_destr, 349 getnetgr_end, 350 getnetgr_set, 351 getnetgr_get, /* getnetgrent_r() */ 352 }; 353 354 355 /* 356 * The nss_backend_t for innetgr() and setnetgrent(). 357 * Also getbyname(), but that's only for testing. 358 */ 359 360 361 362 /* 363 * Code to do top-down search in the graph defined by the 'netgroup' YP map 364 */ 365 366 /* 367 * ===> This code is now used for setnetgrent(), not just innetgr(). 368 * 369 * If the easy way doesn't pan out, recursively search the 'netgroup' map. 370 * In order to do this, we: 371 * 372 * - remember all the netgroup names we've seen during this search, 373 * whether or not we've expanded them yet (we want fast insertion 374 * with duplicate-detection, so use yet another chained hash table), 375 * 376 * - keep a list of all the netgroups we haven't expanded yet (we just 377 * want fast insertion and pop-first, so a linked list will do fine). 378 * If we insert at the head, we get a depth-first search; insertion 379 * at the tail gives breadth-first (?), which seems preferable (?). 380 * 381 * A netgrnam struct contains pointers for both the hash-table and the list. 382 * It also contains the netgroup name; note that we embed the name at the 383 * end of the structure rather than holding a pointer to yet another 384 * malloc()ed region. 385 * 386 * A netgrtab structure contains the hash-chain heads and the head/tail 387 * pointers for the expansion list. 388 */ 389 390 struct netgrnam { 391 struct netgrnam *hash_chain; 392 struct netgrnam *expand_next; 393 char name[1]; /* Really [strlen(name) + 1] */ 394 }; 395 396 #define HASHMOD 113 397 398 struct netgrtab { 399 struct netgrnam *expand_first; 400 struct netgrnam **expand_lastp; 401 struct netgrnam *hash_heads[HASHMOD]; 402 }; 403 404 static void 405 ngt_init(struct netgrtab *ngt) 406 { 407 (void) memset((void *)ngt, '\0', sizeof (*ngt)); 408 ngt->expand_lastp = &ngt->expand_first; 409 } 410 411 /* === ? Change ngt_init() and ngt_destroy() to malloc/free struct netgrtab */ 412 413 static void 414 /* ==> ? Should return 'failed' (out-of-memory) status ? */ 415 ngt_insert(struct netgrtab *ngt, const char *name, size_t namelen) 416 { 417 unsigned hashval; 418 size_t i; 419 struct netgrnam *cur; 420 struct netgrnam **head; 421 422 if (__nss_files_netgr_debug != NULL) { 423 __nss_files_netgr_debug( 424 "ngt_insert: ngt=%p names=%s", ngt, name); 425 } 426 427 for (hashval = 0, i = 0; i < namelen; i++) { 428 hashval = (hashval << 2) + hashval + 429 ((const unsigned char *)name)[i]; 430 } 431 head = &ngt->hash_heads[hashval % HASHMOD]; 432 for (cur = *head; cur != 0; cur = cur->hash_chain) { 433 if (strncmp(cur->name, name, namelen) == 0 && 434 cur->name[namelen] == 0) { 435 return; /* Already in table, do nothing */ 436 } 437 } 438 /* Create new netgrnam struct */ 439 cur = malloc(offsetof(struct netgrnam, name) + namelen + 1); 440 if (cur == NULL) { 441 return; /* Out of memory, too bad */ 442 } 443 (void) memcpy(cur->name, name, namelen); 444 cur->name[namelen] = '\0'; 445 446 /* Insert in hash table */ 447 cur->hash_chain = *head; 448 *head = cur; 449 450 /* Insert in expansion list (insert at end for breadth-first search */ 451 cur->expand_next = NULL; 452 *ngt->expand_lastp = cur; 453 ngt->expand_lastp = &cur->expand_next; 454 } 455 456 static const char * 457 ngt_next(struct netgrtab *ngt) 458 { 459 struct netgrnam *first; 460 461 if ((first = ngt->expand_first) == NULL) { 462 return (NULL); 463 } 464 if ((ngt->expand_first = first->expand_next) == NULL) { 465 ngt->expand_lastp = &ngt->expand_first; 466 } 467 return (first->name); 468 } 469 470 static void 471 ngt_destroy(struct netgrtab *ngt) 472 { 473 struct netgrnam *cur; 474 struct netgrnam *next; 475 int i; 476 477 for (i = 0; i < HASHMOD; i++) { 478 for (cur = ngt->hash_heads[i]; cur != NULL; ) { 479 next = cur->hash_chain; 480 free(cur); 481 cur = next; 482 } 483 } 484 /* Don't bother zeroing pointers; must do init if we want to reuse */ 485 } 486 487 typedef const char *ccp; 488 489 static nss_status_t 490 top_down(struct files_backend *be, const char **groups, int ngroups, 491 int (*func)(ccp triple[3], void *iter_args, nss_status_t *return_val), 492 void *iter_args) 493 { 494 struct netgrtab *ngt; 495 /* netgrtab goes on the heap, not the stack, because it's large and */ 496 /* stacks may not be all that big in multi-threaded programs. */ 497 498 const char *group; 499 int nfound; 500 int done; 501 nss_status_t result; 502 503 if ((ngt = malloc(sizeof (*ngt))) == NULL) { 504 return (NSS_UNAVAIL); 505 } 506 ngt_init(ngt); 507 508 while (ngroups > 0) { 509 ngt_insert(ngt, *groups, strlen(*groups)); 510 groups++; 511 ngroups--; 512 } 513 514 done = 0; /* Set to 1 to indicate that we cut the iteration */ 515 /* short (and 'result' holds the return value) */ 516 nfound = 0; /* Number of successful netgroup getbyname calls */ 517 518 while (!done && (group = ngt_next(ngt)) != NULL) { 519 char *val = NULL; 520 char *p; 521 522 result = netgr_get_members(be, group, &val); 523 if (result != NSS_SUCCESS) { 524 if (result == NSS_NOTFOUND) { 525 if (__nss_files_netgr_error != NULL) 526 __nss_files_netgr_error( 527 "files netgroup lookup: %s doesn't exist", 528 group); 529 } else { 530 if (__nss_files_netgr_error != NULL) 531 __nss_files_netgr_error( 532 "files netgroup lookup: getbyname returned [%s]", 533 strerror(errno)); 534 done = 1; /* Give up, return result */ 535 } 536 /* Don't need to clean up anything */ 537 continue; 538 } 539 540 if (__nss_files_netgr_debug != NULL) { 541 __nss_files_netgr_debug( 542 "ngt_top: ngt=%p grp=%s members=\"%s\"", 543 ngt, group, val); 544 } 545 546 nfound++; 547 548 if ((p = strpbrk(val, "#\n")) != NULL) { 549 *p = '\0'; 550 } 551 p = val; 552 553 /* Parse val into triples and recursive netgroup references */ 554 for (;;) { 555 ccp triple[NSS_NETGR_N]; 556 int syntax_err; 557 enum nss_netgr_argn i; 558 559 while (isspace(*p)) 560 p++; 561 if (*p == '\0') { 562 /* Finished processing this particular val */ 563 break; 564 } 565 if (*p != '(') { 566 /* Doesn't look like the start of a triple, */ 567 /* so assume it's a recursive netgroup. */ 568 char *start = p; 569 p = strpbrk(start, " \t"); 570 if (p == 0) { 571 /* Point p at the final '\0' */ 572 p = start + strlen(start); 573 } 574 ngt_insert(ngt, start, (size_t)(p - start)); 575 continue; 576 } 577 578 /* Main case: a (machine, user, domain) triple */ 579 p++; 580 syntax_err = 0; 581 for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) { 582 char *start; 583 char *limit; 584 const char *terminators = ",) \t"; 585 586 if (i == NSS_NETGR_DOMAIN) { 587 /* Don't allow comma */ 588 terminators++; 589 } 590 while (isspace(*p)) 591 p++; 592 start = p; 593 limit = strpbrk(start, terminators); 594 if (limit == 0) { 595 syntax_err++; 596 break; 597 } 598 p = limit; 599 while (isspace(*p)) 600 p++; 601 if (*p == terminators[0]) { 602 /* 603 * Successfully parsed this name and 604 * the separator after it (comma or 605 * right paren); leave p ready for 606 * next parse. 607 */ 608 p++; 609 if (start == limit) { 610 /* Wildcard */ 611 triple[i] = 0; 612 } else { 613 *limit = '\0'; 614 triple[i] = start; 615 } 616 } else { 617 syntax_err++; 618 break; 619 } 620 } 621 622 if (syntax_err) { 623 /* 624 * ===> log it; 625 * ===> try skipping past next ')'; failing that, abandon the line; 626 */ 627 break; /* Abandon this line */ 628 } else if ((*func)(triple, iter_args, &result) == 0) { 629 /* Return result, good or bad */ 630 done = 1; 631 break; 632 } 633 } 634 /* End of inner loop over val[] */ 635 free(val); 636 val = NULL; 637 } 638 /* End of outer loop (!done && ngt_next(ngt) != 0) */ 639 640 ngt_destroy(ngt); 641 free(ngt); 642 643 if (done) { 644 return (result); 645 } else if (nfound > 0) { 646 /* ==== ? Should only do this if all the top-level groups */ 647 /* exist in YP? */ 648 return (NSS_SUCCESS); 649 } else { 650 return (NSS_NOTFOUND); 651 } 652 } 653 654 655 /* 656 * Code for setnetgrent() 657 */ 658 659 /* 660 * Iterator function for setnetgrent(): copy triple, add to be->all_members 661 */ 662 static int 663 save_triple(ccp trippp[NSS_NETGR_N], void *headp_arg, 664 nss_status_t *return_val) 665 { 666 struct grouplist **headp = headp_arg; 667 struct grouplist *gl; 668 enum nss_netgr_argn i; 669 670 if (__nss_files_netgr_debug != NULL) { 671 __nss_files_netgr_debug( 672 "save_tripple: h=%s u=%s d=%s", 673 trippp[0] ? trippp[0] : "*", 674 trippp[1] ? trippp[1] : "*", 675 trippp[2] ? trippp[2] : "*"); 676 } 677 678 if ((gl = malloc(sizeof (*gl))) == NULL) { 679 /* Out of memory */ 680 *return_val = NSS_UNAVAIL; 681 return (0); 682 } 683 for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) { 684 if (trippp[i] == NULL) { 685 /* Wildcard */ 686 gl->triple[i] = NULL; 687 } else if ((gl->triple[i] = strdup(trippp[i])) == NULL) { 688 /* Out of memory. Free any we've allocated */ 689 enum nss_netgr_argn j; 690 691 for (j = NSS_NETGR_MACHINE; j < i; j++) { 692 free(gl->triple[j]); 693 } 694 free(gl); 695 *return_val = NSS_UNAVAIL; 696 return (0); 697 } 698 } 699 gl->gl_nxt = *headp; 700 *headp = gl; 701 return (1); /* Tell top_down() to keep iterating */ 702 } 703 704 static nss_status_t 705 netgr_set(struct files_backend *be, void *a) 706 { 707 struct nss_setnetgrent_args *args = (struct nss_setnetgrent_args *)a; 708 struct files_getnetgr_be *get_be; 709 nss_status_t res; 710 711 get_be = malloc(sizeof (*get_be)); 712 if (get_be == NULL) { 713 return (NSS_UNAVAIL); 714 } 715 716 get_be->all_members = NULL; 717 res = top_down(be, &args->netgroup, 1, save_triple, 718 &get_be->all_members); 719 720 if (res == NSS_SUCCESS) { 721 get_be->ops = getnetgr_ops; 722 get_be->n_ops = ARRAY_SIZE(getnetgr_ops); 723 get_be->netgroup = strdup(args->netgroup); 724 if (get_be->netgroup == NULL) { 725 /* Out of memory. */ 726 args->iterator = NULL; 727 free(get_be); 728 return (NSS_UNAVAIL); 729 } 730 get_be->next_member = get_be->all_members; 731 732 args->iterator = (nss_backend_t *)get_be; 733 } else { 734 args->iterator = NULL; 735 free(get_be); 736 } 737 return (res); 738 } 739 740 741 /* 742 * Code for innetgr() 743 */ 744 745 /* 746 * Iterator function for innetgr(): Check whether triple matches args 747 */ 748 static int 749 match_triple(ccp triple[NSS_NETGR_N], void *ia_arg, nss_status_t *return_val) 750 { 751 struct nss_innetgr_args *ia = ia_arg; 752 enum nss_netgr_argn i; 753 754 if (__nss_files_netgr_debug != NULL) { 755 __nss_files_netgr_debug( 756 "match_triple: h=%s u=%s d=%s", 757 triple[0] ? triple[0] : "*", 758 triple[1] ? triple[1] : "*", 759 triple[2] ? triple[2] : "*"); 760 } 761 762 for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) { 763 int (*cmpf)(const char *, const char *); 764 char **argv; 765 uint_t n; 766 const char *name = triple[i]; 767 int argc = ia->arg[i].argc; 768 769 if (argc == 0 || name == NULL) { 770 /* Wildcarded on one side or t'other */ 771 continue; 772 } 773 argv = ia->arg[i].argv; 774 cmpf = (i == NSS_NETGR_MACHINE) ? strcasecmp : strcmp; 775 for (n = 0; n < argc; n++) { 776 if ((*cmpf)(argv[n], name) == 0) { 777 break; 778 } 779 } 780 if (n >= argc) { 781 /* Match failed, tell top_down() to keep looking */ 782 return (1); 783 } 784 } 785 /* Matched on all three, so quit looking and declare victory */ 786 787 if (__nss_files_netgr_debug != NULL) 788 __nss_files_netgr_debug("match_triple: found"); 789 790 ia->status = NSS_NETGR_FOUND; 791 *return_val = NSS_SUCCESS; 792 return (0); 793 } 794 795 /* 796 * Used to have easy_way() and it's support functions here. 797 */ 798 799 static nss_status_t 800 netgr_in(struct files_backend *be, void *a) 801 { 802 struct nss_innetgr_args *ia = (struct nss_innetgr_args *)a; 803 nss_status_t res; 804 805 ia->status = NSS_NETGR_NO; 806 807 /* 808 * Used to have "easy_way" calls here for the cases 809 * where we have just a user, or just a machine. 810 * 811 * That was important for NIS, where getting the list of 812 * members for some netgroup was a yp_match call that may 813 * need to go over-the-wire. Here in the "files" backend, 814 * getting the members of a group (getbyname) is a strictly 815 * local operation, and is cached (see hashinfo above) so 816 * it can normally complete with just memory operations. 817 * 818 * With a low-cost getbyname operation, the simple 819 * top_down algorithm has acceptable performance. 820 */ 821 822 /* Nope, try the slow way */ 823 ia->status = NSS_NETGR_NO; 824 res = top_down(be, (const char **)ia->groups.argv, ia->groups.argc, 825 match_triple, ia); 826 return (res); 827 } 828 829 830 /* 831 * (Almost) boilerplate for a switch backend 832 */ 833 834 static nss_status_t 835 netgr_destr(struct files_backend *be, void *dummy) 836 { 837 free(be); 838 return (NSS_SUCCESS); 839 } 840 841 static files_backend_op_t netgroup_ops[] = { 842 netgr_destr, 843 NULL, /* No endent, because no setent/getent */ 844 NULL, /* No setent; setnetgrent() is really a getXbyY() */ 845 NULL, /* No getent in the normal sense */ 846 847 netgr_in, /* innetgr(), via NSS_DBOP_NETGROUP_IN */ 848 netgr_set, /* setnetgrent(), via NSS_DBOP_NETGROUP_SET */ 849 getbyname, /* For testing, via NSS_DBOP_NETGROUP_BYNAME */ 850 }; 851 852 /* 853 * This is the one-and-only external entry point in this file. 854 * It's called by the NSS framework when loading this backend. 855 */ 856 nss_backend_t * 857 _nss_files_netgroup_constr(const char *dummy1, const char *dummy2, 858 const char *dummy3) 859 { 860 nss_backend_t *be; 861 862 be = _nss_files_constr(netgroup_ops, 863 ARRAY_SIZE(netgroup_ops), 864 "/etc/netgroup", 865 NSS_LINELEN_NETGROUP, 866 &hashinfo); 867 868 return (be); 869 } 870