--- /dev/null
+/*
+ * BSD 3-Clause License
+ *
+ * Copyright (c) 2007, Stefan Metzmacher <metze@samba.org>
+ * Copyright (c) 2009, Guenther Deschner <gd@samba.org>
+ * Copyright (c) 2014-2015, Michael Adam <obnox@samba.org>
+ * Copyright (c) 2015, Robin Hack <hack.robin@gmail.com>
+ * Copyright (c) 2013-2018, Andreas Schneider <asn@samba.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <grp.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "nss_utils.h"
+
+int nwrap_gr_copy_r(const struct group *src, struct group *dst,
+ char *buf, size_t buflen, struct group **dstp)
+{
+ char *p = NULL;
+ uintptr_t align = 0;
+ unsigned int gr_mem_cnt = 0;
+ unsigned i;
+ size_t total_len;
+ size_t gr_name_len = strlen(src->gr_name) + 1;
+ size_t gr_passwd_len = strlen(src->gr_passwd) + 1;
+ union {
+ char *ptr;
+ char **data;
+ } g_mem;
+
+ for (i = 0; src->gr_mem[i] != NULL; i++) {
+ gr_mem_cnt++;
+ }
+
+ /* Align the memory for storing pointers */
+ align = __alignof__(char *) - ((p - (char *)0) % __alignof__(char *));
+ total_len = align +
+ (1 + gr_mem_cnt) * sizeof(char *) +
+ gr_name_len + gr_passwd_len;
+
+ if (total_len > buflen) {
+ errno = ERANGE;
+ return -1;
+ }
+ buflen -= total_len;
+
+ /* gr_mem */
+ p = buf + align;
+ g_mem.ptr = p;
+ dst->gr_mem = g_mem.data;
+
+ /* gr_name */
+ p += (1 + gr_mem_cnt) * sizeof(char *);
+ dst->gr_name = p;
+
+ /* gr_passwd */
+ p += gr_name_len;
+ dst->gr_passwd = p;
+
+ /* gr_mem[x] */
+ p += gr_passwd_len;
+
+ /* gr_gid */
+ dst->gr_gid = src->gr_gid;
+
+ memcpy(dst->gr_name, src->gr_name, gr_name_len);
+
+ memcpy(dst->gr_passwd, src->gr_passwd, gr_passwd_len);
+
+ /* Set the terminating entry */
+ dst->gr_mem[gr_mem_cnt] = NULL;
+
+ /* Now add the group members content */
+ total_len = 0;
+ for (i = 0; i < gr_mem_cnt; i++) {
+ size_t len = strlen(src->gr_mem[i]) + 1;
+
+ dst->gr_mem[i] = p;
+ total_len += len;
+ p += len;
+ }
+
+ if (total_len > buflen) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ for (i = 0; i < gr_mem_cnt; i++) {
+ size_t len = strlen(src->gr_mem[i]) + 1;
+
+ memcpy(dst->gr_mem[i],
+ src->gr_mem[i],
+ len);
+ }
+
+ if (dstp != NULL) {
+ *dstp = dst;
+ }
+
+ return 0;
+}
#include <string.h>
#include <unistd.h>
#include <ctype.h>
+#include <limits.h>
#include <netinet/in.h>
#include <search.h>
#include <assert.h>
+#include "nss_utils.h"
/*
* Defining _POSIX_PTHREAD_SEMANTICS before including pwd.h and grp.h gives us
* the posix getpwnam_r(), getpwuid_r(), getgrnam_r and getgrgid_r calls on
#define NWRAP_INET_ADDRSTRLEN INET_ADDRSTRLEN
#endif
+#define MAX(a,b) ((a) < (b) ? (b) : (a))
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+
static bool nwrap_initialized = false;
static pthread_mutex_t nwrap_initialized_mutex = PTHREAD_MUTEX_INITIALIZER;
size_t buflen,
int *errnop);
typedef NSS_STATUS (*__nss_endpwent)(void);
-typedef NSS_STATUS (*__nss_initgroups)(const char *user,
+typedef NSS_STATUS (*__nss_initgroups_dyn)(const char *user,
gid_t group,
long int *start,
long int *size,
NWRAP_NSS_MODULE_SYMBOL_ENTRY(getpwent_r);
NWRAP_NSS_MODULE_SYMBOL_ENTRY(endpwent);
- NWRAP_NSS_MODULE_SYMBOL_ENTRY(initgroups);
+ NWRAP_NSS_MODULE_SYMBOL_ENTRY(initgroups_dyn);
NWRAP_NSS_MODULE_SYMBOL_ENTRY(getgrnam_r);
NWRAP_NSS_MODULE_SYMBOL_ENTRY(getgrgid_r);
NWRAP_NSS_MODULE_SYMBOL_ENTRY(setgrent);
struct passwd *pwdst, char *buf,
size_t buflen, struct passwd **pwdstp);
void (*nw_endpwent)(struct nwrap_backend *b);
- int (*nw_initgroups)(struct nwrap_backend *b,
- const char *user, gid_t group);
+ int (*nw_initgroups_dyn)(struct nwrap_backend *b,
+ const char *user,
+ gid_t group,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ long int limit,
+ int *errnop);
struct group * (*nw_getgrnam)(struct nwrap_backend *b,
const char *name);
int (*nw_getgrnam_r)(struct nwrap_backend *b,
struct passwd *pwdst, char *buf,
size_t buflen, struct passwd **pwdstp);
static void nwrap_files_endpwent(struct nwrap_backend *b);
-static int nwrap_files_initgroups(struct nwrap_backend *b,
- const char *user, gid_t group);
+static int nwrap_files_initgroups_dyn(struct nwrap_backend *b,
+ const char *user,
+ gid_t group,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ long int limit,
+ int *errnop);
static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
const char *name);
static int nwrap_files_getgrnam_r(struct nwrap_backend *b,
char *buf, size_t buflen, struct group **grdstp);
static void nwrap_module_setgrent(struct nwrap_backend *b);
static void nwrap_module_endgrent(struct nwrap_backend *b);
-static int nwrap_module_initgroups(struct nwrap_backend *b,
- const char *user, gid_t group);
+static int nwrap_module_initgroups_dyn(struct nwrap_backend *b,
+ const char *user,
+ gid_t group,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ long int limit,
+ int *errnop);
static struct hostent *nwrap_module_gethostbyaddr(struct nwrap_backend *b,
const void *addr,
socklen_t len, int type);
.nw_getpwent = nwrap_files_getpwent,
.nw_getpwent_r = nwrap_files_getpwent_r,
.nw_endpwent = nwrap_files_endpwent,
- .nw_initgroups = nwrap_files_initgroups,
+ .nw_initgroups_dyn = nwrap_files_initgroups_dyn,
.nw_getgrnam = nwrap_files_getgrnam,
.nw_getgrnam_r = nwrap_files_getgrnam_r,
.nw_getgrgid = nwrap_files_getgrgid,
.nw_getpwent = nwrap_module_getpwent,
.nw_getpwent_r = nwrap_module_getpwent_r,
.nw_endpwent = nwrap_module_endpwent,
- .nw_initgroups = nwrap_module_initgroups,
+ .nw_initgroups_dyn = nwrap_module_initgroups_dyn,
.nw_getgrnam = nwrap_module_getgrnam,
.nw_getgrnam_r = nwrap_module_getgrnam_r,
.nw_getgrgid = nwrap_module_getgrgid,
struct addrinfo **pai,
bool skip_canonname);
+#ifdef HAVE_GETGROUPLIST
+static int nwrap_getgrouplist(const char *user,
+ gid_t group,
+ long int *size,
+ gid_t **groupsp,
+ long int limit);
+#endif
+
/*
* VECTORS
*/
nwrap_nss_module_bind_symbol(setpwent);
nwrap_nss_module_bind_symbol(getpwent_r);
nwrap_nss_module_bind_symbol(endpwent);
- nwrap_nss_module_bind_symbol2(initgroups, initgroups_dyn);
+ nwrap_nss_module_bind_symbol(initgroups_dyn);
nwrap_nss_module_bind_symbol(getgrnam_r);
nwrap_nss_module_bind_symbol(getgrgid_r);
nwrap_nss_module_bind_symbol(setgrent);
nwrap_gr->idx = 0;
}
-static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
- char *buf, size_t buflen, struct group **dstp)
-{
- char *p = NULL;
- uintptr_t align = 0;
- unsigned int gr_mem_cnt = 0;
- unsigned i;
- size_t total_len;
- size_t gr_name_len = strlen(src->gr_name) + 1;
- size_t gr_passwd_len = strlen(src->gr_passwd) + 1;
- union {
- char *ptr;
- char **data;
- } g_mem;
-
- for (i = 0; src->gr_mem[i] != NULL; i++) {
- gr_mem_cnt++;
- }
-
- /* Align the memory for storing pointers */
- align = __alignof__(char *) - ((p - (char *)0) % __alignof__(char *));
- total_len = align +
- (1 + gr_mem_cnt) * sizeof(char *) +
- gr_name_len + gr_passwd_len;
-
- if (total_len > buflen) {
- errno = ERANGE;
- return -1;
- }
- buflen -= total_len;
-
- /* gr_mem */
- p = buf + align;
- g_mem.ptr = p;
- dst->gr_mem = g_mem.data;
-
- /* gr_name */
- p += (1 + gr_mem_cnt) * sizeof(char *);
- dst->gr_name = p;
-
- /* gr_passwd */
- p += gr_name_len;
- dst->gr_passwd = p;
-
- /* gr_mem[x] */
- p += gr_passwd_len;
-
- /* gr_gid */
- dst->gr_gid = src->gr_gid;
-
- memcpy(dst->gr_name, src->gr_name, gr_name_len);
-
- memcpy(dst->gr_passwd, src->gr_passwd, gr_passwd_len);
-
- /* Set the terminating entry */
- dst->gr_mem[gr_mem_cnt] = NULL;
-
- /* Now add the group members content */
- total_len = 0;
- for (i = 0; i < gr_mem_cnt; i++) {
- size_t len = strlen(src->gr_mem[i]) + 1;
-
- dst->gr_mem[i] = p;
- total_len += len;
- p += len;
- }
-
- if (total_len > buflen) {
- errno = ERANGE;
- return -1;
- }
-
- for (i = 0; i < gr_mem_cnt; i++) {
- size_t len = strlen(src->gr_mem[i]) + 1;
-
- memcpy(dst->gr_mem[i],
- src->gr_mem[i],
- len);
- }
-
- if (dstp != NULL) {
- *dstp = dst;
- }
-
- return 0;
-}
-
static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed)
{
struct nwrap_entlist *el;
#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
/* misc functions */
-static int nwrap_files_initgroups(struct nwrap_backend *b,
- const char *user,
- gid_t group)
+static int nwrap_files_initgroups_dyn(struct nwrap_backend *b,
+ const char *user,
+ gid_t group,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ long int limit,
+ int *errnop)
{
struct group *grp;
- gid_t *groups;
- int size = 1;
- int rc;
-
- groups = (gid_t *)malloc(size * sizeof(gid_t));
- if (groups == NULL) {
- NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
- errno = ENOMEM;
- return -1;
- }
- groups[0] = group;
+ int i = 0;
+ (void)errnop; /* unused */
nwrap_files_setgrent(b);
while ((grp = nwrap_files_getgrent(b)) != NULL) {
- int i = 0;
-
NWRAP_LOG(NWRAP_LOG_DEBUG,
"Inspecting %s for group membership",
grp->gr_name);
user,
grp->gr_name);
- groups = (gid_t *)realloc(groups,
- (size + 1) * sizeof(gid_t));
- if (groups == NULL) {
- NWRAP_LOG(NWRAP_LOG_ERROR,
- "Out of memory");
- errno = ENOMEM;
- return -1;
+ if (*start == *size) {
+ long int newsize;
+ gid_t *newgroups;
+
+ newsize = 2 * (*size);
+ if (limit > 0 && newsize > limit) {
+ newsize = MAX(limit, *size);
+ }
+ newgroups = (gid_t *) realloc((*groups),
+ newsize * sizeof(**groups));
+ if (!newgroups) {
+ errno = ENOMEM;
+ return -1;
+ }
+ *groups = newgroups;
+ *size = newsize;
}
-
- groups[size] = grp->gr_gid;
- size++;
+ (*groups)[*start] = grp->gr_gid;
+ (*start)++;
}
}
}
nwrap_files_endgrent(b);
-
- NWRAP_LOG(NWRAP_LOG_DEBUG,
- "%s is member of %d groups",
- user, size);
-
- /* This really only works if uid_wrapper is loaded */
- rc = setgroups(size, groups);
-
- free(groups);
-
- return rc;
+ return *start;
}
/* group functions */
b->symbols->_nss_endpwent.f();
}
-static int nwrap_module_initgroups(struct nwrap_backend *b,
- const char *user, gid_t group)
+static int nwrap_module_initgroups_dyn(struct nwrap_backend *b,
+ const char *user,
+ gid_t group,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ long int limit,
+ int *errnop)
{
- gid_t *groups;
- long int start;
- long int size;
-
- if (b->symbols->_nss_initgroups.f == NULL) {
+ if (b->symbols->_nss_initgroups_dyn.f == NULL) {
return NSS_STATUS_UNAVAIL;
}
- return b->symbols->_nss_initgroups.f(user,
+ return b->symbols->_nss_initgroups_dyn.f(user,
group,
- &start,
- &size,
- &groups,
- 0,
- &errno);
+ start,
+ size,
+ groups,
+ limit,
+ errnop);
}
static struct group *nwrap_module_getgrnam(struct nwrap_backend *b,
static int nwrap_initgroups(const char *user, gid_t group)
{
- size_t i;
+#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0
+ /* No extra groups allowed. */
+ return 0;
+#elif !defined(HAVE_GETGROUPLIST)
+ return 0;
+#else
+ long int size;
+ long int limit;
+ gid_t *groups;
+ int ngroups;
+ int result;
+ const char *env = getenv("UID_WRAPPER");
- for (i=0; i < nwrap_main_global->num_backends; i++) {
- struct nwrap_backend *b = &nwrap_main_global->backends[i];
- int rc;
+ if (env == NULL || env[0] != '1') {
+ NWRAP_LOG(NWRAP_LOG_WARN,
+ "initgroups() requires uid_wrapper to work!");
+ return 0;
+ }
- rc = b->ops->nw_initgroups(b, user, group);
- if (rc == 0) {
- return 0;
- }
+ limit = sysconf(_SC_NGROUPS_MAX);
+ if (limit > 0) {
+ size = MIN(limit, 64);
+ } else {
+ size = 16;
}
- errno = ENOENT;
- return -1;
+ groups = (gid_t *)malloc(size * sizeof(gid_t));
+ if (groups == NULL) {
+ /* No more memory. */
+ return -1;
+ }
+
+ ngroups = nwrap_getgrouplist(user, group, &size, &groups, limit);
+
+ /* Try to set the maximum number of groups the kernel can handle. */
+ do {
+ result = setgroups(ngroups, groups);
+ } while (result == -1 && errno == EINVAL && --ngroups > 0);
+
+ free(groups);
+
+ return result;
+#endif
}
int initgroups(const char *user, gid_t group)
***************************************************************************/
#ifdef HAVE_GETGROUPLIST
-static int nwrap_getgrouplist(const char *user, gid_t group,
- gid_t *groups, int *ngroups)
-{
- struct group *grp;
- gid_t *groups_tmp;
- int count = 1;
-
- NWRAP_LOG(NWRAP_LOG_DEBUG, "getgrouplist called for %s", user);
-
- groups_tmp = (gid_t *)malloc(count * sizeof(gid_t));
- if (!groups_tmp) {
- NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
- errno = ENOMEM;
- return -1;
- }
- groups_tmp[0] = group;
-
- nwrap_setgrent();
- while ((grp = nwrap_getgrent()) != NULL) {
- int i = 0;
-
- NWRAP_LOG(NWRAP_LOG_DEBUG,
- "Inspecting %s for group membership",
- grp->gr_name);
-
- for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
-
- if (group != grp->gr_gid &&
- (strcmp(user, grp->gr_mem[i]) == 0)) {
+static int nwrap_getgrouplist(const char *user,
+ gid_t group,
+ long int *size,
+ gid_t **groupsp,
+ long int limit)
+{
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+ /* Start is one, because we have the first group as parameter. */
+ long int start = 1;
+ size_t i;
- NWRAP_LOG(NWRAP_LOG_DEBUG,
- "%s is member of %s",
- user,
- grp->gr_name);
+ /* Never store more than the starting *SIZE number of elements. */
+ assert(*size > 0);
+ (*groupsp)[0] = group;
- groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t));
- if (!groups_tmp) {
- NWRAP_LOG(NWRAP_LOG_ERROR,
- "Out of memory");
- errno = ENOMEM;
- return -1;
- }
- groups_tmp[count] = grp->gr_gid;
+ for (i = 0; i < nwrap_main_global->num_backends; i++) {
+ struct nwrap_backend *b = &nwrap_main_global->backends[i];
+ long int prev_start = start;
+ long int cnt = prev_start;
+
+ status = b->ops->nw_initgroups_dyn(b,
+ user,
+ group,
+ &start,
+ size,
+ groupsp,
+ limit,
+ &errno);
+
+ /* Remove duplicates. */
+ while (cnt < start) {
+ long int inner;
+ for (inner = 0; inner < prev_start; ++inner)
+ if ((*groupsp)[inner] == (*groupsp)[cnt])
+ break;
- count++;
- }
+ if (inner < prev_start)
+ (*groupsp)[cnt] = (*groupsp)[--start];
+ else
+ ++cnt;
}
+ NWRAP_LOG(NWRAP_LOG_DEBUG,
+ "Resource '%s' returned status=%d and increased "
+ "count of groups to %ld",
+ b->name,
+ status,
+ start);
}
+ return start;
+}
- nwrap_endgrent();
+int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
+{
+ long int size;
+ int total, retval;
+ gid_t *newgroups;
- NWRAP_LOG(NWRAP_LOG_DEBUG,
- "%s is member of %d groups",
- user, *ngroups);
+ if (!nss_wrapper_enabled()) {
+ return libc_getgrouplist(user, group, groups, ngroups);
+ }
- if (*ngroups < count) {
- *ngroups = count;
- free(groups_tmp);
+ size = MAX(1, *ngroups);
+ newgroups = (gid_t *)malloc(size * sizeof(gid_t));
+ if (newgroups == NULL) {
return -1;
}
- *ngroups = count;
- memcpy(groups, groups_tmp, count * sizeof(gid_t));
- free(groups_tmp);
+ total = nwrap_getgrouplist(user, group, &size, &newgroups, -1);
- return count;
-}
-
-int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
-{
- if (!nss_wrapper_enabled()) {
- return libc_getgrouplist(user, group, groups, ngroups);
+ if (groups != NULL) {
+ memcpy(groups, newgroups, MIN(*ngroups, total) * sizeof(gid_t));
}
- return nwrap_getgrouplist(user, group, groups, ngroups);
+ free(newgroups);
+
+ retval = total > *ngroups ? -1 : total;
+ *ngroups = total;
+
+ return retval;
}
#endif
/* libc */
if (m->libc != NULL) {
- if (m->libc->handle != NULL) {
+ if (m->libc->handle != NULL
+#ifdef RTLD_NEXT
+ && m->libc->handle != RTLD_NEXT
+#endif
+ ) {
dlclose(m->libc->handle);
}
- if (m->libc->nsl_handle != NULL) {
+ if (m->libc->nsl_handle != NULL
+#ifdef RTLD_NEXT
+ && m->libc->nsl_handle != RTLD_NEXT
+#endif
+ ) {
dlclose(m->libc->nsl_handle);
}
- if (m->libc->sock_handle != NULL) {
+ if (m->libc->sock_handle != NULL
+#ifdef RTLD_NEXT
+ && m->libc->sock_handle != RTLD_NEXT
+#endif
+ ) {
dlclose(m->libc->sock_handle);
}
SAFE_FREE(m->libc);