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