Implement caching of nscd

This commit is contained in:
Ulrich Drepper 2011-10-07 10:06:31 -04:00
parent 21fd49a9ef
commit 684ae51599
19 changed files with 1196 additions and 30 deletions

View File

@ -1,5 +1,45 @@
2011-10-07 Ulrich Drepper <drepper@gmail.com>
* inet/getnetgrent_r.c: Hook up nscd.
* nscd/Makefile (routines): Add nscd_netgroup.
(nscd-modules): Add netgroupcache.
(CFLAGS-netgroupcache.c): Define.
* nscd/cache.c (readdfcts): Add entries for GETNETGRENT and INNETGR.
(cache_search): Add const to second parameter.
* nscd/connections.c (serv2str): Add entries for GETNETGRENT and
INNETGR.
(dbs): Add netgrdb entry.
(reqinfo): Add entries for GETNETGRENT, INNETGR, and GETFDNETGR.
(verify_persistent_db): Handle netgrdb.
(handle_request): Handle GETNETGRENT, INNETGR, and GETFDNETGR.
* nscd/nscd-client.h (request_type): Add GETNETGRENT, INNETGR, and
GETFDNETGR.
(netgroup_response_header): Define.
(innetgroup_response_header): Define.
(datahead): Add netgroup_response_header and innetgroup_response_header
elements.
* nscd/nscd.conf: Add entries for netgroup cache.
* nscd/nscd.h (dbtype): Add netgrdb.
(_PATH_NSCD_NETGROUP_DB): Define.
(netgroup_iov_disabled): Declare.
(xmalloc, xcalloc, xrealloc): Move declarations here.
(cache_search): Adjust prototype.
Add netgroup-related prototypes.
* nscd/nscd_conf.c (dbnames): Add netgrdb entry.
* nscd/nscd_proto.h (__nss_not_use_nscd_netgroup): Declare.
(__nscd_innetgr): Declare.
* nscd/selinux.c (perms): Use access_vector_t as element type and
add netgroup-related initializers.
* nscd/netgroupcache.c: New file.
* nscd/nscd_netgroup.c: New file.
* nss/Versions [libc] (GLIBC_PRIVATE): Export __nss_lookup.
* nss/getent.c (netgroup_keys): Use setnetgrent only for one parameter.
For four parameters use innetgr.
* nss/nss_files/files-init.c: Add definition and callback for netgr.
* nss/nsswitch.c (__nss_lookup): Add libc_hidden_def.
(__nss_disable_nscd): Set __nss_not_use_nscd_netgroup.
* nss/nsswitch.h (__nss_lookup): Add libc_hidden_proto.
* nscd/connections.c (register_traced_file): Don't register file
for disabled databases.

5
NEWS
View File

@ -1,4 +1,4 @@
GNU C Library NEWS -- history of user-visible changes. 2011-9-11
GNU C Library NEWS -- history of user-visible changes. 2011-10-7
Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc.
See the end for copying conditions.
@ -38,6 +38,9 @@ Version 2.15
* Checking versions of FD_SET, FD_CLR, and FD_ISSET added.
Implemented by Ulrich Drepper.
* nscd now also caches the netgroup database.
Implemented by Ulrich Drepper.
Version 2.14

View File

@ -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 *datap)
{
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 **userp, char **domainp,
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, char **userp, char **domainp,
/* 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 = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
@ -342,6 +385,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
{
enum nss_status (*f) (const char *, struct __netgrent *);
@ -453,7 +508,7 @@ innetgr (const char *netgroup, const char *host, const char *user,
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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

669
nscd/netgroupcache.c Normal file
View File

@ -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);
}

View File

@ -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];

View File

@ -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

View File

@ -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);

View File

@ -43,7 +43,8 @@ const char *const dbnames[lastdb] =
[pwddb] = "passwd",
[grpdb] = "group",
[hstdb] = "hosts",
[servdb] = "services"
[servdb] = "services",
[netgrdb] = "netgroup"
};

290
nscd/nscd_netgroup.c Normal file
View File

@ -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;
}

View File

@ -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 */

View File

@ -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. */

View File

@ -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;
}
}

View File

@ -480,18 +480,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');
}
}

View File

@ -38,6 +38,7 @@ TF (grp, "/etc/group");
TF (hst, "/etc/hosts");
TF (resolv, "/etc/resolv.conf", .call_res_init = 1);
TF (serv, "/etc/services");
TF (netgr, "/etc/netgroup");
void
@ -52,4 +53,6 @@ _nss_files_init (void (*cb) (size_t, struct traced_file *))
cb (hstdb, &resolv_traced_file.file);
cb (servdb, &serv_traced_file.file);
cb (netgrdb, &netgr_traced_file.file);
}

View File

@ -176,6 +176,7 @@ __nss_lookup (service_user **ni, const char *fct_name, const char *fct2_name,
return *fctp != NULL ? 0 : (*ni)->next == NULL ? 1 : -1;
}
libc_hidden_def (__nss_lookup)
/* -1 == not found
@ -812,6 +813,7 @@ __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
__nss_not_use_nscd_group = -1;
__nss_not_use_nscd_hosts = -1;
__nss_not_use_nscd_services = -1;
__nss_not_use_nscd_netgroup = -1;
}
#endif

View File

@ -125,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