getnetgrent.c revision 1.28 1 /* $NetBSD: getnetgrent.c,v 1.28 2000/01/22 22:40:58 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1994 Christos Zoulas
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Christos Zoulas.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 #if defined(LIBC_SCCS) && !defined(lint)
36 __RCSID("$NetBSD: getnetgrent.c,v 1.28 2000/01/22 22:40:58 mycroft Exp $");
37 #endif /* LIBC_SCCS and not lint */
38
39 #include "namespace.h"
40 #include <sys/types.h>
41
42 #include <assert.h>
43 #include <ctype.h>
44 #include <db.h>
45 #include <err.h>
46 #include <fcntl.h>
47 #define _NETGROUP_PRIVATE
48 #include <netgroup.h>
49 #include <nsswitch.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <stringlist.h>
54
55 #ifdef YP
56 #include <rpc/rpc.h>
57 #include <rpcsvc/ypclnt.h>
58 #include <rpcsvc/yp_prot.h>
59 #endif
60
61 #ifdef __STDC__
62 #include <stdarg.h>
63 #else
64 #include <varargs.h>
65 #endif
66
67 #ifdef __weak_alias
68 __weak_alias(endnetgrent,_endnetgrent)
69 __weak_alias(getnetgrent,_getnetgrent)
70 __weak_alias(innetgr,_innetgr)
71 __weak_alias(setnetgrent,_setnetgrent)
72 #endif
73
74 #define _NG_STAR(s) (((s) == NULL || *(s) == '\0') ? _ngstar : s)
75 #define _NG_EMPTY(s) ((s) == NULL ? "" : s)
76 #define _NG_ISSPACE(p) (isspace((unsigned char) (p)) || (p) == '\n')
77
78 static const char _ngstar[] = "*";
79 static struct netgroup *_nghead = (struct netgroup *)NULL;
80 static struct netgroup *_nglist = (struct netgroup *)NULL;
81 static DB *_ng_db;
82
83 static int getstring __P((char **, int, __aconst char **));
84 static struct netgroup *getnetgroup __P((char **));
85 static int lookup __P((char *, char **, int));
86 static int addgroup __P((StringList *, char *));
87 static int in_check __P((const char *, const char *,
88 const char *, struct netgroup *));
89 static int in_find __P((StringList *, char *, const char *,
90 const char *, const char *));
91 static char *in_lookup1 __P((const char *, const char *, int));
92 static int in_lookup __P((const char *, const char *,
93 const char *, int));
94
95 static const ns_src default_files_nis[] = {
96 { NSSRC_FILES, NS_SUCCESS | NS_NOTFOUND },
97 #ifdef YP
98 { NSSRC_NIS, NS_SUCCESS },
99 #endif
100 { 0 }
101 };
102
103 /*
104 * getstring(): Get a string delimited by the character, skipping leading and
105 * trailing blanks and advancing the pointer
106 */
107 static int
108 getstring(pp, del, str)
109 char **pp;
110 int del;
111 char __aconst **str;
112 {
113 size_t len;
114 char *sp, *ep, *dp;
115
116 _DIAGASSERT(pp != NULL);
117 _DIAGASSERT(str != NULL);
118
119 /* skip leading blanks */
120 for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++)
121 continue;
122
123 /* accumulate till delimiter or space */
124 for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++)
125 continue;
126
127 /* hunt for the delimiter */
128 for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++)
129 continue;
130
131 if (*dp != del) {
132 *str = NULL;
133 return 0;
134 }
135
136 *pp = ++dp;
137
138 len = (ep - sp) + 1;
139 if (len > 1) {
140 dp = malloc(len);
141 if (dp == NULL)
142 return 0;
143 memcpy(dp, sp, len);
144 dp[len - 1] = '\0';
145 } else
146 dp = NULL;
147
148 *str = dp;
149 return 1;
150 }
151
152
153 /*
154 * getnetgroup(): Parse a netgroup, and advance the pointer
155 */
156 static struct netgroup *
157 getnetgroup(pp)
158 char **pp;
159 {
160 struct netgroup *ng;
161
162 _DIAGASSERT(pp != NULL);
163 _DIAGASSERT(*pp != NULL);
164
165 ng = malloc(sizeof(struct netgroup));
166 if (ng == NULL)
167 return NULL;
168
169 (*pp)++; /* skip '(' */
170 if (!getstring(pp, ',', &ng->ng_host))
171 goto badhost;
172
173 if (!getstring(pp, ',', &ng->ng_user))
174 goto baduser;
175
176 if (!getstring(pp, ')', &ng->ng_domain))
177 goto baddomain;
178
179 #ifdef DEBUG_NG
180 {
181 char buf[1024];
182 (void) fprintf(stderr, "netgroup %s\n",
183 _ng_print(buf, sizeof(buf), ng));
184 }
185 #endif
186 return ng;
187
188 baddomain:
189 if (ng->ng_user)
190 free((char *)ng->ng_user);
191 baduser:
192 if (ng->ng_host)
193 free((char *)ng->ng_host);
194 badhost:
195 free(ng);
196 return NULL;
197 }
198
199
200 static int _local_lookup __P((void *, void *, va_list));
201
202 /*ARGSUSED*/
203 static int
204 _local_lookup(rv, cb_data, ap)
205 void *rv;
206 void *cb_data;
207 va_list ap;
208 {
209 char *name = va_arg(ap, char *);
210 char **line = va_arg(ap, char **);
211 int bywhat = va_arg(ap, int);
212
213 DBT key, data;
214 size_t len;
215 char *ks;
216 int r;
217
218 if (_ng_db == NULL)
219 return NS_UNAVAIL;
220
221 len = strlen(name) + 2;
222 ks = malloc(len);
223 if (ks == NULL)
224 return NS_UNAVAIL;
225
226 ks[0] = bywhat;
227 memcpy(&ks[1], name, len - 1);
228
229 key.data = (u_char *) ks;
230 key.size = len;
231
232 r = (_ng_db->get) (_ng_db, &key, &data, 0);
233 free(ks);
234 switch (r) {
235 case 0:
236 break;
237 case 1:
238 return NS_NOTFOUND;
239 case -1:
240 /* XXX: call endnetgrent() here ? */
241 return NS_UNAVAIL;
242 }
243
244 *line = strdup(data.data);
245 if (*line == NULL)
246 return NS_UNAVAIL;
247 return NS_SUCCESS;
248 }
249
250 #ifdef YP
251 static int _nis_lookup __P((void *, void *, va_list));
252
253 /*ARGSUSED*/
254 static int
255 _nis_lookup(rv, cb_data, ap)
256 void *rv;
257 void *cb_data;
258 va_list ap;
259 {
260 char *name = va_arg(ap, char *);
261 char **line = va_arg(ap, char **);
262 int bywhat = va_arg(ap, int);
263
264 static char *__ypdomain;
265 int i;
266 char *map = NULL;
267
268 if(__ypdomain == NULL) {
269 switch (yp_get_default_domain(&__ypdomain)) {
270 case 0:
271 break;
272 case YPERR_RESRC:
273 return NS_TRYAGAIN;
274 default:
275 return NS_UNAVAIL;
276 }
277 }
278
279 switch (bywhat) {
280 case _NG_KEYBYNAME:
281 map = "netgroup";
282 break;
283
284 case _NG_KEYBYUSER:
285 map = "netgroup.byuser";
286 break;
287
288 case _NG_KEYBYHOST:
289 map = "netgroup.byhost";
290 break;
291
292 default:
293 abort();
294 }
295
296 *line = NULL;
297 switch (yp_match(__ypdomain, map, name, (int)strlen(name), line, &i)) {
298 case 0:
299 return NS_SUCCESS;
300 case YPERR_KEY:
301 if (*line)
302 free(*line);
303 return NS_NOTFOUND;
304 default:
305 if (*line)
306 free(*line);
307 return NS_UNAVAIL;
308 }
309 /* NOTREACHED */
310 }
311 #endif
312
313
314 /*
315 * lookup(): Find the given key in the database or yp, and return its value
316 * in *line; returns 1 if key was found, 0 otherwise
317 */
318 static int
319 lookup(name, line, bywhat)
320 char *name;
321 char **line;
322 int bywhat;
323 {
324 int r;
325 static const ns_dtab dtab[] = {
326 NS_FILES_CB(_local_lookup, NULL)
327 NS_NIS_CB(_nis_lookup, NULL)
328 { 0 }
329 };
330
331 _DIAGASSERT(name != NULL);
332 _DIAGASSERT(line != NULL);
333
334 r = nsdispatch(NULL, dtab, NSDB_NETGROUP, "lookup", default_files_nis,
335 name, line, bywhat);
336 return (r == NS_SUCCESS) ? 1 : 0;
337 }
338
339 /*
340 * _ng_parse(): Parse a line and return: _NG_ERROR: Syntax Error _NG_NONE:
341 * line was empty or a comment _NG_GROUP: line had a netgroup definition,
342 * returned in ng _NG_NAME: line had a netgroup name, returned in name
343 *
344 * Public since used by netgroup_mkdb
345 */
346 int
347 _ng_parse(p, name, ng)
348 char **p;
349 char **name;
350 struct netgroup **ng;
351 {
352
353 _DIAGASSERT(p != NULL);
354 _DIAGASSERT(*p != NULL);
355 _DIAGASSERT(name != NULL);
356 _DIAGASSERT(ng != NULL);
357
358 while (**p) {
359 if (**p == '#')
360 /* comment */
361 return _NG_NONE;
362
363 while (**p && _NG_ISSPACE(**p))
364 /* skipblank */
365 (*p)++;
366
367 if (**p == '(') {
368 if ((*ng = getnetgroup(p)) == NULL)
369 return _NG_ERROR;
370 return _NG_GROUP;
371 } else {
372 char *np;
373 size_t i;
374
375 for (np = *p; **p && !_NG_ISSPACE(**p); (*p)++)
376 continue;
377 if (np != *p) {
378 i = (*p - np) + 1;
379 *name = malloc(i);
380 if (*name == NULL)
381 return _NG_ERROR;
382 memcpy(*name, np, i);
383 (*name)[i - 1] = '\0';
384 return _NG_NAME;
385 }
386 }
387 }
388 return _NG_NONE;
389 }
390
391
392 /*
393 * addgroup(): Recursively add all the members of the netgroup to this group.
394 * returns 0 upon failure, nonzero upon success.
395 * grp is not a valid pointer after return (either free(3)ed or allocated
396 * to a stringlist). in either case, it shouldn't be used again.
397 */
398 static int
399 addgroup(sl, grp)
400 StringList *sl;
401 char *grp;
402 {
403 char *line, *p;
404 struct netgroup *ng;
405 char *name;
406
407 _DIAGASSERT(sl != NULL);
408 _DIAGASSERT(grp != NULL);
409
410 #ifdef DEBUG_NG
411 (void) fprintf(stderr, "addgroup(%s)\n", grp);
412 #endif
413 /* check for cycles */
414 if (sl_find(sl, grp) != NULL) {
415 free(grp);
416 warnx("netgroup: Cycle in group `%s'", grp);
417 return 0;
418 }
419 if (sl_add(sl, grp) == -1) {
420 free(grp);
421 return 0;
422 }
423
424 /* Lookup this netgroup */
425 line = NULL;
426 if (!lookup(grp, &line, _NG_KEYBYNAME)) {
427 if (line != NULL)
428 free(line);
429 return 0;
430 }
431
432 p = line;
433
434 for (;;) {
435 switch (_ng_parse(&p, &name, &ng)) {
436 case _NG_NONE:
437 /* Done with the line */
438 free(line);
439 return 1;
440
441 case _NG_GROUP:
442 /* new netgroup */
443 /* add to the list */
444 ng->ng_next = _nglist;
445 _nglist = ng;
446 break;
447
448 case _NG_NAME:
449 /* netgroup name */
450 if (!addgroup(sl, name))
451 return 0;
452 break;
453
454 case _NG_ERROR:
455 return 0;
456
457 default:
458 abort();
459 }
460 }
461 }
462
463
464 /*
465 * in_check(): Compare the spec with the netgroup
466 */
467 static int
468 in_check(host, user, domain, ng)
469 const char *host;
470 const char *user;
471 const char *domain;
472 struct netgroup *ng;
473 {
474
475 /* host may be NULL */
476 /* user may be NULL */
477 /* domain may be NULL */
478 _DIAGASSERT(ng != NULL);
479
480 if ((host != NULL) && (ng->ng_host != NULL)
481 && strcmp(ng->ng_host, host) != 0)
482 return 0;
483
484 if ((user != NULL) && (ng->ng_user != NULL)
485 && strcmp(ng->ng_user, user) != 0)
486 return 0;
487
488 if ((domain != NULL) && (ng->ng_domain != NULL)
489 && strcmp(ng->ng_domain, domain) != 0)
490 return 0;
491
492 return 1;
493 }
494
495
496 /*
497 * in_find(): Find a match for the host, user, domain spec.
498 * grp is not a valid pointer after return (either free(3)ed or allocated
499 * to a stringlist). in either case, it shouldn't be used again.
500 */
501 static int
502 in_find(sl, grp, host, user, domain)
503 StringList *sl;
504 char *grp;
505 const char *host;
506 const char *user;
507 const char *domain;
508 {
509 char *line, *p;
510 int i;
511 struct netgroup *ng;
512 char *name;
513
514 _DIAGASSERT(sl != NULL);
515 _DIAGASSERT(grp != NULL);
516 /* host may be NULL */
517 /* user may be NULL */
518 /* domain may be NULL */
519
520 #ifdef DEBUG_NG
521 (void) fprintf(stderr, "in_find(%s)\n", grp);
522 #endif
523 /* check for cycles */
524 if (sl_find(sl, grp) != NULL) {
525 free(grp);
526 warnx("netgroup: Cycle in group `%s'", grp);
527 return 0;
528 }
529 if (sl_add(sl, grp) == -1) {
530 free(grp);
531 return 0;
532 }
533
534 /* Lookup this netgroup */
535 line = NULL;
536 if (!lookup(grp, &line, _NG_KEYBYNAME)) {
537 if (line)
538 free(line);
539 return 0;
540 }
541
542 p = line;
543
544 for (;;) {
545 switch (_ng_parse(&p, &name, &ng)) {
546 case _NG_NONE:
547 /* Done with the line */
548 free(line);
549 return 0;
550
551 case _NG_GROUP:
552 /* new netgroup */
553 i = in_check(host, user, domain, ng);
554 if (ng->ng_host != NULL)
555 free((char *)ng->ng_host);
556 if (ng->ng_user != NULL)
557 free((char *)ng->ng_user);
558 if (ng->ng_domain != NULL)
559 free((char *)ng->ng_domain);
560 free(ng);
561 if (i) {
562 free(line);
563 return 1;
564 }
565 break;
566
567 case _NG_NAME:
568 /* netgroup name */
569 if (in_find(sl, name, host, user, domain)) {
570 free(line);
571 return 1;
572 }
573 break;
574
575 case _NG_ERROR:
576 free(line);
577 return 0;
578
579 default:
580 abort();
581 }
582 }
583 }
584
585 /*
586 * _ng_makekey(): Make a key from the two names given. The key is of the form
587 * <name1>.<name2> Names strings are replaced with * if they are empty;
588 * Returns NULL if there's a problem.
589 */
590 char *
591 _ng_makekey(s1, s2, len)
592 const char *s1, *s2;
593 size_t len;
594 {
595 char *buf;
596
597 /* s1 may be NULL */
598 /* s2 may be NULL */
599
600 buf = malloc(len);
601 if (buf != NULL)
602 (void) snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2));
603 return buf;
604 }
605
606 void
607 _ng_print(buf, len, ng)
608 char *buf;
609 size_t len;
610 const struct netgroup *ng;
611 {
612 _DIAGASSERT(buf != NULL);
613 _DIAGASSERT(ng != NULL);
614
615 (void) snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host),
616 _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain));
617 }
618
619
620 /*
621 * in_lookup1(): Fast lookup for a key in the appropriate map
622 */
623 static char *
624 in_lookup1(key, domain, map)
625 const char *key;
626 const char *domain;
627 int map;
628 {
629 char *line;
630 size_t len;
631 char *ptr;
632 int res;
633
634 /* key may be NULL */
635 /* domain may be NULL */
636
637 len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2;
638 ptr = _ng_makekey(key, domain, len);
639 if (ptr == NULL)
640 return NULL;
641 res = lookup(ptr, &line, map);
642 free(ptr);
643 return res ? line : NULL;
644 }
645
646
647 /*
648 * in_lookup(): Fast lookup for a key in the appropriate map
649 */
650 static int
651 in_lookup(group, key, domain, map)
652 const char *group;
653 const char *key;
654 const char *domain;
655 int map;
656 {
657 size_t len;
658 char *ptr, *line;
659
660 _DIAGASSERT(group != NULL);
661 /* key may be NULL */
662 /* domain may be NULL */
663
664 if (domain != NULL) {
665 /* Domain specified; look in "group.domain" and "*.domain" */
666 if ((line = in_lookup1(key, domain, map)) == NULL)
667 line = in_lookup1(NULL, domain, map);
668 } else
669 line = NULL;
670
671 if (line == NULL) {
672 /*
673 * domain not specified or domain lookup failed; look in
674 * "group.*" and "*.*"
675 */
676 if (((line = in_lookup1(key, NULL, map)) == NULL) &&
677 ((line = in_lookup1(NULL, NULL, map)) == NULL))
678 return 0;
679 }
680
681 len = strlen(group);
682
683 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;)
684 /* Make sure we did not find a substring */
685 if ((ptr != line && ptr[-1] != ',') ||
686 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL))
687 ptr++;
688 else {
689 free(line);
690 return 1;
691 }
692
693 free(line);
694 return 0;
695 }
696
697
698 void
699 endnetgrent()
700 {
701 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) {
702 _nghead = _nglist->ng_next;
703 if (_nglist->ng_host != NULL)
704 free((char *)_nglist->ng_host);
705 if (_nglist->ng_user != NULL)
706 free((char *)_nglist->ng_user);
707 if (_nglist->ng_domain != NULL)
708 free((char *)_nglist->ng_domain);
709 free(_nglist);
710 }
711
712 if (_ng_db) {
713 (void) (_ng_db->close) (_ng_db);
714 _ng_db = NULL;
715 }
716 }
717
718
719 void
720 setnetgrent(ng)
721 const char *ng;
722 {
723 StringList *sl;
724 char *ng_copy;
725
726 _DIAGASSERT(ng != NULL);
727
728 sl = sl_init();
729 if (sl == NULL)
730 return;
731
732 /* Cleanup any previous storage */
733 if (_nghead != NULL)
734 endnetgrent();
735
736 if (_ng_db == NULL)
737 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
738
739 ng_copy = strdup(ng);
740 if (ng_copy != NULL)
741 addgroup(sl, ng_copy);
742 _nghead = _nglist;
743 sl_free(sl, 1);
744 }
745
746
747 int
748 getnetgrent(host, user, domain)
749 const char **host;
750 const char **user;
751 const char **domain;
752 {
753 _DIAGASSERT(host != NULL);
754 _DIAGASSERT(user != NULL);
755 _DIAGASSERT(domain != NULL);
756
757 if (_nglist == NULL)
758 return 0;
759
760 *host = _nglist->ng_host;
761 *user = _nglist->ng_user;
762 *domain = _nglist->ng_domain;
763
764 _nglist = _nglist->ng_next;
765
766 return 1;
767 }
768
769
770 int
771 innetgr(grp, host, user, domain)
772 const char *grp, *host, *user, *domain;
773 {
774 int found;
775 StringList *sl;
776
777 _DIAGASSERT(grp != NULL);
778 /* host may be NULL */
779 /* user may be NULL */
780 /* domain may be NULL */
781
782 if (_ng_db == NULL)
783 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
784
785 /* Try the fast lookup first */
786 if (host != NULL && user == NULL) {
787 if (in_lookup(grp, host, domain, _NG_KEYBYHOST))
788 return 1;
789 } else if (host == NULL && user != NULL) {
790 if (in_lookup(grp, user, domain, _NG_KEYBYUSER))
791 return 1;
792 }
793 /* If a domainname is given, we would have found a match */
794 if (domain != NULL)
795 return 0;
796
797 /* Too bad need the slow recursive way */
798 sl = sl_init();
799 if (sl == NULL)
800 return 0;
801 found = in_find(sl, strdup(grp), host, user, domain);
802 sl_free(sl, 1);
803
804 return found;
805 }
806