+diff -Nrup a/grp/initgroups.c b/grp/initgroups.c
+--- a/grp/initgroups.c 2012-08-06 15:07:48.935060494 -0600
++++ b/grp/initgroups.c 2012-08-23 14:19:49.370442142 -0600
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1989,91,93,1996-2005,2006,2008 Free Software Foundation, Inc.
++/* Copyright (C) 1989,91,93,1996-2006,2008,2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+@@ -58,7 +58,8 @@ internal_getgrouplist (const char *user,
+ if (__nss_not_use_nscd_group > 0
+ && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
+ __nss_not_use_nscd_group = 0;
+- if (!__nss_not_use_nscd_group)
++ if (!__nss_not_use_nscd_group
++ && !__nss_database_custom[NSS_DBSIDX_group])
+ {
+ int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
+ if (n >= 0)
+diff -Nrup a/inet/getnetgrent_r.c b/inet/getnetgrent_r.c
+--- a/inet/getnetgrent_r.c 2010-05-04 05:27:23.000000000 -0600
++++ b/inet/getnetgrent_r.c 2012-08-06 15:10:05.865520055 -0600
+@@ -28,6 +28,7 @@
+ #include "netgroup.h"
+ #include "nsswitch.h"
+ #include <sysdep.h>
++#include <nscd/nscd_proto.h>
+
+
+ /* Protect above variable against multiple uses at the same time. */
+@@ -101,7 +102,7 @@ endnetgrent_hook (struct __netgrent *dat
+ {
+ enum nss_status (*endfct) (struct __netgrent *);
+
+- if (datap->nip == NULL)
++ if (datap->nip == NULL || datap->nip == (service_user *) -1l)
+ return;
+
+ endfct = __nss_lookup_function (datap->nip, "endnetgrent");
+@@ -189,8 +190,21 @@ setnetgrent (const char *group)
+
+ __libc_lock_lock (lock);
+
++ if (__nss_not_use_nscd_netgroup > 0
++ && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
++ __nss_not_use_nscd_netgroup = 0;
++
++ if (!__nss_not_use_nscd_netgroup
++ && !__nss_database_custom[NSS_DBSIDX_netgroup])
++ {
++ result = __nscd_setnetgrent (group, &dataset);
++ if (result >= 0)
++ goto out;
++ }
++
+ result = internal_setnetgrent (group, &dataset);
+
++ out:
+ __libc_lock_unlock (lock);
+
+ return result;
+@@ -226,6 +240,26 @@ int internal_getnetgrent_r (char **hostp
+ char *buffer, size_t buflen, int *errnop);
+ libc_hidden_proto (internal_getnetgrent_r)
+
++
++static enum nss_status
++nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
++ int *errnop)
++{
++ if (datap->cursor >= datap->data + datap->data_size)
++ return NSS_STATUS_UNAVAIL;
++
++ datap->type = triple_val;
++ datap->val.triple.host = datap->cursor;
++ datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
++ datap->val.triple.user = datap->cursor;
++ datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
++ datap->val.triple.domain = datap->cursor;
++ datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
++
++ return NSS_STATUS_SUCCESS;
++}
++
++
+ int
+ internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
+ struct __netgrent *datap,
+@@ -239,9 +273,18 @@ internal_getnetgrent_r (char **hostp, ch
+ /* Run through available functions, starting with the same function last
+ run. We will repeat each function as long as it succeeds, and then go
+ on to the next service action. */
+- int no_more = (datap->nip == NULL
+- || (fct = __nss_lookup_function (datap->nip, "getnetgrent_r"))
+- == NULL);
++ int no_more = datap->nip == NULL;
++ if (! no_more)
++ {
++ if (datap->nip == (service_user *) -1l)
++ fct = nscd_getnetgrent;
++ else
++ {
++ fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
++ no_more = fct == NULL;
++ }
++ }
++
+ while (! no_more)
+ {
+ status = (*fct) (datap, buffer, buflen, &errno);
+@@ -337,6 +380,18 @@ int
+ innetgr (const char *netgroup, const char *host, const char *user,
+ const char *domain)
+ {
++ if (__nss_not_use_nscd_netgroup > 0
++ && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
++ __nss_not_use_nscd_netgroup = 0;
++
++ if (!__nss_not_use_nscd_netgroup
++ && !__nss_database_custom[NSS_DBSIDX_netgroup])
++ {
++ int result = __nscd_innetgr (netgroup, host, user, domain);
++ if (result >= 0)
++ return result;
++ }
++
+ union
+ {
+ int (*f) (const char *, struct __netgrent *);
+@@ -444,7 +499,7 @@ innetgr (const char *netgroup, const cha
+ entry.needed_groups = tmp->next;
+ tmp->next = entry.known_groups;
+ entry.known_groups = tmp;
+- current_group = entry.known_groups->name;
++ current_group = tmp->name;
+ continue;
+ }
+
+diff -Nrup a/nscd/Makefile b/nscd/Makefile
+--- a/nscd/Makefile 2010-05-04 05:27:23.000000000 -0600
++++ b/nscd/Makefile 2012-08-06 15:08:19.045941627 -0600
+@@ -22,7 +22,7 @@
+ subdir := nscd
+
+ routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai \
+- nscd_initgroups nscd_getserv_r
++ nscd_initgroups nscd_getserv_r nscd_netgroup
+ aux := nscd_helper
+
+ include ../Makeconfig
+@@ -34,7 +34,8 @@ nscd-modules := nscd connections pwdcach
+ getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \
+ getsrvbynm_r getsrvbypt_r servicescache \
+ dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
+- xmalloc xstrdup aicache initgrcache gai res_hconf
++ xmalloc xstrdup aicache initgrcache gai res_hconf \
++ netgroupcache
+
+ ifeq ($(have-thread-library),yes)
+
+@@ -122,6 +123,7 @@ CFLAGS-servicescache.c += $(nscd-cflags)
+ CFLAGS-getsrvbynm_r.c += $(nscd-cflags)
+ CFLAGS-getsrvbypt_r.c += $(nscd-cflags)
+ CFLAGS-res_hconf.c += $(nscd-cflags)
++CFLAGS-netgroupcache.c += $(nscd-cflags)
+
+ ifeq (yesyes,$(have-fpie)$(build-shared))
+ relro-LDFLAGS += -Wl,-z,now
+diff -Nrup a/nscd/cache.c b/nscd/cache.c
+--- a/nscd/cache.c 2012-08-06 15:07:48.973060344 -0600
++++ b/nscd/cache.c 2012-08-06 15:08:19.046941626 -0600
+@@ -60,7 +60,9 @@ static time_t (*const readdfcts[LASTREQ]
+ [GETAI] = readdhstai,
+ [INITGROUPS] = readdinitgroups,
+ [GETSERVBYNAME] = readdservbyname,
+- [GETSERVBYPORT] = readdservbyport
++ [GETSERVBYPORT] = readdservbyport,
++ [GETNETGRENT] = readdgetnetgrent,
++ [INNETGR] = readdinnetgr
+ };
+
+
+@@ -70,7 +72,7 @@ static time_t (*const readdfcts[LASTREQ]
+
+ This function must be called with the read-lock held. */
+ struct datahead *
+-cache_search (request_type type, void *key, size_t len,
++cache_search (request_type type, const void *key, size_t len,
+ struct database_dyn *table, uid_t owner)
+ {
+ unsigned long int hash = __nis_hash (key, len) % table->head->module;
+diff -Nrup a/nscd/connections.c b/nscd/connections.c
+--- a/nscd/connections.c 2012-08-06 15:07:49.076059937 -0600
++++ b/nscd/connections.c 2012-08-21 21:36:10.210358578 -0600
+@@ -57,11 +57,6 @@
+ #endif
+
+
+-/* Wrapper functions with error checking for standard functions. */
+-extern void *xmalloc (size_t n);
+-extern void *xcalloc (size_t n, size_t s);
+-extern void *xrealloc (void *o, size_t n);
+-
+ /* Support to run nscd as an unprivileged user */
+ const char *server_user;
+ static uid_t server_uid;
+@@ -100,7 +95,10 @@ const char *const serv2str[LASTREQ] =
+ [INITGROUPS] = "INITGROUPS",
+ [GETSERVBYNAME] = "GETSERVBYNAME",
+ [GETSERVBYPORT] = "GETSERVBYPORT",
+- [GETFDSERV] = "GETFDSERV"
++ [GETFDSERV] = "GETFDSERV",
++ [GETNETGRENT] = "GETNETGRENT",
++ [INNETGR] = "INNETGR",
++ [GETFDNETGR] = "GETFDNETGR"
+ };
+
+ /* The control data structures for the services. */
+@@ -189,6 +187,27 @@ struct database_dyn dbs[lastdb] =
+ .wr_fd = -1,
+ .ro_fd = -1,
+ .mmap_used = false
++ },
++ [netgrdb] = {
++ .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
++ .prune_lock = PTHREAD_MUTEX_INITIALIZER,
++ .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
++ .enabled = 0,
++ .check_file = 1,
++ .persistent = 0,
++ .propagate = 0, /* Not used. */
++ .shared = 0,
++ .max_db_size = DEFAULT_MAX_DB_SIZE,
++ .suggested_module = DEFAULT_SUGGESTED_MODULE,
++ .reset_res = 0,
++ .filename = "/etc/netgroup",
++ .db_filename = _PATH_NSCD_NETGROUP_DB,
++ .disabled_iov = &netgroup_iov_disabled,
++ .postimeout = 28800,
++ .negtimeout = 20,
++ .wr_fd = -1,
++ .ro_fd = -1,
++ .mmap_used = false
+ }
+ };
+
+@@ -218,7 +237,10 @@ static struct
+ [INITGROUPS] = { true, &dbs[grpdb] },
+ [GETSERVBYNAME] = { true, &dbs[servdb] },
+ [GETSERVBYPORT] = { true, &dbs[servdb] },
+- [GETFDSERV] = { false, &dbs[servdb] }
++ [GETFDSERV] = { false, &dbs[servdb] },
++ [GETNETGRENT] = { true, &dbs[netgrdb] },
++ [INNETGR] = { true, &dbs[netgrdb] },
++ [GETFDNETGR] = { false, &dbs[netgrdb] }
+ };
+
+
+@@ -366,7 +388,8 @@ check_use (const char *data, nscd_ssize_
+ static int
+ verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
+ {
+- assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb);
++ assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb
++ || dbnr == netgrdb);
+
+ time_t now = time (NULL);
+
+@@ -1241,6 +1264,14 @@ request from '%s' [%ld] not handled due
+ addservbyport (db, fd, req, key, uid);
+ break;
+
++ case GETNETGRENT:
++ addgetnetgrent (db, fd, req, key, uid);
++ break;
++
++ case INNETGR:
++ addinnetgr (db, fd, req, key, uid);
++ break;
++
+ case GETSTAT:
+ case SHUTDOWN:
+ case INVALIDATE:
+@@ -1287,6 +1318,7 @@ request from '%s' [%ld] not handled due
+ case GETFDGR:
+ case GETFDHST:
+ case GETFDSERV:
++ case GETFDNETGR:
+ #ifdef SCM_RIGHTS
+ send_ro_fd (reqinfo[req->type].db, key, fd);
+ #endif
+diff -Nrup a/nscd/netgroupcache.c b/nscd/netgroupcache.c
+--- a/nscd/netgroupcache.c 1969-12-31 17:00:00.000000000 -0700
++++ b/nscd/netgroupcache.c 2012-08-24 11:38:05.118254176 -0600
+@@ -0,0 +1,667 @@
++/* Cache handling for netgroup lookup.
++ Copyright (C) 2011 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++ Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published
++ by the Free Software Foundation; version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software Foundation,
++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
++
++#include <alloca.h>
++#include <assert.h>
++#include <errno.h>
++#include <libintl.h>
++#include <stdbool.h>
++#include <unistd.h>
++#include <sys/mman.h>
++
++#include "../inet/netgroup.h"
++#include "nscd.h"
++#include "dbg_log.h"
++#ifdef HAVE_SENDFILE
++# include <kernel-features.h>
++#endif
++
++
++/* This is the standard reply in case the service is disabled. */
++static const netgroup_response_header disabled =
++{
++ .version = NSCD_VERSION,
++ .found = -1,
++ .nresults = 0,
++ .result_len = 0
++};
++
++/* This is the struct describing how to write this record. */
++const struct iovec netgroup_iov_disabled =
++{
++ .iov_base = (void *) &disabled,
++ .iov_len = sizeof (disabled)
++};
++
++
++/* This is the standard reply in case we haven't found the dataset. */
++static const netgroup_response_header notfound =
++{
++ .version = NSCD_VERSION,
++ .found = 0,
++ .nresults = 0,
++ .result_len = 0
++};
++
++
++struct dataset
++{
++ struct datahead head;
++ netgroup_response_header resp;
++ char strdata[0];
++};
++
++
++static time_t
++addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
++ const char *key, uid_t uid, struct hashentry *he,
++ struct datahead *dh, struct dataset **resultp)
++{
++ if (__builtin_expect (debug_level > 0, 0))
++ {
++ if (he == NULL)
++ dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key);
++ else
++ dbg_log (_("Reloading \"%s\" in netgroup cache!"), key);
++ }
++
++ static service_user *netgroup_database;
++ time_t timeout;
++ struct dataset *dataset;
++ bool cacheable = false;
++ ssize_t total;
++
++ char *key_copy = NULL;
++ struct __netgrent data;
++ size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
++ size_t buffilled = sizeof (*dataset);
++ char *buffer = NULL;
++ size_t nentries = 0;
++ bool use_malloc = false;
++ size_t group_len = strlen (key) + 1;
++ union
++ {
++ struct name_list elem;
++ char mem[sizeof (struct name_list) + group_len];
++ } first_needed;
++
++ if (netgroup_database == NULL
++ && __nss_database_lookup ("netgroup", NULL, NULL, &netgroup_database))
++ {
++ /* No such service. */
++ total = sizeof (notfound);
++ timeout = time (NULL) + db->negtimeout;
++
++ if (fd != -1)
++ TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL));
++
++ dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
++ /* If we cannot permanently store the result, so be it. */
++ if (dataset != NULL)
++ {
++ dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
++ dataset->head.recsize = total;
++ dataset->head.notfound = true;
++ dataset->head.nreloads = 0;
++ dataset->head.usable = true;
++
++ /* Compute the timeout time. */
++ timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
++ dataset->head.ttl = db->negtimeout;
++
++ /* This is the reply. */
++ memcpy (&dataset->resp, ¬found, total);
++
++ /* Copy the key data. */
++ memcpy (dataset->strdata, key, req->key_len);
++
++ cacheable = true;
++ }
++
++ goto writeout;
++ }
++
++ memset (&data, '\0', sizeof (data));
++ buffer = alloca (buflen);
++ first_needed.elem.next = &first_needed.elem;
++ memcpy (first_needed.elem.name, key, group_len);
++ data.needed_groups = &first_needed.elem;
++
++ while (data.needed_groups != NULL)
++ {
++ /* Add the next group to the list of those which are known. */
++ struct name_list *this_group = data.needed_groups->next;
++ if (this_group == data.needed_groups)
++ data.needed_groups = NULL;
++ else
++ data.needed_groups->next = this_group->next;
++ this_group->next = data.known_groups;
++ data.known_groups = this_group;
++
++ union
++ {
++ enum nss_status (*f) (const char *, struct __netgrent *);
++ void *ptr;
++ } setfct;
++
++ service_user *nip = netgroup_database;
++ int no_more = __nss_lookup (&nip, "setnetgrent", NULL, &setfct.ptr);
++ while (!no_more)
++ {
++ enum nss_status status
++ = DL_CALL_FCT (*setfct.f, (data.known_groups->name, &data));
++
++ if (status == NSS_STATUS_SUCCESS)
++ {
++ union
++ {
++ enum nss_status (*f) (struct __netgrent *, char *, size_t,
++ int *);
++ void *ptr;
++ } getfct;
++ getfct.ptr = __nss_lookup_function (nip, "getnetgrent_r");
++ if (getfct.f != NULL)
++ while (1)
++ {
++ int e;
++ status = getfct.f (&data, buffer + buffilled,
++ buflen - buffilled, &e);
++ if (status == NSS_STATUS_RETURN)
++ /* This was the last one for this group. Look
++ at next group if available. */
++ break;
++ if (status == NSS_STATUS_SUCCESS)
++ {
++ if (data.type == triple_val)
++ {
++ const char *nhost = data.val.triple.host;
++ const char *nuser = data.val.triple.user;
++ const char *ndomain = data.val.triple.domain;
++
++ if (data.val.triple.host > data.val.triple.user
++ || data.val.triple.user > data.val.triple.domain)
++ {
++ const char *last = MAX (nhost,
++ MAX (nuser, ndomain));
++ size_t bufused = (last + strlen (last) + 1
++ - buffer);
++
++ /* We have to make temporary copies. */
++ size_t hostlen = strlen (nhost) + 1;
++ size_t userlen = strlen (nuser) + 1;
++ size_t domainlen = strlen (ndomain) + 1;
++ size_t needed = hostlen + userlen + domainlen;
++
++ if (buflen - req->key_len - bufused < needed)
++ {
++ size_t newsize = MAX (2 * buflen,
++ buflen + 2 * needed);
++ if (use_malloc || newsize > 1024 * 1024)
++ {
++ buflen = newsize;
++ char *newbuf = xrealloc (use_malloc
++ ? buffer
++ : NULL,
++ buflen);
++
++ buffer = newbuf;
++ use_malloc = true;
++ }
++ else
++ extend_alloca (buffer, buflen, newsize);
++ }
++
++ nhost = memcpy (buffer + bufused,
++ nhost, hostlen);
++ nuser = memcpy ((char *) nhost + hostlen,
++ nuser, userlen);
++ ndomain = memcpy ((char *) nuser + userlen,
++ ndomain, domainlen);
++ }
++
++ char *wp = buffer + buffilled;
++ wp = stpcpy (wp, nhost) + 1;
++ wp = stpcpy (wp, nuser) + 1;
++ wp = stpcpy (wp, ndomain) + 1;
++ buffilled = wp - buffer;
++ ++nentries;
++ }
++ else
++ {
++ /* Check that the group has not been
++ requested before. */
++ struct name_list *runp = data.needed_groups;
++ if (runp != NULL)
++ while (1)
++ {
++ if (strcmp (runp->name, data.val.group) == 0)
++ break;
++
++ runp = runp->next;
++ if (runp == data.needed_groups)
++ {
++ runp = NULL;
++ break;
++ }
++ }
++
++ if (runp == NULL)
++ {
++ runp = data.known_groups;
++ while (runp != NULL)
++ if (strcmp (runp->name, data.val.group) == 0)
++ break;
++ else
++ runp = runp->next;
++ }
++
++ if (runp == NULL)
++ {
++ /* A new group is requested. */
++ size_t namelen = strlen (data.val.group) + 1;
++ struct name_list *newg = alloca (sizeof (*newg)
++ + namelen);
++ memcpy (newg->name, data.val.group, namelen);
++ if (data.needed_groups == NULL)
++ data.needed_groups = newg->next = newg;
++ else
++ {
++ newg->next = data.needed_groups->next;
++ data.needed_groups->next = newg;
++ data.needed_groups = newg;
++ }
++ }
++ }
++ }
++ else if (status == NSS_STATUS_UNAVAIL && e == ERANGE)
++ {
++ size_t newsize = 2 * buflen;
++ if (use_malloc || newsize > 1024 * 1024)
++ {
++ buflen = newsize;
++ char *newbuf = xrealloc (use_malloc
++ ? buffer : NULL, buflen);
++
++ buffer = newbuf;
++ use_malloc = true;
++ }
++ else
++ extend_alloca (buffer, buflen, newsize);
++ }
++ }
++
++ enum nss_status (*endfct) (struct __netgrent *);
++ endfct = __nss_lookup_function (nip, "endnetgrent");
++ if (endfct != NULL)
++ (void) DL_CALL_FCT (*endfct, (&data));
++
++ break;
++ }
++
++ no_more = __nss_next2 (&nip, "setnetgrent", NULL, &setfct.ptr,
++ status, 0);
++ }
++ }
++
++ total = buffilled;
++
++ /* Fill in the dataset. */
++ dataset = (struct dataset *) buffer;
++ dataset->head.allocsize = total + req->key_len;
++ dataset->head.recsize = total - offsetof (struct dataset, resp);
++ dataset->head.notfound = false;
++ dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
++ dataset->head.usable = true;
++ dataset->head.ttl = db->postimeout;
++ timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl;
++
++ dataset->resp.version = NSCD_VERSION;
++ dataset->resp.found = 1;
++ dataset->resp.nresults = nentries;
++ dataset->resp.result_len = buffilled - sizeof (*dataset);
++
++ assert (buflen - buffilled >= req->key_len);
++ key_copy = memcpy (buffer + buffilled, key, req->key_len);
++ buffilled += req->key_len;
++
++ /* Now we can determine whether on refill we have to create a new
++ record or not. */
++ if (he != NULL)
++ {
++ assert (fd == -1);
++
++ if (dataset->head.allocsize == dh->allocsize
++ && dataset->head.recsize == dh->recsize
++ && memcmp (&dataset->resp, dh->data,
++ dh->allocsize - offsetof (struct dataset, resp)) == 0)
++ {
++ /* The data has not changed. We will just bump the timeout
++ value. Note that the new record has been allocated on
++ the stack and need not be freed. */
++ dh->timeout = dataset->head.timeout;
++ dh->ttl = dataset->head.ttl;
++ ++dh->nreloads;
++ dataset = (struct dataset *) dh;
++
++ goto out;
++ }
++ }
++
++ {
++ struct dataset *newp
++ = (struct dataset *) mempool_alloc (db, total + req->key_len, 1);
++ if (__builtin_expect (newp != NULL, 1))
++ {
++ /* Adjust pointer into the memory block. */
++ key_copy = (char *) newp + (key_copy - buffer);
++
++ dataset = memcpy (newp, dataset, total + req->key_len);
++ cacheable = true;
++
++ if (he != NULL)
++ /* Mark the old record as obsolete. */
++ dh->usable = false;
++ }
++ }
++
++ if (he == NULL && fd != -1)
++ {
++ /* We write the dataset before inserting it to the database
++ since while inserting this thread might block and so would
++ unnecessarily let the receiver wait. */
++ writeout:
++#ifdef HAVE_SENDFILE
++ if (__builtin_expect (db->mmap_used, 1) && cacheable)
++ {
++ assert (db->wr_fd != -1);
++ assert ((char *) &dataset->resp > (char *) db->data);
++ assert ((char *) dataset - (char *) db->head + total
++ <= (sizeof (struct database_pers_head)
++ + db->head->module * sizeof (ref_t)
++ + db->head->data_size));
++# ifndef __ASSUME_SENDFILE
++ ssize_t written =
++# endif
++ sendfileall (fd, db->wr_fd, (char *) &dataset->resp
++ - (char *) db->head, dataset->head.recsize);
++# ifndef __ASSUME_SENDFILE
++ if (written == -1 && errno == ENOSYS)
++ goto use_write;
++# endif
++ }
++ else
++#endif
++ {
++#if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
++ use_write:
++#endif
++ writeall (fd, &dataset->resp, dataset->head.recsize);
++ }
++ }
++
++ if (cacheable)
++ {
++ /* If necessary, we also propagate the data to disk. */
++ if (db->persistent)
++ {
++ // XXX async OK?
++ uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
++ msync ((void *) pval,
++ ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
++ MS_ASYNC);
++ }
++
++ (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
++ true, db, uid, he == NULL);
++
++ pthread_rwlock_unlock (&db->lock);
++
++ /* Mark the old entry as obsolete. */
++ if (dh != NULL)
++ dh->usable = false;
++ }
++
++ out:
++ if (use_malloc)
++ free (buffer);
++
++ *resultp = dataset;
++
++ return timeout;
++}
++
++
++static time_t
++addinnetgrX (struct database_dyn *db, int fd, request_header *req,
++ char *key, uid_t uid, struct hashentry *he,
++ struct datahead *dh)
++{
++ const char *group = key;
++ key = (char *) rawmemchr (key, '\0') + 1;
++ size_t group_len = key - group - 1;
++ const char *host = *key++ ? key : NULL;
++ if (host != NULL)
++ key = (char *) rawmemchr (key, '\0') + 1;
++ const char *user = *key++ ? key : NULL;
++ if (user != NULL)
++ key = (char *) rawmemchr (key, '\0') + 1;
++ const char *domain = *key++ ? key : NULL;
++
++ if (__builtin_expect (debug_level > 0, 0))
++ {
++ if (he == NULL)
++ dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
++ group, host ?: "", user ?: "", domain ?: "");
++ else
++ dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
++ group, host ?: "", user ?: "", domain ?: "");
++ }
++
++ struct dataset *result = (struct dataset *) cache_search (GETNETGRENT,
++ group, group_len,
++ db, uid);
++ time_t timeout;
++ if (result != NULL)
++ timeout = result->head.timeout;
++ else
++ {
++ request_header req_get =
++ {
++ .type = GETNETGRENT,
++ .key_len = group_len
++ };
++ timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
++ &result);
++ }
++
++ struct indataset
++ {
++ struct datahead head;
++ innetgroup_response_header resp;
++ } *dataset
++ = (struct indataset *) mempool_alloc (db,
++ sizeof (*dataset) + req->key_len,
++ 1);
++ struct indataset dataset_mem;
++ bool cacheable = true;
++ if (__builtin_expect (dataset == NULL, 0))
++ {
++ cacheable = false;
++ dataset = &dataset_mem;
++ }
++
++ dataset->head.allocsize = sizeof (*dataset) + req->key_len;
++ dataset->head.recsize = sizeof (innetgroup_response_header);
++ dataset->head.notfound = result->head.notfound;
++ dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
++ dataset->head.usable = true;
++ dataset->head.ttl = result->head.ttl;
++ dataset->head.timeout = timeout;
++
++ dataset->resp.version = NSCD_VERSION;
++ dataset->resp.found = result->resp.found;
++ /* Until we find a matching entry the result is 0. */
++ dataset->resp.result = 0;
++
++ char *key_copy = memcpy ((char *) (dataset + 1), group, req->key_len);
++
++ if (dataset->resp.found)
++ {
++ const char *triplets = (const char *) (&result->resp + 1);
++
++ for (nscd_ssize_t i = result->resp.nresults; i > 0; --i)
++ {
++ bool success = true;
++
++ if (host != NULL)
++ success = strcmp (host, triplets) == 0;
++ triplets = (const char *) rawmemchr (triplets, '\0') + 1;
++
++ if (success && user != NULL)
++ success = strcmp (user, triplets) == 0;
++ triplets = (const char *) rawmemchr (triplets, '\0') + 1;
++
++ if (success && (domain == NULL || strcmp (domain, triplets) == 0))
++ {
++ dataset->resp.result = 1;
++ break;
++ }
++ triplets = (const char *) rawmemchr (triplets, '\0') + 1;
++ }
++ }
++
++ if (he != NULL && dh->data[0].innetgroupdata.result == dataset->resp.result)
++ {
++ /* The data has not changed. We will just bump the timeout
++ value. Note that the new record has been allocated on
++ the stack and need not be freed. */
++ dh->timeout = timeout;
++ dh->ttl = dataset->head.ttl;
++ ++dh->nreloads;
++ return timeout;
++ }
++
++ if (he == NULL)
++ {
++ /* We write the dataset before inserting it to the database
++ since while inserting this thread might block and so would
++ unnecessarily let the receiver wait. */
++ assert (fd != -1);
++
++#ifdef HAVE_SENDFILE
++ if (__builtin_expect (db->mmap_used, 1) && cacheable)
++ {
++ assert (db->wr_fd != -1);
++ assert ((char *) &dataset->resp > (char *) db->data);
++ assert ((char *) dataset - (char *) db->head + sizeof (*dataset)
++ <= (sizeof (struct database_pers_head)
++ + db->head->module * sizeof (ref_t)
++ + db->head->data_size));
++# ifndef __ASSUME_SENDFILE
++ ssize_t written =
++# endif
++ sendfileall (fd, db->wr_fd,
++ (char *) &dataset->resp - (char *) db->head,
++ sizeof (innetgroup_response_header));
++# ifndef __ASSUME_SENDFILE
++ if (written == -1 && errno == ENOSYS)
++ goto use_write;
++# endif
++ }
++ else
++# ifndef __ASSUME_SENDFILE
++ use_write:
++# endif
++#endif
++ writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
++ }
++
++ if (cacheable)
++ {
++ /* If necessary, we also propagate the data to disk. */
++ if (db->persistent)
++ {
++ // XXX async OK?
++ uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
++ msync ((void *) pval,
++ ((uintptr_t) dataset & pagesize_m1) + sizeof (*dataset)
++ + req->key_len,
++ MS_ASYNC);
++ }
++
++ (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
++ true, db, uid, he == NULL);
++
++ pthread_rwlock_unlock (&db->lock);
++
++ /* Mark the old entry as obsolete. */
++ if (dh != NULL)
++ dh->usable = false;
++ }
++
++ return timeout;
++}
++
++
++void
++addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
++ void *key, uid_t uid)
++{
++ struct dataset *ignore;
++
++ addgetnetgrentX (db, fd, req, key, uid, NULL, NULL, &ignore);
++}
++
++
++time_t
++readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
++ struct datahead *dh)
++{
++ request_header req =
++ {
++ .type = GETNETGRENT,
++ .key_len = he->len
++ };
++ struct dataset *ignore;
++
++ return addgetnetgrentX (db, -1, &req, db->data + he->key, he->owner, he, dh,
++ &ignore);
++}
++
++
++void
++addinnetgr (struct database_dyn *db, int fd, request_header *req,
++ void *key, uid_t uid)
++{
++ addinnetgrX (db, fd, req, key, uid, NULL, NULL);
++}
++
++
++time_t
++readdinnetgr (struct database_dyn *db, struct hashentry *he,
++ struct datahead *dh)
++{
++ request_header req =
++ {
++ .type = INNETGR,
++ .key_len = he->len
++ };
++
++ return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
++}
+diff -Nrup a/nscd/nscd-client.h b/nscd/nscd-client.h
+--- a/nscd/nscd-client.h 2012-08-06 15:07:49.082059915 -0600
++++ b/nscd/nscd-client.h 2012-08-06 15:08:19.090941456 -0600
+@@ -70,6 +70,9 @@ typedef enum
+ GETSERVBYNAME,
+ GETSERVBYPORT,
+ GETFDSERV,
++ GETNETGRENT,
++ INNETGR,
++ GETFDNETGR,
+ LASTREQ
+ } request_type;
+
+@@ -171,6 +174,24 @@ typedef struct
+ } serv_response_header;
+
+
++/* Structure send in reply to netgroup query. Note that this struct is
++ sent also if the service is disabled or there is no record found. */
++typedef struct
++{
++ int32_t version;
++ int32_t found;
++ nscd_ssize_t nresults;
++ nscd_ssize_t result_len;
++} netgroup_response_header;
++
++typedef struct
++{
++ int32_t version;
++ int32_t found;
++ int32_t result;
++} innetgroup_response_header;
++
++
+ /* Type for offsets in data part of database. */
+ typedef uint32_t ref_t;
+ /* Value for invalid/no reference. */
+@@ -210,6 +231,8 @@ struct datahead
+ ai_response_header aidata;
+ initgr_response_header initgrdata;
+ serv_response_header servdata;
++ netgroup_response_header netgroupdata;
++ innetgroup_response_header innetgroupdata;
+ nscd_ssize_t align1;
+ nscd_time_t align2;
+ } data[0];
+diff -Nrup a/nscd/nscd.conf b/nscd/nscd.conf
+--- a/nscd/nscd.conf 2012-08-06 15:07:48.553062002 -0600
++++ b/nscd/nscd.conf 2012-08-06 15:08:19.091941452 -0600
+@@ -77,3 +77,12 @@
+ persistent services yes
+ shared services yes
+ max-db-size services 33554432
++
++ enable-cache netgroup yes
++ positive-time-to-live netgroup 28800
++ negative-time-to-live netgroup 20
++ suggested-size netgroup 211
++ check-files netgroup yes
++ persistent netgroup yes
++ shared netgroup yes
++ max-db-size netgroup 33554432
+diff -Nrup a/nscd/nscd.h b/nscd/nscd.h
+--- a/nscd/nscd.h 2012-08-06 15:07:49.085059903 -0600
++++ b/nscd/nscd.h 2012-08-06 15:08:19.093941443 -0600
+@@ -38,6 +38,7 @@ typedef enum
+ grpdb,
+ hstdb,
+ servdb,
++ netgrdb,
+ lastdb
+ } dbtype;
+
+@@ -107,6 +108,7 @@ struct database_dyn
+ #define _PATH_NSCD_GROUP_DB "/var/db/nscd/group"
+ #define _PATH_NSCD_HOSTS_DB "/var/db/nscd/hosts"
+ #define _PATH_NSCD_SERVICES_DB "/var/db/nscd/services"
++#define _PATH_NSCD_NETGROUP_DB "/var/db/nscd/netgroup"
+
+ /* Path used when not using persistent storage. */
+ #define _PATH_NSCD_XYZ_DB_TMP "/var/run/nscd/dbXXXXXX"
+@@ -140,6 +142,7 @@ extern const struct iovec pwd_iov_disabl
+ extern const struct iovec grp_iov_disabled;
+ extern const struct iovec hst_iov_disabled;
+ extern const struct iovec serv_iov_disabled;
++extern const struct iovec netgroup_iov_disabled;
+
+
+ /* Initial number of threads to run. */
+@@ -185,6 +188,11 @@ extern gid_t old_gid;
+
+ /* Prototypes for global functions. */
+
++/* Wrapper functions with error checking for standard functions. */
++extern void *xmalloc (size_t n);
++extern void *xcalloc (size_t n, size_t s);
++extern void *xrealloc (void *o, size_t n);
++
+ /* nscd.c */
+ extern void termination_handler (int signum) __attribute__ ((__noreturn__));
+ extern int nscd_open_socket (void);
+@@ -203,8 +211,8 @@ extern void send_stats (int fd, struct d
+ extern int receive_print_stats (void) __attribute__ ((__noreturn__));
+
+ /* cache.c */
+-extern struct datahead *cache_search (request_type, void *key, size_t len,
+- struct database_dyn *table,
++extern struct datahead *cache_search (request_type, const void *key,
++ size_t len, struct database_dyn *table,
+ uid_t owner);
+ extern int cache_add (int type, const void *key, size_t len,
+ struct datahead *packet, bool first,
+@@ -273,6 +281,16 @@ extern void addservbyport (struct databa
+ extern time_t readdservbyport (struct database_dyn *db, struct hashentry *he,
+ struct datahead *dh);
+
++/* netgroupcache.c */
++extern void addinnetgr (struct database_dyn *db, int fd, request_header *req,
++ void *key, uid_t uid);
++extern time_t readdinnetgr (struct database_dyn *db, struct hashentry *he,
++ struct datahead *dh);
++extern void addgetnetgrent (struct database_dyn *db, int fd,
++ request_header *req, void *key, uid_t uid);
++extern time_t readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
++ struct datahead *dh);
++
+ /* mem.c */
+ extern void *mempool_alloc (struct database_dyn *db, size_t len,
+ int data_alloc);
+diff -Nrup a/nscd/nscd_conf.c b/nscd/nscd_conf.c
+--- a/nscd/nscd_conf.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nscd/nscd_conf.c 2012-08-06 15:08:19.093941443 -0600
+@@ -43,7 +43,8 @@ const char *const dbnames[lastdb] =
+ [pwddb] = "passwd",
+ [grpdb] = "group",
+ [hstdb] = "hosts",
+- [servdb] = "services"
++ [servdb] = "services",
++ [netgrdb] = "netgroup"
+ };
+
+
+diff -Nrup a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
+--- a/nscd/nscd_netgroup.c 1969-12-31 17:00:00.000000000 -0700
++++ b/nscd/nscd_netgroup.c 2012-08-06 15:08:19.094941439 -0600
+@@ -0,0 +1,290 @@
++/* Copyright (C) 2011 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++ Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, write to the Free
++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++ 02111-1307 USA. */
++
++#include <alloca.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++#include <not-cancel.h>
++
++#include "nscd-client.h"
++#include "nscd_proto.h"
++
++int __nss_not_use_nscd_netgroup;
++
++
++libc_locked_map_ptr (static, map_handle);
++/* Note that we only free the structure if necessary. The memory
++ mapping is not removed since it is not visible to the malloc
++ handling. */
++libc_freeres_fn (pw_map_free)
++{
++ if (map_handle.mapped != NO_MAPPING)
++ {
++ void *p = map_handle.mapped;
++ map_handle.mapped = NO_MAPPING;
++ free (p);
++ }
++}
++
++
++int
++__nscd_setnetgrent (const char *group, struct __netgrent *datap)
++{
++ int gc_cycle;
++ int nretries = 0;
++ size_t group_len = strlen (group);
++
++ /* If the mapping is available, try to search there instead of
++ communicating with the nscd. */
++ struct mapped_database *mapped;
++ mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
++
++ retry:;
++ char *respdata = NULL;
++ int retval = -1;
++ netgroup_response_header netgroup_resp;
++
++ if (mapped != NO_MAPPING)
++ {
++ struct datahead *found = __nscd_cache_search (GETNETGRENT, group,
++ group_len, mapped,
++ sizeof netgroup_resp);
++ if (found != NULL)
++ {
++ respdata = (char *) (&found->data[0].netgroupdata + 1);
++ netgroup_resp = found->data[0].netgroupdata;
++ /* Now check if we can trust pw_resp fields. If GC is
++ in progress, it can contain anything. */
++ if (mapped->head->gc_cycle != gc_cycle)
++ {
++ retval = -2;
++ goto out;
++ }
++ }
++ }
++
++ int sock = -1;
++ if (respdata == NULL)
++ {
++ sock = __nscd_open_socket (group, group_len, GETNETGRENT,
++ &netgroup_resp, sizeof (netgroup_resp));
++ if (sock == -1)
++ {
++ /* nscd not running or wrong version. */
++ __nss_not_use_nscd_netgroup = 1;
++ goto out;
++ }
++ }
++
++ if (netgroup_resp.found == 1)
++ {
++ size_t datalen = netgroup_resp.result_len;
++
++ /* If we do not have to read the data here it comes from the
++ mapped data and does not have to be freed. */
++ if (respdata == NULL)
++ {
++ /* The data will come via the socket. */
++ respdata = malloc (datalen);
++ if (respdata == NULL)
++ goto out_close;
++
++ if ((size_t) __readall (sock, respdata, datalen) != datalen)
++ {
++ free (respdata);
++ goto out_close;
++ }
++ }
++
++ datap->data = respdata;
++ datap->data_size = datalen;
++ datap->cursor = respdata;
++ datap->first = 1;
++ datap->nip = (service_user *) -1l;
++ datap->known_groups = NULL;
++ datap->needed_groups = NULL;
++
++ retval = 1;
++ }
++ else
++ {
++ if (__builtin_expect (netgroup_resp.found == -1, 0))
++ {
++ /* The daemon does not cache this database. */
++ __nss_not_use_nscd_netgroup = 1;
++ goto out_close;
++ }
++
++ /* Set errno to 0 to indicate no error, just no found record. */
++ __set_errno (0);
++ /* Even though we have not found anything, the result is zero. */
++ retval = 0;
++ }
++
++ out_close:
++ if (sock != -1)
++ close_not_cancel_no_status (sock);
++ out:
++ if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
++ {
++ /* When we come here this means there has been a GC cycle while we
++ were looking for the data. This means the data might have been
++ inconsistent. Retry if possible. */
++ if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
++ {
++ /* nscd is just running gc now. Disable using the mapping. */
++ if (atomic_decrement_val (&mapped->counter) == 0)
++ __nscd_unmap (mapped);
++ mapped = NO_MAPPING;
++ }
++
++ if (retval != -1)
++ goto retry;
++ }
++
++ return retval;
++}
++
++
++int
++__nscd_innetgr (const char *netgroup, const char *host, const char *user,
++ const char *domain)
++{
++ size_t key_len = (strlen (netgroup) + strlen (host ?: "")
++ + strlen (user ?: "") + strlen (domain ?: "") + 7);
++ char *key;
++ bool use_alloca = __libc_use_alloca (key_len);
++ if (use_alloca)
++ key = alloca (key_len);
++ else
++ {
++ key = malloc (key_len);
++ if (key == NULL)
++ return -1;
++ }
++ char *wp = stpcpy (key, netgroup) + 1;
++ if (host != NULL)
++ {
++ *wp++ = '\1';
++ wp = stpcpy (wp, host) + 1;
++ }
++ else
++ *wp++ = '\0';
++ if (user != NULL)
++ {
++ *wp++ = '\1';
++ wp = stpcpy (wp, user) + 1;
++ }
++ else
++ *wp++ = '\0';
++ if (domain != NULL)
++ {
++ *wp++ = '\1';
++ wp = stpcpy (wp, domain) + 1;
++ }
++ else
++ *wp++ = '\0';
++ key_len = wp - key;
++
++ /* If the mapping is available, try to search there instead of
++ communicating with the nscd. */
++ int gc_cycle;
++ int nretries = 0;
++ struct mapped_database *mapped;
++ mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
++
++ retry:;
++ int retval = -1;
++ innetgroup_response_header innetgroup_resp;
++ int sock = -1;
++
++ if (mapped != NO_MAPPING)
++ {
++ struct datahead *found = __nscd_cache_search (INNETGR, key,
++ key_len, mapped,
++ sizeof innetgroup_resp);
++ if (found != NULL)
++ {
++ innetgroup_resp = found->data[0].innetgroupdata;
++ /* Now check if we can trust pw_resp fields. If GC is
++ in progress, it can contain anything. */
++ if (mapped->head->gc_cycle != gc_cycle)
++ {
++ retval = -2;
++ goto out;
++ }
++
++ goto found_entry;
++ }
++ }
++
++ sock = __nscd_open_socket (key, key_len, INNETGR,
++ &innetgroup_resp, sizeof (innetgroup_resp));
++ if (sock == -1)
++ {
++ /* nscd not running or wrong version. */
++ __nss_not_use_nscd_netgroup = 1;
++ goto out;
++ }
++
++ found_entry:
++ if (innetgroup_resp.found == 1)
++ retval = innetgroup_resp.result;
++ else
++ {
++ if (__builtin_expect (innetgroup_resp.found == -1, 0))
++ {
++ /* The daemon does not cache this database. */
++ __nss_not_use_nscd_netgroup = 1;
++ goto out_close;
++ }
++
++ /* Set errno to 0 to indicate no error, just no found record. */
++ __set_errno (0);
++ /* Even though we have not found anything, the result is zero. */
++ retval = 0;
++ }
++
++ out_close:
++ if (sock != -1)
++ close_not_cancel_no_status (sock);
++ out:
++ if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
++ {
++ /* When we come here this means there has been a GC cycle while we
++ were looking for the data. This means the data might have been
++ inconsistent. Retry if possible. */
++ if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
++ {
++ /* nscd is just running gc now. Disable using the mapping. */
++ if (atomic_decrement_val (&mapped->counter) == 0)
++ __nscd_unmap (mapped);
++ mapped = NO_MAPPING;
++ }
++
++ if (retval != -1)
++ goto retry;
++ }
++
++ if (! use_alloca)
++ free (key);
++
++ return retval;
++}
+diff -Nrup a/nscd/nscd_proto.h b/nscd/nscd_proto.h
+--- a/nscd/nscd_proto.h 2010-05-04 05:27:23.000000000 -0600
++++ b/nscd/nscd_proto.h 2012-08-06 15:14:24.446648305 -0600
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1998-2000, 2002, 2004, 2007 Free Software Foundation, Inc.
++/* Copyright (C) 1998-2000,2002,2004,2007,2011 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
+
+@@ -36,6 +36,7 @@ extern int __nss_not_use_nscd_passwd att
+ extern int __nss_not_use_nscd_group attribute_hidden;
+ extern int __nss_not_use_nscd_hosts attribute_hidden;
+ extern int __nss_not_use_nscd_services attribute_hidden;
++extern int __nss_not_use_nscd_netgroup attribute_hidden;
+
+ extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
+ char *buffer, size_t buflen,
+@@ -71,5 +72,9 @@ extern int __nscd_getservbyname_r (const
+ extern int __nscd_getservbyport_r (int port, const char *proto,
+ struct servent *result_buf, char *buf,
+ size_t buflen, struct servent **result);
++extern int __nscd_innetgr (const char *netgroup, const char *host,
++ const char *user, const char *domain);
++extern int __nscd_setnetgrent (const char *group, struct __netgrent *datap);
++
+
+ #endif /* _NSCD_PROTO_H */
+diff -Nrup a/nscd/selinux.c b/nscd/selinux.c
+--- a/nscd/selinux.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nscd/selinux.c 2012-08-06 15:08:19.096941431 -0600
+@@ -1,5 +1,5 @@
+ /* SELinux access controls for nscd.
+- Copyright (C) 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
++ Copyright (C) 2004,2005,2006,2007,2009,2011 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
+
+@@ -46,7 +46,7 @@
+ int selinux_enabled;
+
+ /* Define mappings of access vector permissions to request types. */
+-static const int perms[LASTREQ] =
++static const access_vector_t perms[LASTREQ] =
+ {
+ [GETPWBYNAME] = NSCD__GETPWD,
+ [GETPWBYUID] = NSCD__GETPWD,
+@@ -69,6 +69,11 @@ static const int perms[LASTREQ] =
+ [GETSERVBYPORT] = NSCD__GETSERV,
+ [GETFDSERV] = NSCD__SHMEMSERV,
+ #endif
++#ifdef NSCD__GETNETGRP
++ [GETNETGRENT] = NSCD__GETNETGRP,
++ [INNETGR] = NSCD__GETNETGRP,
++ [GETFDNETGR] = NSCD__SHMEMNETGRP,
++#endif
+ };
+
+ /* Store an entry ref to speed AVC decisions. */
+diff -Nrup a/nss/Makefile b/nss/Makefile
+--- a/nss/Makefile 2012-08-06 15:07:48.938060482 -0600
++++ b/nss/Makefile 2012-08-23 14:19:49.382442094 -0600
+@@ -1,4 +1,5 @@
+-# Copyright (C) 1996-1998,2000-2002,2007,2009 Free Software Foundation, Inc.
++# Copyright (C) 1996-1998,2000-2002,2007,2009,2010
++# Free Software Foundation, Inc.
+ # This file is part of the GNU C Library.
+
+ # The GNU C Library is free software; you can redistribute it and/or
+@@ -39,7 +40,7 @@ databases = proto service hosts network
+ others := getent
+ install-bin := getent
+
+-tests = test-netdb
++tests = test-netdb tst-nss-test1
+ xtests = bug-erange
+
+ include ../Makeconfig
+@@ -85,3 +86,14 @@ endif
+ # a statically-linked program that hasn't already loaded it.
+ $(services:%=$(objpfx)libnss_%.so): $(common-objpfx)libc.so \
+ $(common-objpfx)libc_nonshared.a
++
++
++distribute += nss_test1.c
++
++CFLAGS-nss_test1.c = -DNOT_IN_libc=1
++$(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(common-objpfx)libc.so \
++ $(common-objpfx)libc_nonshared.a
++ $(build-module)
++$(objpfx)/libnss_test1.so$(libnss_test1.so-version): $(objpfx)/libnss_test1.so
++ $(make-link)
++$(objpfx)tst-nss-test1.out: $(objpfx)/libnss_test1.so$(libnss_test1.so-version)
+diff -Nrup a/nss/Versions b/nss/Versions
+--- a/nss/Versions 2012-08-06 15:07:48.939060479 -0600
++++ b/nss/Versions 2012-08-06 15:08:19.096941431 -0600
+@@ -12,7 +12,7 @@ libc {
+ __nss_disable_nscd; __nss_lookup_function; _nss_files_parse_sgent;
+
+ __nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
+- __nss_services_lookup2; __nss_next2;
++ __nss_services_lookup2; __nss_next2; __nss_lookup;
+ }
+ }
+
+diff -Nrup a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
+--- a/nss/getXXbyYY_r.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nss/getXXbyYY_r.c 2012-08-23 14:19:49.403442011 -0600
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1996-2004, 2006, 2007, 2009 Free Software Foundation, Inc.
++/* Copyright (C) 1996-2004,2006,2007,2009,2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+@@ -87,6 +87,8 @@
+ # define NOT_USENSCD_NAME ADD_NOT_NSCDUSE (DATABASE_NAME)
+ # define ADD_NOT_NSCDUSE(name) ADD_NOT_NSCDUSE1 (name)
+ # define ADD_NOT_NSCDUSE1(name) __nss_not_use_nscd_##name
++# define CONCAT2(arg1, arg2) CONCAT2_2 (arg1, arg2)
++# define CONCAT2_2(arg1, arg2) arg1##arg2
+ #endif
+
+ #define FUNCTION_NAME_STRING STRINGIZE (FUNCTION_NAME)
+@@ -186,7 +188,8 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, L
+ if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
+ NOT_USENSCD_NAME = 0;
+
+- if (!NOT_USENSCD_NAME)
++ if (!NOT_USENSCD_NAME
++ && !__nss_database_custom[CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)])
+ {
+ nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
+ H_ERRNO_VAR);
+diff -Nrup a/nss/getent.c b/nss/getent.c
+--- a/nss/getent.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nss/getent.c 2012-08-06 15:15:06.479427609 -0600
+@@ -466,7 +466,6 @@ static int
+ netgroup_keys (int number, char *key[])
+ {
+ int result = 0;
+- int i;
+
+ if (number == 0)
+ {
+@@ -474,18 +473,28 @@ netgroup_keys (int number, char *key[])
+ return 3;
+ }
+
+- for (i = 0; i < number; ++i)
++ if (number == 4)
+ {
+- if (!setnetgrent (key[i]))
++ char *host = strcmp (key[1], "*") == 0 ? NULL : key[1];
++ char *user = strcmp (key[2], "*") == 0 ? NULL : key[2];
++ char *domain = strcmp (key[3], "*") == 0 ? NULL : key[3];
++
++ printf ("%-21s (%s,%s,%s) = %d\n",
++ key[0], host ?: "", user ?: "", domain ?: "",
++ innetgr (key[0], host, user, domain));
++ }
++ else if (number == 1)
++ {
++ if (!setnetgrent (key[0]))
+ result = 2;
+ else
+ {
+ char *p[3];
+
+- printf ("%-21s", key[i]);
++ printf ("%-21s", key[0]);
+
+ while (getnetgrent (p, p + 1, p + 2))
+- printf (" (%s, %s, %s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
++ printf (" (%s,%s,%s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
+ putchar_unlocked ('\n');
+ }
+ }
+diff -Nrup a/nss/nss_files/files-parse.c b/nss/nss_files/files-parse.c
+--- a/nss/nss_files/files-parse.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nss/nss_files/files-parse.c 2012-08-23 14:19:49.418441951 -0600
+@@ -1,5 +1,5 @@
+ /* Common code for file-based database parsers in nss_files module.
+- Copyright (C) 1996-2000, 2003, 2004, 2009 Free Software Foundation, Inc.
++ Copyright (C) 1996-2000,2003,2004,2009,2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+@@ -29,7 +29,7 @@
+ DATABASE -- string of the database file's name ("hosts", "passwd").
+
+ ENTDATA -- if defined, `struct ENTDATA' is used by the parser to store
+- things pointed to by the resultant `struct STRUCTURE'.
++ things pointed to by the resultant `struct STRUCTURE'.
+
+ NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
+
+@@ -229,7 +229,7 @@ strtou32 (const char *nptr, char **endpt
+ char **list = parse_list (&line, buf_start, buf_end, '\0', errnop); \
+ if (list) \
+ result->TRAILING_LIST_MEMBER = list; \
+- else \
++ else \
+ return -1; /* -1 indicates we ran out of space. */ \
+ }
+
+diff -Nrup a/nss/nss_test1.c b/nss/nss_test1.c
+--- a/nss/nss_test1.c 1969-12-31 17:00:00.000000000 -0700
++++ b/nss/nss_test1.c 2012-08-23 14:19:49.418441951 -0600
+@@ -0,0 +1,154 @@
++#include <errno.h>
++#include <nss.h>
++#include <pthread.h>
++#include <string.h>
++
++
++#define COPY_IF_ROOM(s) \
++ ({ size_t len_ = strlen (s) + 1; \
++ char *start_ = cp; \
++ buflen - (cp - buffer) < len_ \
++ ? NULL \
++ : (cp = mempcpy (cp, s, len_), start_); })
++
++
++/* Password handling. */
++#include <pwd.h>
++
++static struct passwd pwd_data[] =
++ {
++#define PWD(u) \
++ { .pw_name = (char *) "name" #u, .pw_passwd = (char *) "*", .pw_uid = u, \
++ .pw_gid = 100, .pw_gecos = (char *) "*", .pw_dir = (char *) "*", \
++ .pw_shell = (char *) "*" }
++ PWD (100),
++ PWD (30),
++ PWD (200),
++ PWD (60),
++ PWD (20000)
++ };
++#define npwd_data (sizeof (pwd_data) / sizeof (pwd_data[0]))
++
++static size_t pwd_iter;
++#define CURPWD pwd_data[pwd_iter]
++
++static pthread_mutex_t pwd_lock = PTHREAD_MUTEX_INITIALIZER;
++
++
++enum nss_status
++_nss_test1_setpwent (int stayopen)
++{
++ pwd_iter = 0;
++ return NSS_STATUS_SUCCESS;
++}
++
++
++enum nss_status
++_nss_test1_endpwent (void)
++{
++ return NSS_STATUS_SUCCESS;
++}
++
++
++enum nss_status
++_nss_test1_getpwent_r (struct passwd *result, char *buffer, size_t buflen,
++ int *errnop)
++{
++ char *cp = buffer;
++ int res = NSS_STATUS_SUCCESS;
++
++ pthread_mutex_lock (&pwd_lock);
++
++ if (pwd_iter >= npwd_data)
++ res = NSS_STATUS_NOTFOUND;
++ else
++ {
++ result->pw_name = COPY_IF_ROOM (CURPWD.pw_name);
++ result->pw_passwd = COPY_IF_ROOM (CURPWD.pw_passwd);
++ result->pw_uid = CURPWD.pw_uid;
++ result->pw_gid = CURPWD.pw_gid;
++ result->pw_gecos = COPY_IF_ROOM (CURPWD.pw_gecos);
++ result->pw_dir = COPY_IF_ROOM (CURPWD.pw_dir);
++ result->pw_shell = COPY_IF_ROOM (CURPWD.pw_shell);
++
++ if (result->pw_name == NULL || result->pw_passwd == NULL
++ || result->pw_gecos == NULL || result->pw_dir == NULL
++ || result->pw_shell == NULL)
++ {
++ *errnop = ERANGE;
++ res = NSS_STATUS_TRYAGAIN;
++ }
++
++ ++pwd_iter;
++ }
++
++ pthread_mutex_unlock (&pwd_lock);
++
++ return res;
++}
++
++
++enum nss_status
++_nss_test1_getpwuid_r (uid_t uid, struct passwd *result, char *buffer,
++ size_t buflen, int *errnop)
++{
++ for (size_t idx = 0; idx < npwd_data; ++idx)
++ if (pwd_data[idx].pw_uid == uid)
++ {
++ char *cp = buffer;
++ int res = NSS_STATUS_SUCCESS;
++
++ result->pw_name = COPY_IF_ROOM (pwd_data[idx].pw_name);
++ result->pw_passwd = COPY_IF_ROOM (pwd_data[idx].pw_passwd);
++ result->pw_uid = pwd_data[idx].pw_uid;
++ result->pw_gid = pwd_data[idx].pw_gid;
++ result->pw_gecos = COPY_IF_ROOM (pwd_data[idx].pw_gecos);
++ result->pw_dir = COPY_IF_ROOM (pwd_data[idx].pw_dir);
++ result->pw_shell = COPY_IF_ROOM (pwd_data[idx].pw_shell);
++
++ if (result->pw_name == NULL || result->pw_passwd == NULL
++ || result->pw_gecos == NULL || result->pw_dir == NULL
++ || result->pw_shell == NULL)
++ {
++ *errnop = ERANGE;
++ res = NSS_STATUS_TRYAGAIN;
++ }
++
++ return res;
++ }
++
++ return NSS_STATUS_NOTFOUND;
++}
++
++
++enum nss_status
++_nss_test1_getpwnam_r (const char *name, struct passwd *result, char *buffer,
++ size_t buflen, int *errnop)
++{
++ for (size_t idx = 0; idx < npwd_data; ++idx)
++ if (strcmp (pwd_data[idx].pw_name, name) == 0)
++ {
++ char *cp = buffer;
++ int res = NSS_STATUS_SUCCESS;
++
++ result->pw_name = COPY_IF_ROOM (pwd_data[idx].pw_name);
++ result->pw_passwd = COPY_IF_ROOM (pwd_data[idx].pw_passwd);
++ result->pw_uid = pwd_data[idx].pw_uid;
++ result->pw_gid = pwd_data[idx].pw_gid;
++ result->pw_gecos = COPY_IF_ROOM (pwd_data[idx].pw_gecos);
++ result->pw_dir = COPY_IF_ROOM (pwd_data[idx].pw_dir);
++ result->pw_shell = COPY_IF_ROOM (pwd_data[idx].pw_shell);
++
++ if (result->pw_name == NULL || result->pw_passwd == NULL
++ || result->pw_gecos == NULL || result->pw_dir == NULL
++ || result->pw_shell == NULL)
++ {
++ *errnop = ERANGE;
++ res = NSS_STATUS_TRYAGAIN;
++ }
++
++ return res;
++ }
++
++ return NSS_STATUS_NOTFOUND;
++}
+diff -Nrup a/nss/nsswitch.c b/nss/nsswitch.c
+--- a/nss/nsswitch.c 2010-05-04 05:27:23.000000000 -0600
++++ b/nss/nsswitch.c 2012-08-23 14:19:49.430441903 -0600
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1996-1999, 2001-2007, 2009 Free Software Foundation, Inc.
++/* Copyright (C) 1996-1999,2001-2007,2009,2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+@@ -71,6 +71,9 @@ static const struct
+ };
+ #define ndatabases (sizeof (databases) / sizeof (databases[0]))
+
++/* Flags whether custom rules for database is set. */
++bool __nss_database_custom[NSS_DBSIDX_max];
++
+
+ __libc_lock_define_initialized (static, lock)
+
+@@ -165,6 +168,7 @@ __nss_lookup (service_user **ni, const c
+
+ return *fctp != NULL ? 0 : (*ni)->next == NULL ? 1 : -1;
+ }
++libc_hidden_def (__nss_lookup)
+
+
+ /* -1 == not found
+@@ -265,6 +269,7 @@ __nss_configure_lookup (const char *dbna
+
+ /* Install new rules. */
+ *databases[cnt].dbp = new_db;
++ __nss_database_custom[cnt] = true;
+
+ __libc_lock_unlock (lock);
+
+@@ -729,6 +734,7 @@ __nss_disable_nscd (void)
+ __nss_not_use_nscd_group = -1;
+ __nss_not_use_nscd_hosts = -1;
+ __nss_not_use_nscd_services = -1;
++ __nss_not_use_nscd_netgroup = -1;
+ }
+
+
+diff -Nrup a/nss/nsswitch.h b/nss/nsswitch.h
+--- a/nss/nsswitch.h 2010-05-04 05:27:23.000000000 -0600
++++ b/nss/nsswitch.h 2012-08-23 14:19:49.431441899 -0600
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1996-1999,2001,2002,2003,2004,2007
++/* Copyright (C) 1996-1999,2001,2002,2003,2004,2007,2010
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+@@ -28,6 +28,7 @@
+ #include <resolv.h>
+ #include <search.h>
+ #include <dlfcn.h>
++#include <stdbool.h>
+
+ /* Actions performed after lookup finished. */
+ typedef enum
+@@ -96,6 +97,19 @@ typedef struct name_database
+ } name_database;
+
+
++/* Indices into DATABASES in nsswitch.c and __NSS_DATABASE_CUSTOM. */
++enum
++ {
++#define DEFINE_DATABASE(arg) NSS_DBSIDX_##arg,
++#include "databases.def"
++#undef DEFINE_DATABASE
++ NSS_DBSIDX_max
++ };
++
++/* Flags whether custom rules for database is set. */
++extern bool __nss_database_custom[NSS_DBSIDX_max];
++
++
+ /* Interface functions for NSS. */
+
+ /* Get the data structure representing the specified database.
+@@ -111,7 +125,8 @@ libc_hidden_proto (__nss_database_lookup
+ position is remembered in NI. The function returns a value < 0 if
+ an error occurred or no such function exists. */
+ extern int __nss_lookup (service_user **ni, const char *fct_name,
+- const char *fct2_name, void **fctp) attribute_hidden;
++ const char *fct2_name, void **fctp);
++libc_hidden_proto (__nss_lookup)
+
+ /* Determine the next step in the lookup process according to the
+ result STATUS of the call to the last function returned by
+diff -Nrup a/nss/tst-nss-test1.c b/nss/tst-nss-test1.c
+--- a/nss/tst-nss-test1.c 1969-12-31 17:00:00.000000000 -0700
++++ b/nss/tst-nss-test1.c 2012-08-23 14:19:49.432441895 -0600
+@@ -0,0 +1,72 @@
++#include <nss.h>
++#include <pwd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++
++static int
++do_test (void)
++{
++ int retval = 0;
++
++ __nss_configure_lookup ("passwd", "test1");
++
++ static const unsigned int pwdids[] = { 100, 30, 200, 60, 20000 };
++#define npwdids (sizeof (pwdids) / sizeof (pwdids[0]))
++ setpwent ();
++
++ const unsigned int *np = pwdids;
++ for (struct passwd *p = getpwent (); p != NULL; ++np, p = getpwent ())
++ if (p->pw_uid != *np || strncmp (p->pw_name, "name", 4) != 0
++ || atol (p->pw_name + 4) != *np)
++ {
++ printf ("passwd entry %ju wrong (%s, %u)\n",
++ np - pwdids, p->pw_name, p->pw_uid);
++ retval = 1;
++ break;
++ }
++
++ endpwent ();
++
++ for (int i = npwdids - 1; i >= 0; --i)
++ {
++ char buf[30];
++ snprintf (buf, sizeof (buf), "name%u", pwdids[i]);
++
++ struct passwd *p = getpwnam (buf);
++ if (p == NULL || p->pw_uid != pwdids[i] || strcmp (buf, p->pw_name) != 0)
++ {
++ printf ("passwd entry \"%s\" wrong\n", buf);
++ retval = 1;
++ }
++
++ p = getpwuid (pwdids[i]);
++ if (p == NULL || p->pw_uid != pwdids[i] || strcmp (buf, p->pw_name) != 0)
++ {
++ printf ("passwd entry %u wrong\n", pwdids[i]);
++ retval = 1;
++ }
++
++ snprintf (buf, sizeof (buf), "name%u", pwdids[i] + 1);
++
++ p = getpwnam (buf);
++ if (p != NULL)
++ {
++ printf ("passwd entry \"%s\" wrong\n", buf);
++ retval = 1;
++ }
++
++ p = getpwuid (pwdids[i] + 1);
++ if (p != NULL)
++ {
++ printf ("passwd entry %u wrong\n", pwdids[i] + 1);
++ retval = 1;
++ }
++ }
++
++ return retval;
++}
++
++#define TEST_FUNCTION do_test ()
++#include "../test-skeleton.c"
+diff -Nrup a/shlib-versions b/shlib-versions
+--- a/shlib-versions 2010-05-04 05:27:23.000000000 -0600
++++ b/shlib-versions 2012-08-23 14:19:49.473441732 -0600
+@@ -114,6 +114,10 @@ alpha.*-.*-linux.* libresolv=2.1
+ .*-.*-.* libnss_ldap=2
+ .*-.*-.* libnss_hesiod=2
+
++# Tests for NSS. They must have the same NSS_SHLIB_REVISION number as
++# the rest.
++.*-.*-.* libnss_test1=2
++
+ # Version for libnsl with YP and NIS+ functions.
+ alpha.*-.*-linux.* libnsl=1.1
+ .*-.*-.* libnsl=1
+diff -Nrup a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
+--- a/sysdeps/posix/getaddrinfo.c 2012-08-06 15:07:49.571057983 -0600
++++ b/sysdeps/posix/getaddrinfo.c 2012-08-23 14:19:49.541441461 -0600
+@@ -669,7 +669,8 @@ gaih_inet (const char *name, const struc
+ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+ __nss_not_use_nscd_hosts = 0;
+
+- if (!__nss_not_use_nscd_hosts)
++ if (!__nss_not_use_nscd_hosts
++ && !__nss_database_custom[NSS_DBSIDX_hosts])
+ {
+ /* Try to use nscd. */
+ struct nscd_ai_result *air = NULL;