mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 20:40:05 +00:00
Implement caching of nscd
This commit is contained in:
parent
21fd49a9ef
commit
684ae51599
40
ChangeLog
40
ChangeLog
@ -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
5
NEWS
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
669
nscd/netgroupcache.c
Normal 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, ¬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
|
||||
{
|
||||
# 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);
|
||||
}
|
@ -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];
|
||||
|
@ -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
|
||||
|
22
nscd/nscd.h
22
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);
|
||||
|
@ -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
290
nscd/nscd_netgroup.c
Normal 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;
|
||||
}
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
18
nss/getent.c
18
nss/getent.c
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user