diff --git a/etc/nsswitch.conf b/etc/nsswitch.conf index c95b9a4e01c9..e0f14af76f2d 100644 --- a/etc/nsswitch.conf +++ b/etc/nsswitch.conf @@ -5,6 +5,7 @@ group: compat group_compat: nis hosts: files dns +netgroup: compat networks: files passwd: compat passwd_compat: nis diff --git a/include/netdb.h b/include/netdb.h index 3fa5ff3763e7..0d9cb4e6d872 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -275,6 +275,7 @@ int getnetbyname_r(const char *, struct netent *, char *, size_t, int getnetent_r(struct netent *, char *, size_t, struct netent **, int *); int getnetgrent(char **, char **, char **); +int getnetgrent_r(char **, char **, char **, char *, size_t); int getprotobyname_r(const char *, struct protoent *, char *, size_t, struct protoent **); int getprotobynumber_r(int, struct protoent *, char *, size_t, diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index c53b7dd731a5..0d5dfec80cce 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -410,6 +410,7 @@ FBSD_1.3 { }; FBSD_1.4 { + getnetgrent_r; pthread_mutex_consistent; pthread_mutexattr_getrobust; pthread_mutexattr_setrobust; diff --git a/lib/libc/gen/getnetgrent.3 b/lib/libc/gen/getnetgrent.3 index 7483e4f346df..8547e66cf443 100644 --- a/lib/libc/gen/getnetgrent.3 +++ b/lib/libc/gen/getnetgrent.3 @@ -28,7 +28,7 @@ .\" @(#)getnetgrent.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 4, 1993 +.Dd June 5, 2016 .Dt GETNETGRENT 3 .Os .Sh NAME @@ -44,6 +44,8 @@ .Ft int .Fn getnetgrent "char **host" "char **user" "char **domain" .Ft int +.Fn getnetgrent_r "char **host" "char **user" "char **domain" "char *buf" "size_t bufsize" +.Ft int .Fn innetgr "const char *netgroup" "const char *host" "const char *user" "const char *domain" .Ft void .Fn setnetgrent "const char *netgroup" diff --git a/lib/libc/gen/getnetgrent.c b/lib/libc/gen/getnetgrent.c index ab3a46f19a0f..d71928e5a189 100644 --- a/lib/libc/gen/getnetgrent.c +++ b/lib/libc/gen/getnetgrent.c @@ -36,12 +36,21 @@ static char sccsid[] = "@(#)getnetgrent.c 8.2 (Berkeley) 4/27/95"; #include __FBSDID("$FreeBSD$"); +#include "namespace.h" + #include +#include +#include +#include +#include +#include #include #include #include #include +#include "nss_tls.h" + #ifdef YP /* * Notes: @@ -98,6 +107,16 @@ static int _yp_innetgr; #define _PATH_NETGROUP "/etc/netgroup" #endif +enum constants { + NGRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + NGRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ +}; + +static const ns_src defaultsrc[] = { + { NSSRC_COMPAT, NS_SUCCESS }, + { NULL, 0 }, +}; + /* * Static Variables and functions used by setnetgrent(), getnetgrent() and * endnetgrent(). @@ -117,51 +136,212 @@ struct netgrp { struct netgrp *ng_next; /* Chain ptr */ char *ng_str[3]; /* Field pointers, see below */ }; + +struct netgr_state { + FILE *st_netf; + struct linelist *st_linehead; + struct netgrp *st_nextgrp; + struct netgrp *st_gr; + char *st_grname; +}; + #define NG_HOST 0 /* Host name */ #define NG_USER 1 /* User name */ #define NG_DOM 2 /* and Domain name */ -static struct linelist *linehead = (struct linelist *)0; -static struct netgrp *nextgrp = (struct netgrp *)0; -static struct { - struct netgrp *gr; - char *grname; -} grouphead = { - (struct netgrp *)0, - (char *)0, -}; -static FILE *netf = (FILE *)0; +static void netgr_endstate(void *); +NSS_TLS_HANDLING(netgr); -static int parse_netgrp(const char *); -static struct linelist *read_for_group(const char *); -void setnetgrent(const char *); -void endnetgrent(void); -int getnetgrent(char **, char **, char **); -int innetgr(const char *, const char *, const char *, const char *); +static int files_endnetgrent(void *, void *, va_list); +static int files_getnetgrent_r(void *, void *, va_list); +static int files_setnetgrent(void *, void *, va_list); + +static int compat_endnetgrent(void *, void *, va_list); +static int compat_innetgr(void *, void *, va_list); +static int compat_getnetgrent_r(void *, void *, va_list); +static int compat_setnetgrent(void *, void *, va_list); + +static void _compat_clearstate(void); +static int _getnetgrent_r(char **, char **, char **, char *, size_t, int *, + struct netgr_state *); +static int _innetgr_fallback(void *, void *, const char *, const char *, + const char *, const char *); +static int innetgr_fallback(void *, void *, va_list); +static int parse_netgrp(const char *, struct netgr_state *, int); +static struct linelist *read_for_group(const char *, struct netgr_state *, int); #define LINSIZ 1024 /* Length of netgroup file line */ +static const ns_dtab getnetgrent_dtab[] = { + NS_FILES_CB(files_getnetgrent_r, NULL) + NS_COMPAT_CB(compat_getnetgrent_r, NULL) + { NULL, NULL, NULL }, +}; + +static const ns_dtab setnetgrent_dtab[] = { + NS_FILES_CB(files_setnetgrent, NULL) + NS_COMPAT_CB(compat_setnetgrent, NULL) + { NULL, NULL, NULL }, +}; + +static const ns_dtab endnetgrent_dtab[] = { + NS_FILES_CB(files_endnetgrent, NULL) + NS_COMPAT_CB(compat_endnetgrent, NULL) + { NULL, NULL, NULL }, +}; + +static struct netgr_state compat_state; + +static void +netgr_endstate(void *arg) +{ + struct linelist *lp, *olp; + struct netgrp *gp, *ogp; + struct netgr_state *st; + + st = (struct netgr_state *)arg; + lp = st->st_linehead; + while (lp != NULL) { + olp = lp; + lp = lp->l_next; + free(olp->l_groupname); + free(olp->l_line); + free(olp); + } + st->st_linehead = NULL; + if (st->st_grname != NULL) { + free(st->st_grname); + st->st_grname = NULL; + } + gp = st->st_gr; + while (gp != NULL) { + ogp = gp; + gp = gp->ng_next; + free(ogp->ng_str[NG_HOST]); + free(ogp->ng_str[NG_USER]); + free(ogp->ng_str[NG_DOM]); + free(ogp); + } + st->st_gr = NULL; + st->st_nextgrp = NULL; +} + +static int +files_getnetgrent_r(void *retval, void *mdata, va_list ap) +{ + struct netgr_state *st; + char **hostp, **userp, **domp, *buf; + size_t bufsize; + int *errnop; + + hostp = va_arg(ap, char **); + userp = va_arg(ap, char **); + domp = va_arg(ap, char **); + buf = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + if (netgr_getstate(&st) != 0) + return (NS_UNAVAIL); + + return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop, st)); +} + +static int +files_setnetgrent(void *retval, void *mdata, va_list ap) +{ + const ns_src src[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 }, + }; + struct netgr_state *st; + const char *group; + int rv; + + group = va_arg(ap, const char *); + + if (group == NULL || group[0] == '\0') + return (NS_RETURN); + + rv = netgr_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + if (st->st_gr == NULL || strcmp(group, st->st_grname) != 0) { + (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, + "endnetgrent", src); + if ((st->st_netf = fopen(_PATH_NETGROUP, "re")) != NULL) { + if (parse_netgrp(group, st, 0) != 0) + (void)_nsdispatch(NULL, endnetgrent_dtab, + NSDB_NETGROUP, "endnetgrent", src); + else + st->st_grname = strdup(group); + (void)fclose(st->st_netf); + st->st_netf = NULL; + } + } + st->st_nextgrp = st->st_gr; + return (st->st_grname != NULL ? NS_SUCCESS : NS_NOTFOUND); +} + +static int +files_endnetgrent(void *retval, void *mdata, va_list ap) +{ + struct netgr_state *st; + + if (netgr_getstate(&st) != 0) + return (NS_UNAVAIL); + netgr_endstate(st); + return (NS_SUCCESS); +} + +static int +compat_getnetgrent_r(void *retval, void *mdata, va_list ap) +{ + char **hostp, **userp, **domp, *buf; + size_t bufsize; + int *errnop; +#ifdef YP + _yp_innetgr = 0; +#endif + + hostp = va_arg(ap, char **); + userp = va_arg(ap, char **); + domp = va_arg(ap, char **); + buf = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + return (_getnetgrent_r(hostp, userp, domp, buf, bufsize, errnop, + &compat_state)); +} + /* - * setnetgrent() + * compat_setnetgrent() * Parse the netgroup file looking for the netgroup and build the list * of netgrp structures. Let parse_netgrp() and read_for_group() do * most of the work. */ -void -setnetgrent(const char *group) +static int +compat_setnetgrent(void *retval, void *mdata, va_list ap) { + FILE *netf; + const char *group; #ifdef YP struct stat _yp_statp; char _yp_plus; #endif + group = va_arg(ap, const char *); + /* Sanity check */ - if (group == NULL || !strlen(group)) - return; + return (NS_RETURN); + + if (compat_state.st_gr == NULL || + strcmp(group, compat_state.st_grname) != 0) { + _compat_clearstate(); - if (grouphead.gr == NULL || strcmp(group, grouphead.grname)) { - endnetgrent(); #ifdef YP /* Presumed guilty until proven innocent. */ _use_only_yp = 0; @@ -173,6 +353,7 @@ setnetgrent(const char *group) errno == ENOENT) || _yp_statp.st_size == 0) _use_only_yp = _netgr_yp_enabled = 1; if ((netf = fopen(_PATH_NETGROUP,"re")) != NULL ||_use_only_yp){ + compat_state.st_netf = netf; /* * Icky: grab the first character of the netgroup file * and turn on NIS if it's a '+'. rewind the stream @@ -193,79 +374,81 @@ setnetgrent(const char *group) /* dohw! */ if (netf != NULL) fclose(netf); - return; + return (NS_RETURN); } #else if ((netf = fopen(_PATH_NETGROUP, "re"))) { + compat_state.st_netf = netf; #endif - if (parse_netgrp(group)) - endnetgrent(); - else { - grouphead.grname = strdup(group); + if (parse_netgrp(group, &compat_state, 1)) { + _compat_clearstate(); + } else { + compat_state.st_grname = strdup(group); } if (netf) fclose(netf); } } - nextgrp = grouphead.gr; + compat_state.st_nextgrp = compat_state.st_gr; + return (NS_SUCCESS); } -/* - * Get the next netgroup off the list. - */ -int -getnetgrent(char **hostp, char **userp, char **domp) +static void +_compat_clearstate(void) { -#ifdef YP - _yp_innetgr = 0; -#endif - if (nextgrp) { - *hostp = nextgrp->ng_str[NG_HOST]; - *userp = nextgrp->ng_str[NG_USER]; - *domp = nextgrp->ng_str[NG_DOM]; - nextgrp = nextgrp->ng_next; - return (1); - } - return (0); -} - -/* - * endnetgrent() - cleanup - */ -void -endnetgrent(void) -{ - struct linelist *lp, *olp; - struct netgrp *gp, *ogp; - - lp = linehead; - while (lp) { - olp = lp; - lp = lp->l_next; - free(olp->l_groupname); - free(olp->l_line); - free(olp); - } - linehead = NULL; - if (grouphead.grname) { - free(grouphead.grname); - grouphead.grname = NULL; - } - gp = grouphead.gr; - while (gp) { - ogp = gp; - gp = gp->ng_next; - free(ogp->ng_str[NG_HOST]); - free(ogp->ng_str[NG_USER]); - free(ogp->ng_str[NG_DOM]); - free(ogp); - } - grouphead.gr = NULL; - nextgrp = NULL; #ifdef YP _netgr_yp_enabled = 0; #endif + netgr_endstate(&compat_state); +} + +/* + * compat_endnetgrent() - cleanup + */ +static int +compat_endnetgrent(void *retval, void *mdata, va_list ap) +{ + + _compat_clearstate(); + return (NS_SUCCESS); +} + +int +_getnetgrent_r(char **hostp, char **userp, char **domp, char *buf, + size_t bufsize, int *errnop, struct netgr_state *st) +{ + char *p, *src; + size_t len; + int rv; + +#define COPY_NG_ELEM(dstp, i) do { \ + src = st->st_nextgrp->ng_str[(i)]; \ + if (src == NULL) \ + src = ""; \ + len = strlcpy(p, src, bufsize); \ + if (len >= bufsize) { \ + *errnop = ERANGE; \ + return (NS_RETURN); \ + } \ + *(dstp) = p; \ + p += len + 1; \ + bufsize -= len + 1; \ +} while (0) + + p = buf; + if (st->st_nextgrp != NULL) { + COPY_NG_ELEM(hostp, NG_HOST); + COPY_NG_ELEM(userp, NG_USER); + COPY_NG_ELEM(domp, NG_DOM); + st->st_nextgrp = st->st_nextgrp->ng_next; + rv = NS_SUCCESS; + } else { + rv = NS_NOTFOUND; + } +#undef COPY_NG_ELEM + + return (rv); } #ifdef YP @@ -343,20 +526,29 @@ _revnetgr_lookup(char* lookupdom, char* map, const char* str, /* * Search for a match in a netgroup. */ -int -innetgr(const char *group, const char *host, const char *user, const char *dom) +static int +compat_innetgr(void *retval, void *mdata, va_list ap) { - char *hst, *usr, *dm; - /* Sanity check */ - +#ifdef YP + const ns_src src[] = { + { mdata, NS_SUCCESS }, + { NULL, 0 }, + }; +#endif + const char *group, *host, *user, *dom; + + group = va_arg(ap, const char *); + host = va_arg(ap, const char *); + user = va_arg(ap, const char *); + dom = va_arg(ap, const char *); + if (group == NULL || !strlen(group)) - return (0); + return (NS_RETURN); #ifdef YP _yp_innetgr = 1; -#endif - setnetgrent(group); -#ifdef YP + (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent", + src, group); _yp_innetgr = 0; /* * If we're in NIS-only mode, do the search using @@ -384,38 +576,105 @@ innetgr(const char *group, const char *host, const char *user, const char *dom) if (_use_only_yp && (host == NULL) != (user == NULL)) { int ret; if(yp_get_default_domain(&_netgr_yp_domain)) - return (0); - ret = _revnetgr_lookup(_netgr_yp_domain, + return (NS_NOTFOUND); + ret = _revnetgr_lookup(_netgr_yp_domain, host?"netgroup.byhost":"netgroup.byuser", host?host:user, dom, group); - if (ret == 1) - return (1); - else if (ret == 0 && dom != NULL) - return (0); + if (ret == 1) { + *(int *)retval = 1; + return (NS_SUCCESS); + } else if (ret == 0 && dom != NULL) { + *(int *)retval = 0; + return (NS_SUCCESS); + } } - - setnetgrent(group); #endif /* YP */ - while (getnetgrent(&hst, &usr, &dm)) - if ((host == NULL || hst == NULL || !strcmp(host, hst)) && - (user == NULL || usr == NULL || !strcmp(user, usr)) && - ( dom == NULL || dm == NULL || !strcmp(dom, dm))) { - endnetgrent(); - return (1); + return (_innetgr_fallback(retval, mdata, group, host, user, dom)); +} + +static int +_innetgr_fallback(void *retval, void *mdata, const char *group, const char *host, + const char *user, const char *dom) +{ + const ns_src src[] = { + { mdata, NS_SUCCESS }, + { NULL, 0 }, + }; + char *h, *u, *d; + char *buf; + size_t bufsize; + int rv, ret_errno; + + if (group == NULL || group[0] == '\0') + return (NS_RETURN); + + bufsize = NGRP_STORAGE_INITIAL; + buf = malloc(bufsize); + if (buf == NULL) + return (NS_UNAVAIL); + + *(int *)retval = 0; + + (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent", + src, group); + + for (;;) { + do { + ret_errno = 0; + rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP, + "getnetgrent_r", src, &h, &u, &d, buf, bufsize, + &ret_errno); + if (rv != NS_SUCCESS && ret_errno == ERANGE) { + bufsize *= 2; + if (bufsize > NGRP_STORAGE_MAX || + (buf = reallocf(buf, bufsize)) == NULL) + goto out; + } + } while (rv != NS_SUCCESS && ret_errno == ERANGE); + + if (rv != NS_SUCCESS) { + if (rv == NS_NOTFOUND && ret_errno == 0) + rv = NS_SUCCESS; + break; } - endnetgrent(); - return (0); + + if ((host == NULL || h == NULL || strcmp(host, h) == 0) && + (user == NULL || u == NULL || strcmp(user, u) == 0) && + (dom == NULL || d == NULL || strcmp(dom, d) == 0)) { + *(int *)retval = 1; + break; + } + } + +out: + free(buf); + (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent", + src); + return (rv); +} + +static int +innetgr_fallback(void *retval, void *mdata, va_list ap) +{ + const char *group, *host, *user, *dom; + + group = va_arg(ap, const char *); + host = va_arg(ap, const char *); + user = va_arg(ap, const char *); + dom = va_arg(ap, const char *); + + return (_innetgr_fallback(retval, mdata, group, host, user, dom)); } /* * Parse the netgroup file setting up the linked lists. */ static int -parse_netgrp(const char *group) +parse_netgrp(const char *group, struct netgr_state *st, int niscompat) { struct netgrp *grp; - struct linelist *lp = linehead; + struct linelist *lp = st->st_linehead; char **ng; char *epos, *gpos, *pos, *spos; int freepos, len, strpos; @@ -431,7 +690,7 @@ parse_netgrp(const char *group) break; lp = lp->l_next; } - if (lp == NULL && (lp = read_for_group(group)) == NULL) + if (lp == NULL && (lp = read_for_group(group, st, niscompat)) == NULL) return (1); if (lp->l_parsed) { #ifdef DEBUG @@ -493,8 +752,8 @@ parse_netgrp(const char *group) } bcopy(spos, ng[strpos], len + 1); } - grp->ng_next = grouphead.gr; - grouphead.gr = grp; + grp->ng_next = st->st_gr; + st->st_gr = grp; #ifdef DEBUG /* * Note: on other platforms, malformed netgroup @@ -515,7 +774,7 @@ parse_netgrp(const char *group) #endif } else { spos = strsep(&pos, ", \t"); - if (parse_netgrp(spos)) + if (parse_netgrp(spos, st, niscompat)) continue; } if (pos == NULL) @@ -531,19 +790,22 @@ parse_netgrp(const char *group) * is found. Return 1 if eof is encountered. */ static struct linelist * -read_for_group(const char *group) +read_for_group(const char *group, struct netgr_state *st, int niscompat) { char *linep, *olinep, *pos, *spos; int len, olen; int cont; struct linelist *lp; char line[LINSIZ + 2]; + FILE *netf; #ifdef YP char *result; int resultlen; linep = NULL; - while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) { + netf = st->st_netf; + while ((_netgr_yp_enabled && niscompat) || + fgets(line, LINSIZ, netf) != NULL) { if (_netgr_yp_enabled) { if(!_netgr_yp_domain) if(yp_get_default_domain(&_netgr_yp_domain)) @@ -571,7 +833,7 @@ read_for_group(const char *group) #endif pos = (char *)&line; #ifdef YP - if (*pos == '+') { + if (niscompat && *pos == '+') { _netgr_yp_enabled = 1; continue; } @@ -588,11 +850,11 @@ read_for_group(const char *group) while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\n' && *pos != '\0') { - lp = (struct linelist *)malloc(sizeof (*lp)); - if (lp == NULL) + lp = malloc(sizeof (*lp)); + if (lp == NULL) return (NULL); lp->l_parsed = 0; - lp->l_groupname = (char *)malloc(len + 1); + lp->l_groupname = malloc(len + 1); if (lp->l_groupname == NULL) { free(lp); return (NULL); @@ -640,8 +902,8 @@ read_for_group(const char *group) } } while (cont); lp->l_line = linep; - lp->l_next = linehead; - linehead = lp; + lp->l_next = st->st_linehead; + st->st_linehead = lp; /* * If this is the one we wanted, we are done. @@ -662,3 +924,93 @@ read_for_group(const char *group) #endif return (NULL); } + +int +getnetgrent_r(char **hostp, char **userp, char **domp, char *buf, size_t bufsize) +{ + int rv, ret_errno; + + ret_errno = 0; + rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP, "getnetgrent_r", + defaultsrc, hostp, userp, domp, buf, bufsize, &ret_errno); + if (rv == NS_SUCCESS) { + return (1); + } else { + errno = ret_errno; + return (0); + } +} + +int +getnetgrent(char **hostp, char **userp, char **domp) +{ + static char *ngrp_storage; + static size_t ngrp_storage_size; + int ret_errno, rv; + + if (ngrp_storage == NULL) { + ngrp_storage_size = NGRP_STORAGE_INITIAL; + ngrp_storage = malloc(ngrp_storage_size); + if (ngrp_storage == NULL) + return (0); + } + + do { + ret_errno = 0; + rv = _nsdispatch(NULL, getnetgrent_dtab, NSDB_NETGROUP, + "getnetgrent_r", defaultsrc, hostp, userp, domp, + ngrp_storage, ngrp_storage_size, &ret_errno); + if (rv != NS_SUCCESS && ret_errno == ERANGE) { + ngrp_storage_size *= 2; + if (ngrp_storage_size > NGRP_STORAGE_MAX) { + free(ngrp_storage); + ngrp_storage = NULL; + errno = ERANGE; + return (0); + } + ngrp_storage = reallocf(ngrp_storage, + ngrp_storage_size); + if (ngrp_storage == NULL) + return (0); + } + } while (rv != NS_SUCCESS && ret_errno == ERANGE); + + if (rv == NS_SUCCESS) { + return (1); + } else { + errno = ret_errno; + return (0); + } +} + +void +setnetgrent(const char *netgroup) +{ + + (void)_nsdispatch(NULL, setnetgrent_dtab, NSDB_NETGROUP, "setnetgrent", + defaultsrc, netgroup); +} + +void +endnetgrent(void) +{ + + (void)_nsdispatch(NULL, endnetgrent_dtab, NSDB_NETGROUP, "endnetgrent", + defaultsrc); +} + +int +innetgr(const char *netgroup, const char *host, const char *user, + const char *domain) +{ + static const ns_dtab dtab[] = { + NS_COMPAT_CB(compat_innetgr, NULL) + NS_FALLBACK_CB(innetgr_fallback) + { NULL, NULL, NULL }, + }; + int result, rv; + + rv = _nsdispatch(&result, dtab, NSDB_NETGROUP, "innetgr", defaultsrc, + netgroup, host, user, domain); + return (rv == NS_SUCCESS ? result : 0); +} diff --git a/share/man/man5/nsswitch.conf.5 b/share/man/man5/nsswitch.conf.5 index b1fbbd489e58..abeb24d5c2f7 100644 --- a/share/man/man5/nsswitch.conf.5 +++ b/share/man/man5/nsswitch.conf.5 @@ -33,7 +33,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 25, 2013 +.Dd June 6, 2016 .Dt NSSWITCH.CONF 5 .Os .Sh NAME @@ -148,7 +148,9 @@ The following databases are used by the following C library functions: .Xr getprotoent 3 .It netgroup .Xr getnetgrent 3 , +.Xr getnetgrent_r 3 , .Xr setnetgrent 3 , +.Xr endnetgrent 3 , .Xr innetgr 3 .El .Ss Status codes