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