Fix broken up NIS groups for compat NSS module.
The check for the inclusion of a group in the result gave up too early in case of broken-up NIS groups. We now fall back automatically to the slow mode of using getgrent_r. As an optimization, if there is not blacklist we need not perform the check in the first place and therefore can just accept the results of the initgroups_dyn callback.
This commit is contained in:
parent
f6887a0d9a
commit
ccab6d8f73
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
|||||||
|
2009-06-20 Ulrich Drepper <drepper@redhat.com>
|
||||||
|
|
||||||
|
[BZ #10085]
|
||||||
|
* nis/nss_compat/compat-initgroups.c (nss_setgrent): New variable.
|
||||||
|
(nss_endgrent): New variable.
|
||||||
|
(struct ent_t): Add need_endgrent and skip_initgroups_dyn
|
||||||
|
fields. Change type of files to bool and adjust all users.
|
||||||
|
(init_nss_interface): Initialize nss_setgrent and nss_endgrent.
|
||||||
|
(internal_endgrent): Call nss_endgrent if necessary.
|
||||||
|
(add_group): New function. Broken out of...
|
||||||
|
(check_and_add_group): ...here.
|
||||||
|
(getgrent_next_nss): Remove test that any callback is available.
|
||||||
|
Use skip_initgroups_dyn to determine whether to use initgroups_dyn
|
||||||
|
callback. If there is no blacklist we can trust the results returned
|
||||||
|
by the initgroups_dyn callback. In case there is a callback and we
|
||||||
|
find a group entry for the group ID but it doesn't contain the
|
||||||
|
correct member, switch to the slow mode and use getgrent_r.
|
||||||
|
(internal_getgrent_r): When we see a +: entry, determine whether
|
||||||
|
there is any callback and which we can use the initgroups_dyn
|
||||||
|
callback.
|
||||||
|
|
||||||
2009-06-18 Ulrich Drepper <drepper@redhat.com>
|
2009-06-18 Ulrich Drepper <drepper@redhat.com>
|
||||||
|
|
||||||
* malloc/malloc.c (_int_malloc): Add some consistency checks.
|
* malloc/malloc.c (_int_malloc): Add some consistency checks.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 1998-2004, 2006, 2007 Free Software Foundation, Inc.
|
/* Copyright (C) 1998-2004, 2006, 2007, 2009 Free Software Foundation, Inc.
|
||||||
This file is part of the GNU C Library.
|
This file is part of the GNU C Library.
|
||||||
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
|
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
|
||||||
|
|
||||||
@ -43,8 +43,10 @@ static enum nss_status (*nss_getgrnam_r) (const char *name,
|
|||||||
static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp,
|
static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp,
|
||||||
char *buffer, size_t buflen,
|
char *buffer, size_t buflen,
|
||||||
int *errnop);
|
int *errnop);
|
||||||
|
static enum nss_status (*nss_setgrent) (int stayopen);
|
||||||
static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer,
|
static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer,
|
||||||
size_t buflen, int *errnop);
|
size_t buflen, int *errnop);
|
||||||
|
static enum nss_status (*nss_endgrent) (void);
|
||||||
|
|
||||||
/* Protect global state against multiple changers. */
|
/* Protect global state against multiple changers. */
|
||||||
__libc_lock_define_initialized (static, lock)
|
__libc_lock_define_initialized (static, lock)
|
||||||
@ -68,7 +70,9 @@ struct blacklist_t
|
|||||||
|
|
||||||
struct ent_t
|
struct ent_t
|
||||||
{
|
{
|
||||||
bool_t files;
|
bool files;
|
||||||
|
bool need_endgrent;
|
||||||
|
bool skip_initgroups_dyn;
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
struct blacklist_t blacklist;
|
struct blacklist_t blacklist;
|
||||||
};
|
};
|
||||||
@ -106,7 +110,9 @@ init_nss_interface (void)
|
|||||||
nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn");
|
nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn");
|
||||||
nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r");
|
nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r");
|
||||||
nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r");
|
nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r");
|
||||||
|
nss_setgrent = __nss_lookup_function (ni, "setgrent");
|
||||||
nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r");
|
nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r");
|
||||||
|
nss_endgrent = __nss_lookup_function (ni, "endgrent");
|
||||||
}
|
}
|
||||||
|
|
||||||
__libc_lock_unlock (lock);
|
__libc_lock_unlock (lock);
|
||||||
@ -117,7 +123,7 @@ internal_setgrent (ent_t *ent)
|
|||||||
{
|
{
|
||||||
enum nss_status status = NSS_STATUS_SUCCESS;
|
enum nss_status status = NSS_STATUS_SUCCESS;
|
||||||
|
|
||||||
ent->files = TRUE;
|
ent->files = true;
|
||||||
|
|
||||||
if (ni == NULL)
|
if (ni == NULL)
|
||||||
init_nss_interface ();
|
init_nss_interface ();
|
||||||
@ -195,54 +201,68 @@ internal_endgrent (ent_t *ent)
|
|||||||
else
|
else
|
||||||
ent->blacklist.current = 0;
|
ent->blacklist.current = 0;
|
||||||
|
|
||||||
|
if (ent->need_endgrent && nss_endgrent != NULL)
|
||||||
|
nss_endgrent ();
|
||||||
|
|
||||||
return NSS_STATUS_SUCCESS;
|
return NSS_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function checks, if the user is a member of this group and if
|
/* Add new group record. */
|
||||||
yes, add the group id to the list. */
|
|
||||||
static void
|
static void
|
||||||
|
add_group (long int *start, long int *size, gid_t **groupsp, long int limit,
|
||||||
|
gid_t gid)
|
||||||
|
{
|
||||||
|
gid_t *groups = *groupsp;
|
||||||
|
|
||||||
|
/* Matches user. Insert this group. */
|
||||||
|
if (__builtin_expect (*start == *size, 0))
|
||||||
|
{
|
||||||
|
/* Need a bigger buffer. */
|
||||||
|
gid_t *newgroups;
|
||||||
|
long int newsize;
|
||||||
|
|
||||||
|
if (limit > 0 && *size == limit)
|
||||||
|
/* We reached the maximum. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (limit <= 0)
|
||||||
|
newsize = 2 * *size;
|
||||||
|
else
|
||||||
|
newsize = MIN (limit, 2 * *size);
|
||||||
|
|
||||||
|
newgroups = realloc (groups, newsize * sizeof (*groups));
|
||||||
|
if (newgroups == NULL)
|
||||||
|
return;
|
||||||
|
*groupsp = groups = newgroups;
|
||||||
|
*size = newsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[*start] = gid;
|
||||||
|
*start += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function checks, if the user is a member of this group and if
|
||||||
|
yes, add the group id to the list. Return nonzero is we couldn't
|
||||||
|
handle the group because the user is not in the member list. */
|
||||||
|
static int
|
||||||
check_and_add_group (const char *user, gid_t group, long int *start,
|
check_and_add_group (const char *user, gid_t group, long int *start,
|
||||||
long int *size, gid_t **groupsp, long int limit,
|
long int *size, gid_t **groupsp, long int limit,
|
||||||
struct group *grp)
|
struct group *grp)
|
||||||
{
|
{
|
||||||
gid_t *groups = *groupsp;
|
|
||||||
char **member;
|
char **member;
|
||||||
|
|
||||||
/* Don't add main group to list of groups. */
|
/* Don't add main group to list of groups. */
|
||||||
if (grp->gr_gid == group)
|
if (grp->gr_gid == group)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
for (member = grp->gr_mem; *member != NULL; ++member)
|
for (member = grp->gr_mem; *member != NULL; ++member)
|
||||||
if (strcmp (*member, user) == 0)
|
if (strcmp (*member, user) == 0)
|
||||||
{
|
{
|
||||||
/* Matches user. Insert this group. */
|
add_group (start, size, groupsp, limit, grp->gr_gid);
|
||||||
if (*start == *size)
|
return 0;
|
||||||
{
|
|
||||||
/* Need a bigger buffer. */
|
|
||||||
gid_t *newgroups;
|
|
||||||
long int newsize;
|
|
||||||
|
|
||||||
if (limit > 0 && *size == limit)
|
|
||||||
/* We reached the maximum. */
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (limit <= 0)
|
|
||||||
newsize = 2 * *size;
|
|
||||||
else
|
|
||||||
newsize = MIN (limit, 2 * *size);
|
|
||||||
|
|
||||||
newgroups = realloc (groups, newsize * sizeof (*groups));
|
|
||||||
if (newgroups == NULL)
|
|
||||||
return;
|
|
||||||
*groupsp = groups = newgroups;
|
|
||||||
*size = newsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
groups[*start] = grp->gr_gid;
|
|
||||||
*start += 1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the next group from NSS (+ entry). If the NSS module supports
|
/* Get the next group from NSS (+ entry). If the NSS module supports
|
||||||
@ -255,15 +275,10 @@ getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
|
|||||||
enum nss_status status;
|
enum nss_status status;
|
||||||
struct group grpbuf;
|
struct group grpbuf;
|
||||||
|
|
||||||
/* if this module does not support getgrent_r and initgroups_dyn,
|
|
||||||
abort. We cannot find the needed group entries. */
|
|
||||||
if (nss_getgrent_r == NULL && nss_initgroups_dyn == NULL)
|
|
||||||
return NSS_STATUS_UNAVAIL;
|
|
||||||
|
|
||||||
/* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
|
/* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
|
||||||
If this function is not supported, step through the whole group
|
If this function is not supported, step through the whole group
|
||||||
database with getgrent_r. */
|
database with getgrent_r. */
|
||||||
if (nss_initgroups_dyn && nss_getgrgid_r)
|
if (! ent->skip_initgroups_dyn)
|
||||||
{
|
{
|
||||||
long int mystart = 0;
|
long int mystart = 0;
|
||||||
long int mysize = limit <= 0 ? *size : limit;
|
long int mysize = limit <= 0 ? *size : limit;
|
||||||
@ -282,39 +297,56 @@ getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
|
|||||||
if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
|
if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
|
||||||
limit, errnop) == NSS_STATUS_SUCCESS)
|
limit, errnop) == NSS_STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
/* A temporary buffer. We use the normal buffer, until we find
|
/* If there is no blacklist we can trust the underlying
|
||||||
an entry, for which this buffer is to small. In this case, we
|
initgroups implementation. */
|
||||||
overwrite the pointer with one to a bigger buffer. */
|
if (ent->blacklist.current <= 1)
|
||||||
char *tmpbuf = buffer;
|
for (int i = 0; i < mystart; i++)
|
||||||
size_t tmplen = buflen;
|
add_group (start, size, groupsp, limit, mygroups[i]);
|
||||||
int i;
|
else
|
||||||
|
|
||||||
for (i = 0; i < mystart; i++)
|
|
||||||
{
|
{
|
||||||
while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, tmpbuf,
|
/* A temporary buffer. We use the normal buffer, until we find
|
||||||
tmplen,
|
an entry, for which this buffer is to small. In this case, we
|
||||||
errnop)) == NSS_STATUS_TRYAGAIN
|
overwrite the pointer with one to a bigger buffer. */
|
||||||
&& *errnop == ERANGE)
|
char *tmpbuf = buffer;
|
||||||
if (tmpbuf == buffer)
|
size_t tmplen = buflen;
|
||||||
{
|
|
||||||
tmplen *= 2;
|
|
||||||
tmpbuf = __alloca (tmplen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
tmpbuf = extend_alloca (tmpbuf, tmplen, 2 * tmplen);
|
|
||||||
|
|
||||||
if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1))
|
for (int i = 0; i < mystart; i++)
|
||||||
{
|
{
|
||||||
if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0))
|
while ((status = nss_getgrgid_r (mygroups[i], &grpbuf,
|
||||||
{
|
tmpbuf, tmplen, errnop))
|
||||||
free (mygroups);
|
== NSS_STATUS_TRYAGAIN
|
||||||
return status;
|
&& *errnop == ERANGE)
|
||||||
}
|
if (tmpbuf == buffer)
|
||||||
|
{
|
||||||
|
tmplen *= 2;
|
||||||
|
tmpbuf = __alloca (tmplen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tmpbuf = extend_alloca (tmpbuf, tmplen, 2 * tmplen);
|
||||||
|
|
||||||
if (!in_blacklist (grpbuf.gr_name,
|
if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1))
|
||||||
strlen (grpbuf.gr_name), ent))
|
{
|
||||||
check_and_add_group (user, group, start, size, groupsp,
|
if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0))
|
||||||
limit, &grpbuf);
|
{
|
||||||
|
free (mygroups);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_blacklist (grpbuf.gr_name,
|
||||||
|
strlen (grpbuf.gr_name), ent)
|
||||||
|
&& check_and_add_group (user, group, start, size,
|
||||||
|
groupsp, limit, &grpbuf))
|
||||||
|
{
|
||||||
|
if (nss_setgrent != NULL)
|
||||||
|
{
|
||||||
|
nss_setgrent (1);
|
||||||
|
ent->need_endgrent = true;
|
||||||
|
}
|
||||||
|
ent->skip_initgroups_dyn = true;
|
||||||
|
|
||||||
|
goto iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,17 +359,21 @@ getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If we come here, the NSS module does not support initgroups_dyn
|
/* If we come here, the NSS module does not support initgroups_dyn
|
||||||
and we have to step through the whole list ourself. */
|
or we were confronted with a split group. In these cases we have
|
||||||
|
to step through the whole list ourself. */
|
||||||
|
iter:
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
|
if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
|
||||||
NSS_STATUS_SUCCESS)
|
NSS_STATUS_SUCCESS)
|
||||||
return status;
|
break;
|
||||||
}
|
}
|
||||||
while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));
|
while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));
|
||||||
|
|
||||||
check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
|
if (status == NSS_STATUS_SUCCESS)
|
||||||
return NSS_STATUS_SUCCESS;
|
check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum nss_status
|
static enum nss_status
|
||||||
@ -435,7 +471,21 @@ internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user,
|
|||||||
/* +:... */
|
/* +:... */
|
||||||
if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0')
|
if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0')
|
||||||
{
|
{
|
||||||
ent->files = FALSE;
|
/* If the selected module does not support getgrent_r or
|
||||||
|
initgroups_dyn, abort. We cannot find the needed group
|
||||||
|
entries. */
|
||||||
|
if (nss_getgrent_r == NULL && nss_initgroups_dyn == NULL)
|
||||||
|
return NSS_STATUS_UNAVAIL;
|
||||||
|
|
||||||
|
ent->files = false;
|
||||||
|
|
||||||
|
if (nss_initgroups_dyn == NULL && nss_setgrent != NULL)
|
||||||
|
{
|
||||||
|
nss_setgrent (1);
|
||||||
|
ent->need_endgrent = true;
|
||||||
|
}
|
||||||
|
ent->skip_initgroups_dyn = true;
|
||||||
|
|
||||||
return getgrent_next_nss (ent, buffer, buflen, user, group,
|
return getgrent_next_nss (ent, buffer, buflen, user, group,
|
||||||
start, size, groupsp, limit, errnop);
|
start, size, groupsp, limit, errnop);
|
||||||
}
|
}
|
||||||
@ -455,7 +505,7 @@ _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
|
|||||||
size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
|
size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
|
||||||
char *tmpbuf;
|
char *tmpbuf;
|
||||||
enum nss_status status;
|
enum nss_status status;
|
||||||
ent_t intern = { TRUE, NULL, {NULL, 0, 0} };
|
ent_t intern = { true, false, false, NULL, {NULL, 0, 0} };
|
||||||
|
|
||||||
status = internal_setgrent (&intern);
|
status = internal_setgrent (&intern);
|
||||||
if (status != NSS_STATUS_SUCCESS)
|
if (status != NSS_STATUS_SUCCESS)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user