summaryrefslogtreecommitdiff
path: root/nscd
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@gmail.com>2011-10-07 10:06:31 -0400
committerUlrich Drepper <drepper@gmail.com>2011-10-07 10:06:31 -0400
commit684ae515993269277448150a1ca70db3b94aa5bd (patch)
tree493609057fdfc4aa2dd41f8d6e0c2047616b5d9c /nscd
parent21fd49a9ef9c21cd2c87d0facf9f53bedb5cea20 (diff)
Implement caching of nscd
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile6
-rw-r--r--nscd/cache.c6
-rw-r--r--nscd/connections.c46
-rw-r--r--nscd/netgroupcache.c669
-rw-r--r--nscd/nscd-client.h23
-rw-r--r--nscd/nscd.conf9
-rw-r--r--nscd/nscd.h22
-rw-r--r--nscd/nscd_conf.c3
-rw-r--r--nscd/nscd_netgroup.c290
-rw-r--r--nscd/nscd_proto.h5
-rw-r--r--nscd/selinux.c9
11 files changed, 1070 insertions, 18 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
index 097e6f0f43..be0afed2d8 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -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 pwdcache getpwnam_r getpwuid_r grpcache \
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)
@@ -121,6 +122,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))
LDFLAGS-nscd = -Wl,-z,now
diff --git a/nscd/cache.c b/nscd/cache.c
index 58f0bcc5f1..507ca78464 100644
--- a/nscd/cache.c
+++ b/nscd/cache.c
@@ -60,7 +60,9 @@ static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
[GETAI] = readdhstai,
[INITGROUPS] = readdinitgroups,
[GETSERVBYNAME] = readdservbyname,
- [GETSERVBYPORT] = readdservbyport
+ [GETSERVBYPORT] = readdservbyport,
+ [GETNETGRENT] = readdgetnetgrent,
+ [INNETGR] = readdinnetgr
};
@@ -70,7 +72,7 @@ static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
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 --git a/nscd/connections.c b/nscd/connections.c
index 7d3ff2e43b..2b5c7ef53c 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -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. */
@@ -181,6 +179,25 @@ 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,
+ .db_filename = _PATH_NSCD_NETGROUP_DB,
+ .disabled_iov = &netgroup_iov_disabled,
+ .postimeout = 28800,
+ .negtimeout = 20,
+ .wr_fd = -1,
+ .ro_fd = -1,
+ .mmap_used = false
}
};
@@ -210,7 +227,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] }
};
@@ -355,7 +375,8 @@ check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
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);
@@ -1230,6 +1251,14 @@ request from '%s' [%ld] not handled due to missing permission"),
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:
@@ -1276,6 +1305,7 @@ request from '%s' [%ld] not handled due to missing permission"),
case GETFDGR:
case GETFDHST:
case GETFDSERV:
+ case GETFDNETGR:
#ifdef SCM_RIGHTS
send_ro_fd (reqinfo[req->type].db, key, fd);
#endif
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
new file mode 100644
index 0000000000..a406ade995
--- /dev/null
+++ b/nscd/netgroupcache.c
@@ -0,0 +1,669 @@
+/* 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, &notfound, 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, &notfound, 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
+ {
+# ifndef __ASSUME_SENDFILE
+ use_write:
+# endif
+#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 --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index 482b052d16..caad26a9cc 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -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 --git a/nscd/nscd.conf b/nscd/nscd.conf
index ada88e6a3d..39b875912d 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -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 --git a/nscd/nscd.h b/nscd/nscd.h
index c15e88bb6f..fdaf01bfbe 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -38,6 +38,7 @@ typedef enum
grpdb,
hstdb,
servdb,
+ netgrdb,
lastdb
} dbtype;
@@ -116,6 +117,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"
@@ -149,6 +151,7 @@ extern const struct iovec pwd_iov_disabled;
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. */
@@ -197,6 +200,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);
@@ -216,8 +224,8 @@ extern void send_stats (int fd, struct database_dyn dbs[lastdb]);
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,
@@ -286,6 +294,16 @@ extern void addservbyport (struct database_dyn *db, int fd,
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 --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index 3b6cbb0b15..98b59280c6 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -43,7 +43,8 @@ const char *const dbnames[lastdb] =
[pwddb] = "passwd",
[grpdb] = "group",
[hstdb] = "hosts",
- [servdb] = "services"
+ [servdb] = "services",
+ [netgrdb] = "netgroup"
};
diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
new file mode 100644
index 0000000000..8457cec9d3
--- /dev/null
+++ b/nscd/nscd_netgroup.c
@@ -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 --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
index 573ca2b2e7..8aa29732ef 100644
--- a/nscd/nscd_proto.h
+++ b/nscd/nscd_proto.h
@@ -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 attribute_hidden;
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,7 @@ extern int __nscd_getservbyname_r (const char *name, const char *proto,
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);
#endif /* _NSCD_PROTO_H */
diff --git a/nscd/selinux.c b/nscd/selinux.c
index e07a454bf8..f618640dd6 100644
--- a/nscd/selinux.c
+++ b/nscd/selinux.c
@@ -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. */