2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Rick Macklem at The University of Guelph.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #if defined(LIBC_SCCS) && !defined(lint)
36 static char sccsid[] = "@(#)getnetgrent.c 8.2 (Berkeley) 4/27/95";
37 #endif /* LIBC_SCCS and not lint */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include "namespace.h"
48 #include <pthread_np.h>
59 * We want to be able to use NIS netgroups properly while retaining
60 * the ability to use a local /etc/netgroup file. Unfortunately, you
61 * can't really do both at the same time - at least, not efficiently.
62 * NetBSD deals with this problem by creating a netgroup database
63 * using Berkeley DB (just like the password database) that allows
64 * for lookups using netgroup, netgroup.byuser or netgroup.byhost
65 * searches. This is a neat idea, but I don't have time to implement
66 * something like that now. (I think ultimately it would be nice
67 * if we DB-fied the group and netgroup stuff all in one shot, but
68 * for now I'm satisfied just to have something that works well
69 * without requiring massive code changes.)
71 * Therefore, to still permit the use of the local file and maintain
72 * optimum NIS performance, we allow for the following conditions:
74 * - If /etc/netgroup does not exist and NIS is turned on, we use
77 * - If /etc/netgroup exists but is empty, we use NIS netgroups
80 * - If /etc/netgroup exists and contains _only_ a '+', we use
83 * - If /etc/netgroup exists, contains locally defined netgroups
84 * and a '+', we use a mixture of NIS and the local entries.
85 * This method should return the same NIS data as just using
86 * NIS alone, but it will be slower if the NIS netgroup database
87 * is large (innetgr() in particular will suffer since extra
88 * processing has to be done in order to determine memberships
89 * using just the raw netgroup data).
91 * - If /etc/netgroup exists and contains only locally defined
92 * netgroup entries, we use just those local entries and ignore
93 * NIS (this is the original, pre-NIS behavior).
97 #include <rpcsvc/yp_prot.h>
98 #include <rpcsvc/ypclnt.h>
99 #include <sys/param.h>
100 #include <sys/stat.h>
101 #include <sys/errno.h>
102 static char *_netgr_yp_domain;
104 static int _netgr_yp_enabled;
105 static int _yp_innetgr;
108 #ifndef _PATH_NETGROUP
109 #define _PATH_NETGROUP "/etc/netgroup"
113 NGRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
114 NGRP_STORAGE_MAX = 1 << 20, /* 1 MByte */
117 static const ns_src defaultsrc[] = {
118 { NSSRC_COMPAT, NS_SUCCESS },
123 * Static Variables and functions used by setnetgrent(), getnetgrent() and
125 * There are two linked lists:
126 * - linelist is just used by setnetgrent() to parse the net group file via.
128 * - netgrp is the list of entries for the current netgroup
131 struct linelist *l_next; /* Chain ptr. */
132 int l_parsed; /* Flag for cycles */
133 char *l_groupname; /* Name of netgroup */
134 char *l_line; /* Netgroup entrie(s) to be parsed */
138 struct netgrp *ng_next; /* Chain ptr */
139 char *ng_str[3]; /* Field pointers, see below */
144 struct linelist *st_linehead;
145 struct netgrp *st_nextgrp;
146 struct netgrp *st_gr;
150 #define NG_HOST 0 /* Host name */
151 #define NG_USER 1 /* User name */
152 #define NG_DOM 2 /* and Domain name */
154 static void netgr_endstate(void *);
155 NSS_TLS_HANDLING(netgr);
157 static int files_endnetgrent(void *, void *, va_list);
158 static int files_getnetgrent_r(void *, void *, va_list);
159 static int files_setnetgrent(void *, void *, va_list);
161 static int compat_endnetgrent(void *, void *, va_list);
162 static int compat_innetgr(void *, void *, va_list);
163 static int compat_getnetgrent_r(void *, void *, va_list);
164 static int compat_setnetgrent(void *, void *, va_list);
166 static void _compat_clearstate(void);
167 static int _getnetgrent_r(char **, char **, char **, char *, size_t, int *,
168 struct netgr_state *);
169 static int _innetgr_fallback(void *, void *, const char *, const char *,
170 const char *, const char *);
171 static int innetgr_fallback(void *, void *, va_list);
172 static int parse_netgrp(const char *, struct netgr_state *, int);
173 static struct linelist *read_for_group(const char *, struct netgr_state *, int);
175 #define LINSIZ 1024 /* Length of netgroup file line */
177 static const ns_dtab getnetgrent_dtab[] = {
178 NS_FILES_CB(files_getnetgrent_r, NULL)
179 NS_COMPAT_CB(compat_getnetgrent_r, NULL)
180 { NULL, NULL, NULL },
183 static const ns_dtab setnetgrent_dtab[] = {
184 NS_FILES_CB(files_setnetgrent, NULL)
185 NS_COMPAT_CB(compat_setnetgrent, NULL)
186 { NULL, NULL, NULL },
189 static const ns_dtab endnetgrent_dtab[] = {
190 NS_FILES_CB(files_endnetgrent, NULL)
191 NS_COMPAT_CB(compat_endnetgrent, NULL)
192 { NULL, NULL, NULL },
195 static struct netgr_state compat_state;
198 netgr_endstate(void *arg)
200 struct linelist *lp, *olp;
201 struct netgrp *gp, *ogp;
202 struct netgr_state *st;
204 st = (struct netgr_state *)arg;
205 lp = st->st_linehead;
209 free(olp->l_groupname);
213 st->st_linehead = NULL;
214 if (st->st_grname != NULL) {
216 st->st_grname = NULL;
222 free(ogp->ng_str[NG_HOST]);
223 free(ogp->ng_str[NG_USER]);
224 free(ogp->ng_str[NG_DOM]);
228 st->st_nextgrp = NULL;
232 files_getnetgrent_r(void *retval, void *mdata, va_list ap)
234 struct netgr_state *st;
235 char **hostp, **userp, **domp, *buf;
239 hostp = va_arg(ap, char **);
240 userp = va_arg(ap, char **);
241 domp = va_arg(ap, char **);
242 buf = va_arg(ap, char *);
243 bufsize = va_arg(ap, size_t);
244 errnop = va_arg(ap, int *);
246 if (netgr_getstate(&st) != 0)
249 return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop, st));
253 files_setnetgrent(void *retval, void *mdata, va_list ap)
255 const ns_src src[] = {
256 { NSSRC_FILES, NS_SUCCESS },
259 struct netgr_state *st;
263 group = va_arg(ap, const char *);
265 if (group == NULL || group[0] == '\0')
268 rv = netgr_getstate(&st);
272 if (st->st_gr == NULL || strcmp(group, st->st_grname) != 0) {
273 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP,
275 if ((st->st_netf = fopen(_PATH_NETGROUP, "re")) != NULL) {
276 if (parse_netgrp(group, st, 0) != 0)
277 (void)_nsdispatch(NULL, endnetgrent_dtab,
278 NSDB_NETGROUP, "endnetgrent", src);
280 st->st_grname = strdup(group);
281 (void)fclose(st->st_netf);
285 st->st_nextgrp = st->st_gr;
286 return (st->st_grname != NULL ? NS_SUCCESS : NS_NOTFOUND);
290 files_endnetgrent(void *retval, void *mdata, va_list ap)
292 struct netgr_state *st;
294 if (netgr_getstate(&st) != 0)
301 compat_getnetgrent_r(void *retval, void *mdata, va_list ap)
303 char **hostp, **userp, **domp, *buf;
310 hostp = va_arg(ap, char **);
311 userp = va_arg(ap, char **);
312 domp = va_arg(ap, char **);
313 buf = va_arg(ap, char *);
314 bufsize = va_arg(ap, size_t);
315 errnop = va_arg(ap, int *);
317 return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop,
322 * compat_setnetgrent()
323 * Parse the netgroup file looking for the netgroup and build the list
324 * of netgrp structures. Let parse_netgrp() and read_for_group() do
328 compat_setnetgrent(void *retval, void *mdata, va_list ap)
333 struct stat _yp_statp;
337 group = va_arg(ap, const char *);
340 if (group == NULL || !strlen(group))
343 if (compat_state.st_gr == NULL ||
344 strcmp(group, compat_state.st_grname) != 0) {
345 _compat_clearstate();
348 /* Presumed guilty until proven innocent. */
351 * If /etc/netgroup doesn't exist or is empty,
352 * use NIS exclusively.
354 if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) &&
355 errno == ENOENT) || _yp_statp.st_size == 0)
356 _use_only_yp = _netgr_yp_enabled = 1;
357 if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){
358 compat_state.st_netf = netf;
360 * Icky: grab the first character of the netgroup file
361 * and turn on NIS if it's a '+'. rewind the stream
362 * afterwards so we don't goof up read_for_group() later.
365 fscanf(netf, "%c", &_yp_plus);
368 _use_only_yp = _netgr_yp_enabled = 1;
371 * If we were called specifically for an innetgr()
372 * lookup and we're in NIS-only mode, short-circuit
373 * parse_netgroup() and cut directly to the chase.
375 if (_use_only_yp && _yp_innetgr) {
382 if ((netf = fopen(_PATH_NETGROUP, "re"))) {
383 compat_state.st_netf = netf;
385 if (parse_netgrp(group, &compat_state, 1)) {
386 _compat_clearstate();
388 compat_state.st_grname = strdup(group);
394 compat_state.st_nextgrp = compat_state.st_gr;
399 _compat_clearstate(void)
403 _netgr_yp_enabled = 0;
405 netgr_endstate(&compat_state);
409 * compat_endnetgrent() - cleanup
412 compat_endnetgrent(void *retval, void *mdata, va_list ap)
415 _compat_clearstate();
420 _getnetgrent_r(char **hostp, char **userp, char **domp, char *buf,
421 size_t bufsize, int *errnop, struct netgr_state *st)
427 #define COPY_NG_ELEM(dstp, i) do { \
428 src = st->st_nextgrp->ng_str[(i)]; \
431 len = strlcpy(p, src, bufsize); \
432 if (len >= bufsize) { \
434 return (NS_RETURN); \
438 bufsize -= len + 1; \
442 if (st->st_nextgrp != NULL) {
443 COPY_NG_ELEM(hostp, NG_HOST);
444 COPY_NG_ELEM(userp, NG_USER);
445 COPY_NG_ELEM(domp, NG_DOM);
446 st->st_nextgrp = st->st_nextgrp->ng_next;
458 _listmatch(const char *list, const char *group, int len)
460 const char *ptr = list;
462 int glen = strlen(group);
464 /* skip possible leading whitespace */
465 while (isspace((unsigned char)*ptr))
468 while (ptr < list + len) {
470 while(*ptr != ',' && *ptr != '\0' && !isspace((unsigned char)*ptr))
472 if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr))
474 while (*ptr == ',' || isspace((unsigned char)*ptr))
482 _revnetgr_lookup(char* lookupdom, char* map, const char* str,
483 const char* dom, const char* group)
486 char key[MAXHOSTNAMELEN];
490 for (rot = 0; ; rot++) {
493 snprintf(key, MAXHOSTNAMELEN, "%s.%s", str,
494 dom ? dom : lookupdom);
497 snprintf(key, MAXHOSTNAMELEN, "%s.*", str);
500 snprintf(key, MAXHOSTNAMELEN, "*.%s",
501 dom ? dom : lookupdom);
504 snprintf(key, MAXHOSTNAMELEN, "*.*");
509 y = yp_match(lookupdom, map, key, strlen(key), &result,
512 rv = _listmatch(result, group, resultlen);
516 } else if (y != YPERR_KEY) {
518 * If we get an error other than 'no
519 * such key in map' then something is
520 * wrong and we should stop the search.
529 * Search for a match in a netgroup.
532 compat_innetgr(void *retval, void *mdata, va_list ap)
535 const ns_src src[] = {
536 { mdata, NS_SUCCESS },
540 const char *group, *host, *user, *dom;
542 group = va_arg(ap, const char *);
543 host = va_arg(ap, const char *);
544 user = va_arg(ap, const char *);
545 dom = va_arg(ap, const char *);
547 if (group == NULL || !strlen(group))
552 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
556 * If we're in NIS-only mode, do the search using
557 * NIS 'reverse netgroup' lookups.
559 * What happens with 'reverse netgroup' lookups:
561 * 1) try 'reverse netgroup' lookup
562 * 1.a) if host is specified and user is null:
563 * look in netgroup.byhost
564 * (try host.domain, host.*, *.domain or *.*)
565 * if found, return yes
566 * 1.b) if user is specified and host is null:
567 * look in netgroup.byuser
568 * (try host.domain, host.*, *.domain or *.*)
569 * if found, return yes
570 * 1.c) if both host and user are specified,
571 * don't do 'reverse netgroup' lookup. It won't work.
572 * 1.d) if neither host ane user are specified (why?!?)
573 * don't do 'reverse netgroup' lookup either.
574 * 2) if domain is specified and 'reverse lookup' is done:
575 * 'reverse lookup' was authoritative. bye bye.
576 * 3) otherwise, too bad, try it the slow way.
578 if (_use_only_yp && (host == NULL) != (user == NULL)) {
580 if(yp_get_default_domain(&_netgr_yp_domain))
581 return (NS_NOTFOUND);
582 ret = _revnetgr_lookup(_netgr_yp_domain,
583 host?"netgroup.byhost":"netgroup.byuser",
584 host?host:user, dom, group);
588 } else if (ret == 0 && dom != NULL) {
595 return (_innetgr_fallback(retval, mdata, group, host, user, dom));
599 _innetgr_fallback(void *retval, void *mdata, const char *group, const char *host,
600 const char *user, const char *dom)
602 const ns_src src[] = {
603 { mdata, NS_SUCCESS },
611 if (group == NULL || group[0] == '\0')
614 bufsize = NGRP_STORAGE_INITIAL;
615 buf = malloc(bufsize);
621 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
627 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP,
628 "getnetgrent_r", src, &h, &u, &d, buf, bufsize,
630 if (rv != NS_SUCCESS && ret_errno == ERANGE) {
632 if (bufsize > NGRP_STORAGE_MAX ||
633 (buf = reallocf(buf, bufsize)) == NULL)
636 } while (rv != NS_SUCCESS && ret_errno == ERANGE);
638 if (rv != NS_SUCCESS) {
639 if (rv == NS_NOTFOUND && ret_errno == 0)
644 if ((host == NULL || h == NULL || strcmp(host, h) == 0) &&
645 (user == NULL || u == NULL || strcmp(user, u) == 0) &&
646 (dom == NULL || d == NULL || strcmp(dom, d) == 0)) {
654 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent",
660 innetgr_fallback(void *retval, void *mdata, va_list ap)
662 const char *group, *host, *user, *dom;
664 group = va_arg(ap, const char *);
665 host = va_arg(ap, const char *);
666 user = va_arg(ap, const char *);
667 dom = va_arg(ap, const char *);
669 return (_innetgr_fallback(retval, mdata, group, host, user, dom));
673 * Parse the netgroup file setting up the linked lists.
676 parse_netgrp(const char *group, struct netgr_state *st, int niscompat)
679 struct linelist *lp = st->st_linehead;
681 char *epos, *gpos, *pos, *spos;
682 int freepos, len, strpos;
688 * First, see if the line has already been read in.
691 if (!strcmp(group, lp->l_groupname))
695 if (lp == NULL && (lp = read_for_group(group, st, niscompat)) == NULL)
700 * This error message is largely superflous since the
701 * code handles the error condition sucessfully, and
702 * spewing it out from inside libc can actually hose
705 fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
711 /* Watch for null pointer dereferences, dammit! */
712 while (pos != NULL && *pos != '\0') {
714 grp = malloc(sizeof(*grp));
718 bzero(grp, sizeof(*grp));
720 gpos = strsep(&pos, ")");
724 for (strpos = 0; strpos < 3; strpos++) {
725 if ((spos = strsep(&gpos, ",")) == NULL) {
727 * All other systems I've tested
728 * return NULL for empty netgroup
729 * fields. It's up to user programs
730 * to handle the NULLs appropriately.
738 while (*spos == ' ' || *spos == '\t')
740 if ((epos = strpbrk(spos, " \t"))) {
747 ng[strpos] = malloc(len + 1);
748 if (ng[strpos] == NULL) {
749 for (freepos = 0; freepos < strpos;
755 bcopy(spos, ng[strpos], len + 1);
757 grp->ng_next = st->st_gr;
761 * Note: on other platforms, malformed netgroup
762 * entries are not normally flagged. While we
763 * can catch bad entries and report them, we should
764 * stay silent by default for compatibility's sake.
768 "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
769 ng[NG_HOST] == NULL ? "" : ng[NG_HOST],
770 ng[NG_USER] == NULL ? "" : ",",
771 ng[NG_USER] == NULL ? "" : ng[NG_USER],
772 ng[NG_DOM] == NULL ? "" : ",",
773 ng[NG_DOM] == NULL ? "" : ng[NG_DOM],
778 spos = strsep(&pos, ", \t");
779 if (parse_netgrp(spos, st, niscompat))
784 while (*pos == ' ' || *pos == ',' || *pos == '\t')
791 * Read the netgroup file and save lines until the line for the netgroup
792 * is found. Return 1 if eof is encountered.
794 static struct linelist *
795 read_for_group(const char *group, struct netgr_state *st, int niscompat)
797 char *linep, *olinep, *pos, *spos;
801 char line[LINSIZ + 2];
809 while ((_netgr_yp_enabled && niscompat) ||
810 fgets(line, LINSIZ, netf) != NULL) {
811 if (_netgr_yp_enabled) {
812 if(!_netgr_yp_domain)
813 if(yp_get_default_domain(&_netgr_yp_domain))
815 if (yp_match(_netgr_yp_domain, "netgroup", group,
816 strlen(group), &result, &resultlen)) {
819 return ((struct linelist *)0);
821 _netgr_yp_enabled = 0;
825 if (strlen(result) == 0) {
829 snprintf(line, LINSIZ, "%s %s", group, result);
834 while (fgets(line, LINSIZ, netf) != NULL) {
838 if (niscompat && *pos == '+') {
839 _netgr_yp_enabled = 1;
845 while (*pos == ' ' || *pos == '\t')
848 while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
852 while (*pos == ' ' || *pos == '\t')
854 if (*pos != '\n' && *pos != '\0') {
855 lp = malloc(sizeof (*lp));
859 lp->l_groupname = malloc(len + 1);
860 if (lp->l_groupname == NULL) {
864 bcopy(spos, lp->l_groupname, len);
865 *(lp->l_groupname + len) = '\0';
870 * Loop around handling line continuations.
873 if (*(pos + len - 1) == '\n')
875 if (*(pos + len - 1) == '\\') {
881 linep = malloc(olen + len + 1);
883 free(lp->l_groupname);
890 bcopy(olinep, linep, olen);
893 bcopy(pos, linep + olen, len);
895 *(linep + olen) = '\0';
899 if (fgets(line, LINSIZ, netf)) {
907 lp->l_next = st->st_linehead;
908 st->st_linehead = lp;
911 * If this is the one we wanted, we are done.
913 if (!strcmp(lp->l_groupname, group))
919 * Yucky. The recursive nature of this whole mess might require
920 * us to make more than one pass through the netgroup file.
921 * This might be best left outside the #ifdef YP, but YP is
922 * defined by default anyway, so I'll leave it like this
923 * until I know better.
931 getnetgrent_r(char **hostp, char **userp, char **domp, char *buf, size_t bufsize)
936 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP, "getnetgrent_r",
937 defaultsrc, hostp, userp, domp, buf, bufsize, &ret_errno);
938 if (rv == NS_SUCCESS) {
947 getnetgrent(char **hostp, char **userp, char **domp)
949 static char *ngrp_storage;
950 static size_t ngrp_storage_size;
953 if (ngrp_storage == NULL) {
954 ngrp_storage_size = NGRP_STORAGE_INITIAL;
955 ngrp_storage = malloc(ngrp_storage_size);
956 if (ngrp_storage == NULL)
962 rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP,
963 "getnetgrent_r", defaultsrc, hostp, userp, domp,
964 ngrp_storage, ngrp_storage_size, &ret_errno);
965 if (rv != NS_SUCCESS && ret_errno == ERANGE) {
966 ngrp_storage_size *= 2;
967 if (ngrp_storage_size > NGRP_STORAGE_MAX) {
973 ngrp_storage = reallocf(ngrp_storage,
975 if (ngrp_storage == NULL)
978 } while (rv != NS_SUCCESS && ret_errno == ERANGE);
980 if (rv == NS_SUCCESS) {
989 setnetgrent(const char *netgroup)
992 (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent",
993 defaultsrc, netgroup);
1000 (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent",
1005 innetgr(const char *netgroup, const char *host, const char *user,
1008 static const ns_dtab dtab[] = {
1009 NS_COMPAT_CB(compat_innetgr, NULL)
1010 NS_FALLBACK_CB(innetgr_fallback)
1011 { NULL, NULL, NULL },
1015 rv = _nsdispatch(&result, dtab, NSDB_NETGROUP, "innetgr", defaultsrc,
1016 netgroup, host, user, domain);
1017 return (rv == NS_SUCCESS ? result : 0);