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
hash_netgrname(nss_XbyY_args_t * argp,int keyhash,const char * line,int linelen)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
check_netgrname(nss_XbyY_args_t * argp,const char * line,int linelen)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
getbyname(files_backend_ptr_t be,void * a)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
str2netgr(const char * instr,int lenstr,void * ent,char * buffer,int buflen)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
netgr_get_members(struct files_backend * be,const char * name,char ** valp)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
getnetgr_set(struct files_getnetgr_be * be,void * a)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
getnetgr_get(struct files_getnetgr_be * be,void * a)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
getnetgr_end(struct files_getnetgr_be * be,void * dummy)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
getnetgr_destr(struct files_getnetgr_be * be,void * dummy)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
ngt_init(struct netgrtab * ngt)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 ? */
ngt_insert(struct netgrtab * ngt,const char * name,size_t namelen)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 *
ngt_next(struct netgrtab * ngt)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
ngt_destroy(struct netgrtab * ngt)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
top_down(struct files_backend * be,const char ** groups,int ngroups,int (* func)(ccp triple[3],void * iter_args,nss_status_t * return_val),void * iter_args)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
save_triple(ccp trippp[NSS_NETGR_N],void * headp_arg,nss_status_t * return_val)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
netgr_set(struct files_backend * be,void * a)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
match_triple(ccp triple[NSS_NETGR_N],void * ia_arg,nss_status_t * return_val)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
netgr_in(struct files_backend * be,void * a)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
netgr_destr(struct files_backend * be,void * dummy)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 *
_nss_files_netgroup_constr(const char * dummy1,const char * dummy2,const char * dummy3)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