getnetgrent.c revision 1.33 1 /* $NetBSD: getnetgrent.c,v 1.33 2006/07/27 18:12:50 christos 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.33 2006/07/27 18:12:50 christos 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 <stringlist.h>
49 #include <netgroup.h>
50 #include <nsswitch.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 #ifdef YP
57 #include <rpc/rpc.h>
58 #include <rpcsvc/ypclnt.h>
59 #include <rpcsvc/yp_prot.h>
60 #endif
61
62 #ifdef __weak_alias
63 __weak_alias(endnetgrent,_endnetgrent)
64 __weak_alias(getnetgrent,_getnetgrent)
65 __weak_alias(innetgr,_innetgr)
66 __weak_alias(setnetgrent,_setnetgrent)
67 #endif
68
69 #define _NG_STAR(s) (((s) == NULL || *(s) == '\0') ? _ngstar : s)
70 #define _NG_EMPTY(s) ((s) == NULL ? "" : s)
71 #define _NG_ISSPACE(p) (isspace((unsigned char) (p)) || (p) == '\n')
72
73 static const char _ngstar[] = "*";
74 static struct netgroup *_nghead = NULL;
75 static struct netgroup *_nglist = NULL;
76 static DB *_ng_db;
77
78 static int getstring(char **, int, __aconst char **);
79 static struct netgroup *getnetgroup(char **);
80 static int lookup(char *, char **, int);
81 static int addgroup(StringList *, char *);
82 static int in_check(const char *, const char *, const char *,
83 struct netgroup *);
84 static int in_find(StringList *, char *, const char *, const char *,
85 const char *);
86 static char *in_lookup1(const char *, const char *, int);
87 static int in_lookup(const char *, const char *, const char *, int);
88
89 #ifdef NSSRC_FILES
90 static const ns_src default_files_nis[] = {
91 { NSSRC_FILES, NS_SUCCESS | NS_NOTFOUND },
92 #ifdef YP
93 { NSSRC_NIS, NS_SUCCESS },
94 #endif
95 { 0 }
96 };
97 #endif
98
99 /*
100 * getstring(): Get a string delimited by the character, skipping leading and
101 * trailing blanks and advancing the pointer
102 */
103 static int
104 getstring(char **pp, int del, char __aconst **str)
105 {
106 size_t len;
107 char *sp, *ep, *dp;
108
109 _DIAGASSERT(pp != NULL);
110 _DIAGASSERT(str != NULL);
111
112 /* skip leading blanks */
113 for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++)
114 continue;
115
116 /* accumulate till delimiter or space */
117 for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++)
118 continue;
119
120 /* hunt for the delimiter */
121 for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++)
122 continue;
123
124 if (*dp != del) {
125 *str = NULL;
126 return 0;
127 }
128
129 *pp = ++dp;
130
131 len = (ep - sp) + 1;
132 if (len > 1) {
133 dp = malloc(len);
134 if (dp == NULL)
135 return 0;
136 (void)memcpy(dp, sp, len);
137 dp[len - 1] = '\0';
138 } else
139 dp = NULL;
140
141 *str = dp;
142 return 1;
143 }
144
145
146 /*
147 * getnetgroup(): Parse a netgroup, and advance the pointer
148 */
149 static struct netgroup *
150 getnetgroup(pp)
151 char **pp;
152 {
153 struct netgroup *ng;
154
155 _DIAGASSERT(pp != NULL);
156 _DIAGASSERT(*pp != NULL);
157
158 ng = malloc(sizeof(struct netgroup));
159 if (ng == NULL)
160 return NULL;
161
162 (*pp)++; /* skip '(' */
163 if (!getstring(pp, ',', &ng->ng_host))
164 goto badhost;
165
166 if (!getstring(pp, ',', &ng->ng_user))
167 goto baduser;
168
169 if (!getstring(pp, ')', &ng->ng_domain))
170 goto baddomain;
171
172 #ifdef DEBUG_NG
173 {
174 char buf[1024];
175 (void) fprintf(stderr, "netgroup %s\n",
176 _ng_print(buf, sizeof(buf), ng));
177 }
178 #endif
179 return ng;
180
181 baddomain:
182 if (ng->ng_user)
183 free(ng->ng_user);
184 baduser:
185 if (ng->ng_host)
186 free(ng->ng_host);
187 badhost:
188 free(ng);
189 return NULL;
190 }
191
192 void
193 _ng_cycle(const char *grp, const StringList *sl)
194 {
195 size_t i;
196 warnx("netgroup: Cycle in group `%s'", grp);
197 (void)fprintf(stderr, "groups: ");
198 for (i = 0; i < sl->sl_cur; i++)
199 (void)fprintf(stderr, "%s ", sl->sl_str[i]);
200 (void)fprintf(stderr, "\n");
201 }
202
203 static int _local_lookup(void *, void *, va_list);
204
205 /*ARGSUSED*/
206 static int
207 _local_lookup(void *rv, void *cb_data, 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 (void)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(void *, void *, va_list);
252
253 /*ARGSUSED*/
254 static int
255 _nis_lookup(void *rv, void *cb_data, va_list ap)
256 {
257 char *name = va_arg(ap, char *);
258 char **line = va_arg(ap, char **);
259 int bywhat = va_arg(ap, int);
260
261 static char *__ypdomain;
262 int i;
263 const char *map = NULL;
264
265 if(__ypdomain == NULL) {
266 switch (yp_get_default_domain(&__ypdomain)) {
267 case 0:
268 break;
269 case YPERR_RESRC:
270 return NS_TRYAGAIN;
271 default:
272 return NS_UNAVAIL;
273 }
274 }
275
276 switch (bywhat) {
277 case _NG_KEYBYNAME:
278 map = "netgroup";
279 break;
280
281 case _NG_KEYBYUSER:
282 map = "netgroup.byuser";
283 break;
284
285 case _NG_KEYBYHOST:
286 map = "netgroup.byhost";
287 break;
288
289 default:
290 abort();
291 }
292
293 *line = NULL;
294 switch (yp_match(__ypdomain, map, name, (int)strlen(name), line, &i)) {
295 case 0:
296 return NS_SUCCESS;
297 case YPERR_KEY:
298 if (*line)
299 free(*line);
300 return NS_NOTFOUND;
301 default:
302 if (*line)
303 free(*line);
304 return NS_UNAVAIL;
305 }
306 /* NOTREACHED */
307 }
308 #endif
309
310 #ifdef NSSRC_FILES
311 /*
312 * lookup(): Find the given key in the database or yp, and return its value
313 * in *line; returns 1 if key was found, 0 otherwise
314 */
315 static int
316 lookup(char *name, char **line, int bywhat)
317 {
318 int r;
319 static const ns_dtab dtab[] = {
320 NS_FILES_CB(_local_lookup, NULL)
321 NS_NIS_CB(_nis_lookup, NULL)
322 { 0 }
323 };
324
325 _DIAGASSERT(name != NULL);
326 _DIAGASSERT(line != NULL);
327
328 r = nsdispatch(NULL, dtab, NSDB_NETGROUP, "lookup", default_files_nis,
329 name, line, bywhat);
330 return (r == NS_SUCCESS) ? 1 : 0;
331 }
332 #else
333 static int
334 _local_lookupv(int *rv, void *cbdata, ...)
335 {
336 int e;
337 va_list ap;
338 va_start(ap, cbdata);
339 e = _local_lookup(rv, cbdata, ap);
340 va_end(ap);
341 return e;
342 }
343
344 static int
345 lookup(name, line, bywhat)
346 char *name;
347 char **line;
348 int bywhat;
349 {
350 return _local_lookupv(NULL, NULL, name, line, bywhat) == NS_SUCCESS;
351 }
352 #endif
353
354 /*
355 * _ng_parse(): Parse a line and return: _NG_ERROR: Syntax Error _NG_NONE:
356 * line was empty or a comment _NG_GROUP: line had a netgroup definition,
357 * returned in ng _NG_NAME: line had a netgroup name, returned in name
358 *
359 * Public since used by netgroup_mkdb
360 */
361 int
362 _ng_parse(char **p, char **name, struct netgroup **ng)
363 {
364
365 _DIAGASSERT(p != NULL);
366 _DIAGASSERT(*p != NULL);
367 _DIAGASSERT(name != NULL);
368 _DIAGASSERT(ng != NULL);
369
370 while (**p) {
371 if (**p == '#')
372 /* comment */
373 return _NG_NONE;
374
375 while (**p && _NG_ISSPACE(**p))
376 /* skipblank */
377 (*p)++;
378
379 if (**p == '(') {
380 if ((*ng = getnetgroup(p)) == NULL)
381 return _NG_ERROR;
382 return _NG_GROUP;
383 } else {
384 char *np;
385 size_t i;
386
387 for (np = *p; **p && !_NG_ISSPACE(**p); (*p)++)
388 continue;
389 if (np != *p) {
390 i = (*p - np) + 1;
391 *name = malloc(i);
392 if (*name == NULL)
393 return _NG_ERROR;
394 (void)memcpy(*name, np, i);
395 (*name)[i - 1] = '\0';
396 return _NG_NAME;
397 }
398 }
399 }
400 return _NG_NONE;
401 }
402
403
404 /*
405 * addgroup(): Recursively add all the members of the netgroup to this group.
406 * returns 0 upon failure, nonzero upon success.
407 * grp is not a valid pointer after return (either free(3)ed or allocated
408 * to a stringlist). in either case, it shouldn't be used again.
409 */
410 static int
411 addgroup(StringList *sl, char *grp)
412 {
413 char *line, *p;
414 struct netgroup *ng;
415 char *name;
416
417 _DIAGASSERT(sl != NULL);
418 _DIAGASSERT(grp != NULL);
419
420 #ifdef DEBUG_NG
421 (void)fprintf(stderr, "addgroup(%s)\n", grp);
422 #endif
423 /* check for cycles */
424 if (sl_find(sl, grp) != NULL) {
425 _ng_cycle(grp, sl);
426 free(grp);
427 return 0;
428 }
429 printf("%s, %d: add %s\n", __FILE__, __LINE__, grp);
430 if (sl_add(sl, grp) == -1) {
431 free(grp);
432 return 0;
433 }
434
435 /* Lookup this netgroup */
436 line = NULL;
437 if (!lookup(grp, &line, _NG_KEYBYNAME)) {
438 if (line)
439 free(line);
440 return 0;
441 }
442
443 p = line;
444
445 for (;;) {
446 switch (_ng_parse(&p, &name, &ng)) {
447 case _NG_NONE:
448 /* Done with the line */
449 free(line);
450 return 1;
451
452 case _NG_GROUP:
453 /* new netgroup */
454 /* add to the list */
455 ng->ng_next = _nglist;
456 _nglist = ng;
457 break;
458
459 case _NG_NAME:
460 /* netgroup name */
461 if (!addgroup(sl, name))
462 return 0;
463 break;
464
465 case _NG_ERROR:
466 return 0;
467
468 default:
469 abort();
470 }
471 }
472 }
473
474
475 /*
476 * in_check(): Compare the spec with the netgroup
477 */
478 static int
479 in_check(const char *host, const char *user, const char *domain,
480 struct netgroup *ng)
481 {
482
483 /* host may be NULL */
484 /* user may be NULL */
485 /* domain may be NULL */
486 _DIAGASSERT(ng != NULL);
487
488 if ((host != NULL) && (ng->ng_host != NULL)
489 && strcmp(ng->ng_host, host) != 0)
490 return 0;
491
492 if ((user != NULL) && (ng->ng_user != NULL)
493 && strcmp(ng->ng_user, user) != 0)
494 return 0;
495
496 if ((domain != NULL) && (ng->ng_domain != NULL)
497 && strcmp(ng->ng_domain, domain) != 0)
498 return 0;
499
500 return 1;
501 }
502
503
504 /*
505 * in_find(): Find a match for the host, user, domain spec.
506 * grp is not a valid pointer after return (either free(3)ed or allocated
507 * to a stringlist). in either case, it shouldn't be used again.
508 */
509 static int
510 in_find(StringList *sl, char *grp, const char *host, const char *user,
511 const char *domain)
512 {
513 char *line, *p;
514 int i;
515 struct netgroup *ng;
516 char *name;
517
518 _DIAGASSERT(sl != NULL);
519 _DIAGASSERT(grp != NULL);
520 /* host may be NULL */
521 /* user may be NULL */
522 /* domain may be NULL */
523
524 #ifdef DEBUG_NG
525 (void)fprintf(stderr, "in_find(%s)\n", grp);
526 #endif
527 /* check for cycles */
528 if (sl_find(sl, grp) != NULL) {
529 _ng_cycle(grp, sl);
530 free(grp);
531 return 0;
532 }
533 printf("%s, %d: add %s\n", __FILE__, __LINE__, grp);
534 if (sl_add(sl, grp) == -1) {
535 free(grp);
536 return 0;
537 }
538
539 /* Lookup this netgroup */
540 line = NULL;
541 if (!lookup(grp, &line, _NG_KEYBYNAME)) {
542 if (line)
543 free(line);
544 return 0;
545 }
546
547 p = line;
548
549 for (;;) {
550 switch (_ng_parse(&p, &name, &ng)) {
551 case _NG_NONE:
552 /* Done with the line */
553 free(line);
554 return 0;
555
556 case _NG_GROUP:
557 /* new netgroup */
558 i = in_check(host, user, domain, ng);
559 if (ng->ng_host != NULL)
560 free(ng->ng_host);
561 if (ng->ng_user != NULL)
562 free(ng->ng_user);
563 if (ng->ng_domain != NULL)
564 free(ng->ng_domain);
565 free(ng);
566 if (i) {
567 free(line);
568 return 1;
569 }
570 break;
571
572 case _NG_NAME:
573 /* netgroup name */
574 if (in_find(sl, name, host, user, domain)) {
575 free(line);
576 return 1;
577 }
578 break;
579
580 case _NG_ERROR:
581 free(line);
582 return 0;
583
584 default:
585 abort();
586 }
587 }
588 }
589
590 /*
591 * _ng_makekey(): Make a key from the two names given. The key is of the form
592 * <name1>.<name2> Names strings are replaced with * if they are empty;
593 * Returns NULL if there's a problem.
594 */
595 char *
596 _ng_makekey(const char *s1, const char *s2, size_t len)
597 {
598 char *buf;
599
600 /* s1 may be NULL */
601 /* s2 may be NULL */
602
603 buf = malloc(len);
604 if (buf != NULL)
605 (void)snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2));
606 return buf;
607 }
608
609 void
610 _ng_print(char *buf, size_t len, 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(const char *key, const char *domain, int map)
625 {
626 char *line;
627 size_t len;
628 char *ptr;
629 int res;
630
631 /* key may be NULL */
632 /* domain may be NULL */
633
634 len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2;
635 ptr = _ng_makekey(key, domain, len);
636 if (ptr == NULL)
637 return NULL;
638 res = lookup(ptr, &line, map);
639 free(ptr);
640 return res ? line : NULL;
641 }
642
643
644 /*
645 * in_lookup(): Fast lookup for a key in the appropriate map
646 */
647 static int
648 in_lookup(const char *group, const char *key, const char *domain, int map)
649 {
650 size_t len;
651 char *ptr, *line;
652
653 _DIAGASSERT(group != NULL);
654 /* key may be NULL */
655 /* domain may be NULL */
656
657 if (domain != NULL) {
658 /* Domain specified; look in "group.domain" and "*.domain" */
659 if ((line = in_lookup1(key, domain, map)) == NULL)
660 line = in_lookup1(NULL, domain, map);
661 } else
662 line = NULL;
663
664 if (line == NULL) {
665 /*
666 * domain not specified or domain lookup failed; look in
667 * "group.*" and "*.*"
668 */
669 if (((line = in_lookup1(key, NULL, map)) == NULL) &&
670 ((line = in_lookup1(NULL, NULL, map)) == NULL))
671 return 0;
672 }
673
674 len = strlen(group);
675
676 for (ptr = line; (ptr = strstr(ptr, group)) != NULL;)
677 /* Make sure we did not find a substring */
678 if ((ptr != line && ptr[-1] != ',') ||
679 (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL))
680 ptr++;
681 else {
682 free(line);
683 return 1;
684 }
685
686 free(line);
687 return 0;
688 }
689
690
691 void
692 endnetgrent(void)
693 {
694 for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) {
695 _nghead = _nglist->ng_next;
696 if (_nglist->ng_host != NULL)
697 free(_nglist->ng_host);
698 if (_nglist->ng_user != NULL)
699 free(_nglist->ng_user);
700 if (_nglist->ng_domain != NULL)
701 free(_nglist->ng_domain);
702 free(_nglist);
703 }
704
705 if (_ng_db) {
706 (void)(*_ng_db->close)(_ng_db);
707 _ng_db = NULL;
708 }
709 }
710
711
712 void
713 setnetgrent(const char *ng)
714 {
715 StringList *sl;
716 char *ng_copy;
717
718 _DIAGASSERT(ng != NULL);
719
720 sl = sl_init();
721 if (sl == NULL)
722 return;
723
724 /* Cleanup any previous storage */
725 if (_nghead != NULL)
726 endnetgrent();
727
728 if (_ng_db == NULL)
729 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
730
731 ng_copy = strdup(ng);
732 if (ng_copy != NULL)
733 addgroup(sl, ng_copy);
734 _nghead = _nglist;
735 sl_free(sl, 1);
736 }
737
738
739 int
740 getnetgrent(const char **host, const char **user, const char **domain)
741 {
742 _DIAGASSERT(host != NULL);
743 _DIAGASSERT(user != NULL);
744 _DIAGASSERT(domain != NULL);
745
746 if (_nglist == NULL)
747 return 0;
748
749 *host = _nglist->ng_host;
750 *user = _nglist->ng_user;
751 *domain = _nglist->ng_domain;
752
753 _nglist = _nglist->ng_next;
754
755 return 1;
756 }
757
758
759 int
760 innetgr(const char *grp, const char *host, const char *user, const char *domain)
761 {
762 int found;
763 StringList *sl;
764 char *grcpy;
765
766 _DIAGASSERT(grp != NULL);
767 /* host may be NULL */
768 /* user may be NULL */
769 /* domain may be NULL */
770
771 if (_ng_db == NULL)
772 _ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
773
774 /* Try the fast lookup first */
775 if (host != NULL && user == NULL) {
776 if (in_lookup(grp, host, domain, _NG_KEYBYHOST))
777 return 1;
778 } else if (host == NULL && user != NULL) {
779 if (in_lookup(grp, user, domain, _NG_KEYBYUSER))
780 return 1;
781 }
782 /* If a domainname is given, we would have found a match */
783 if (domain != NULL)
784 return 0;
785
786 /* Too bad need the slow recursive way */
787 sl = sl_init();
788 if (sl == NULL)
789 return 0;
790 if ((grcpy = strdup(grp)) == NULL) {
791 sl_free(sl, 1);
792 return 0;
793 }
794 found = in_find(sl, grcpy, host, user, domain);
795 sl_free(sl, 1);
796
797 return found;
798 }
799