summaryrefslogtreecommitdiff
path: root/source4/lib
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib')
-rw-r--r--source4/lib/ldb/Makefile.in13
-rw-r--r--source4/lib/ldb/nssldb/ldb-grp.c427
-rw-r--r--source4/lib/ldb/nssldb/ldb-nss.c401
-rw-r--r--source4/lib/ldb/nssldb/ldb-nss.h86
-rw-r--r--source4/lib/ldb/nssldb/ldb-pwd.c241
-rw-r--r--source4/lib/ldb/nssldb/nsstest.c433
6 files changed, 1598 insertions, 3 deletions
diff --git a/source4/lib/ldb/Makefile.in b/source4/lib/ldb/Makefile.in
index ce23f309d3..c90f41afd7 100644
--- a/source4/lib/ldb/Makefile.in
+++ b/source4/lib/ldb/Makefile.in
@@ -18,7 +18,7 @@ SLAPD = @SLAPD@
EXTRA_OBJ=@EXTRA_OBJ@
TESTS=test-tdb.sh @TESTS@
-CFLAGS=-I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
+CFLAGS=-g -I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
@POPT_CFLAGS@ -I@tallocdir@ -I@tdbdir@/include -I@libreplacedir@ \
-DLIBDIR=\"$(libdir)\" -DSHLIBEXT=\"@SHLIBEXT@\" -DUSE_MMAP=1 @CFLAGS@
@@ -41,6 +41,10 @@ MODULES_OBJ=$(MODDIR)/operational.o $(MODDIR)/schema.o $(MODDIR)/rdn_name.o \
$(MODDIR)/objectclass.o \
$(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o
+NSSDIR=nssldb
+NSS_OBJ= $(NSSDIR)/ldb-nss.o $(NSSDIR)/ldb-pwd.o $(NSSDIR)/ldb-grp.o
+NSS_LIB = lib/libnss_ldb.so.2
+
OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) @TDBOBJ@ @TALLOCOBJ@ @POPTOBJ@ @LIBREPLACEOBJ@ $(EXTRA_OBJ)
LDB_LIB = lib/libldb.a
@@ -53,7 +57,7 @@ EXAMPLES = examples/ldbreader examples/ldifreader
DIRS = lib bin common ldb_tdb ldb_ldap ldb_sqlite3 modules tools examples
-all: showflags dirs $(OBJS) $(LDB_LIB) $(BINS) $(EXAMPLES) manpages
+all: showflags dirs $(OBJS) $(LDB_LIB) $(BINS) $(EXAMPLES) manpages $(NSS_LIB)
showflags:
@echo 'ldb will be compiled with flags:'
@@ -72,6 +76,9 @@ lib/libldb.a: $(OBJS)
ar -rv $@ $(OBJS)
@-ranlib $@
+lib/libnss_ldb.so.2: $(NSS_OBJ) $(LIBS)
+ $(CC) -shared -Wl,-soname,libnss_ldb.so.2 -o lib/libnss_ldb.so.2 $(NSS_OBJ) $(OBJS) $(LIB_FLAGS)
+
bin/ldbadd: tools/ldbadd.o tools/cmdline.o $(LIBS)
$(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS)
@@ -112,7 +119,7 @@ doxygen:
clean:
rm -f *.o */*.o *.gcov */*.gc?? tdbtest.ldb*
- rm -f $(BINS) $(TDB_OBJ) $(TALLOC_OBJ) $(LDB_LIB)
+ rm -f $(BINS) $(TDB_OBJ) $(TALLOC_OBJ) $(LDB_LIB) $(NSS_LIB)
rm -f man/*.1 man/*.3 man/*.html
rm -f $(EXAMPLES)
rm -rf apidocs/
diff --git a/source4/lib/ldb/nssldb/ldb-grp.c b/source4/lib/ldb/nssldb/ldb-grp.c
new file mode 100644
index 0000000000..f33ec65c55
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-grp.c
@@ -0,0 +1,427 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_gr_attrs[] = {
+ "cn",
+ "userPassword",
+ "gidNumber",
+ NULL
+};
+
+const char *_ldb_nss_mem_attrs[] = {
+ "uid",
+ NULL
+};
+
+#define _NSS_LDB_ENOMEM(amem) \
+ do { \
+ if ( ! amem) { \
+ errno = ENOMEM; \
+ talloc_free(memctx); \
+ return NSS_STATUS_UNAVAIL; \
+ } \
+ } while(0)
+
+/* This setgrent, getgrent, endgrent is not very efficient */
+
+NSS_STATUS _nss_ldb_setgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res != NULL) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _LDB_NSS_GRENT_FILTER,
+ _ldb_nss_gr_attrs,
+ &_ldb_nss_ctx->gr_res);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrent_r(struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->gr_cur >= _ldb_nss_ctx->gr_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ res = talloc_zero(_ldb_nss_ctx->gr_res, struct ldb_result);
+ if ( ! res) {
+ errno = *errnop = ENOMEM;
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ret = _ldb_nss_group_request(&res,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ talloc_free(res);
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return ret;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur],
+ res);
+
+ talloc_free(res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ if (ret != NSS_STATUS_TRYAGAIN) {
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ }
+ return ret;
+ }
+
+ /* this entry is ok, increment counter to nex entry */
+ _ldb_nss_ctx->gr_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrnam_r(const char *name, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ _ldb_nss_gr_attrs,
+ &gr_res);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getgrgid_r(gid_t gid, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ if (gid == 0) { /* we don't serve root gid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRGID_FILTER, gid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ _ldb_nss_gr_attrs,
+ &gr_res);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_initgroups_dyn(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop)
+{
+ int ret;
+ char *filter;
+ const char * attrs[] = { "uidNumber", "gidNumber", NULL };
+ struct ldb_result *uid_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ mem_res = talloc_zero(_ldb_nss_ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(mem_res, _LDB_NSS_PWNAM_FILTER, user);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ attrs,
+ &uid_res);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(mem_res, uid_res);
+
+ /* if none found return */
+ if (uid_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (uid_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ uid_res->msgs[0]->dn,
+ attrs,
+ "memberOf");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_initgr(group,
+ limit,
+ start,
+ size,
+ groups,
+ errnop,
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+
+done:
+ talloc_free(mem_res);
+ return ret;
+}
diff --git a/source4/lib/ldb/nssldb/ldb-nss.c b/source4/lib/ldb/nssldb/ldb-nss.c
new file mode 100644
index 0000000000..3206f38a10
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-nss.c
@@ -0,0 +1,401 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "ldb-nss.h"
+
+struct _ldb_nss_context *_ldb_nss_ctx = NULL;
+
+NSS_STATUS _ldb_nss_init(void)
+{
+ int ret;
+
+ pid_t mypid = getpid();
+
+ if (_ldb_nss_ctx != NULL) {
+ if (_ldb_nss_ctx->pid == mypid) {
+ /* already initialized */
+ return NSS_STATUS_SUCCESS;
+ } else {
+ /* we are in a forked child now, reinitialize */
+ talloc_free(_ldb_nss_ctx);
+ _ldb_nss_ctx = NULL;
+ }
+ }
+
+ _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid);
+ if (_ldb_nss_ctx == NULL) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ _ldb_nss_ctx->pid = mypid;
+
+ ret = ldb_global_init();
+ if (ret != 0) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx);
+ if (_ldb_nss_ctx->ldb == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->base = ldb_dn_explode(_ldb_nss_ctx, _LDB_NSS_BASEDN);
+ if (_ldb_nss_ctx->base == NULL) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ _ldb_nss_ctx->pw_res = NULL;
+ _ldb_nss_ctx->gr_cur = 0;
+ _ldb_nss_ctx->gr_res = NULL;
+
+ return NSS_STATUS_SUCCESS;
+
+failed:
+ /* talloc_free(_ldb_nss_ctx); */
+ _ldb_nss_ctx = NULL;
+ return NSS_STATUS_UNAVAIL;
+}
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg)
+{
+ int len;
+ int bufpos;
+ const char *tmp;
+
+ bufpos = 0;
+
+ /* get username */
+ tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ /* this backend never serves an uid 0 user */
+ result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
+ if (result->pw_uid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
+ if (result->pw_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* get gecos */
+ tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_gecos = &buffer[bufpos];
+ bufpos += len;
+
+ /* get homeDirectory */
+ tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_dir = &buffer[bufpos];
+ bufpos += len;
+
+ /* get shell */
+ tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_shell = &buffer[bufpos];
+ bufpos += len;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members)
+{
+ const char *tmp;
+ size_t len;
+ size_t bufpos;
+ size_t lsize;
+ int i;
+
+ bufpos = 0;
+
+ /* get group name */
+ tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
+ if (result->gr_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* check if there is enough memory for the list of pointers */
+ lsize = (members->count + 1) * sizeof(char *);
+
+ /* align buffer on pointer boundary */
+ bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
+ if ((buflen - bufpos) < lsize) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ result->gr_mem = (char **)&buffer[bufpos];
+ bufpos += lsize;
+
+ for (i = 0; i < members->count; i++) {
+ tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_mem[i] = &buffer[bufpos];
+ bufpos += len;
+ }
+
+ result->gr_mem[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist)
+{
+ NSS_STATUS ret;
+ int i;
+
+ for (i = 0; i < grlist->count; i++) {
+
+ if (limit && (*start > limit)) {
+ /* TODO: warn no all groups were reported */
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+ goto done;
+ }
+
+ if (*start == *size) {
+ /* buffer full, enlarge it */
+ long int gs;
+ gid_t *gm;
+
+ gs = (*size) + 32;
+ if (limit && (gs > limit)) {
+ gs = limit;
+ }
+
+ gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
+ if ( ! gm) {
+ *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ *groups = gm;
+ *size = gs;
+ }
+
+ (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
+ if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
+ /* skip root group or primary group */
+ continue;
+ }
+ (*start)++;
+
+ }
+
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+done:
+ return ret;
+}
+
+#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr)
+{
+ struct ldb_control **ctrls;
+ struct ldb_control *ctrl;
+ struct ldb_asq_control *asqc;
+ struct ldb_request *req;
+ int ret;
+
+ ctrls = talloc_array(*res, struct ldb_control *, 2);
+ _LDB_NSS_ALLOC_CHECK(ctrls);
+
+ ctrl = talloc(ctrls, struct ldb_control);
+ _LDB_NSS_ALLOC_CHECK(ctrl);
+
+ asqc = talloc(ctrl, struct ldb_asq_control);
+ _LDB_NSS_ALLOC_CHECK(asqc);
+
+ asqc->source_attribute = talloc_strdup(asqc, mattr);
+ _LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
+
+ asqc->request = 1;
+ asqc->src_attr_len = strlen(asqc->source_attribute);
+ ctrl->oid = LDB_CONTROL_ASQ_OID;
+ ctrl->critical = 1;
+ ctrl->data = asqc;
+ ctrls[0] = ctrl;
+ ctrls[1] = NULL;
+
+ ret = ldb_build_search_req(
+ &req,
+ _ldb_nss_ctx->ldb,
+ *res,
+ group_dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs,
+ ctrls,
+ res,
+ ldb_search_default_callback);
+
+ if (ret != LDB_SUCCESS) {
+ errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
+
+ ret = ldb_request(_ldb_nss_ctx->ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ } else {
+ talloc_free(req);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ talloc_free(req);
+ return NSS_STATUS_SUCCESS;
+}
+
diff --git a/source4/lib/ldb/nssldb/ldb-nss.h b/source4/lib/ldb/nssldb/ldb-nss.h
new file mode 100644
index 0000000000..c780a21e81
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-nss.h
@@ -0,0 +1,86 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _LDB_NSS
+#define _LDB_NSS
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define _LDB_NSS_URL "etc/users.ldb"
+#define _LDB_NSS_BASEDN "CN=Users,CN=System"
+#define _LDB_NSS_PWENT_FILTER "(&(objectClass=posixAccount)(!(uidNumber=0))(!(gidNumber=0)))"
+#define _LDB_NSS_PWUID_FILTER "(&(objectClass=posixAccount)(uidNumber=%d)(!(gidNumber=0)))"
+#define _LDB_NSS_PWNAM_FILTER "(&(objectClass=posixAccount)(uid=%s)(!(uidNumber=0))(!(gidNumber=0)))"
+
+#define _LDB_NSS_GRENT_FILTER "(&(objectClass=posixGroup)(!(gidNumber=0)))"
+#define _LDB_NSS_GRGID_FILTER "(&(objectClass=posixGroup)(gidNumber=%d)))"
+#define _LDB_NSS_GRNAM_FILTER "(&(objectClass=posixGroup)(cn=%s)(!(gidNumber=0)))"
+
+typedef enum nss_status NSS_STATUS;
+
+struct _ldb_nss_context {
+
+ pid_t pid;
+
+ struct ldb_context *ldb;
+ const struct ldb_dn *base;
+
+ int pw_cur;
+ struct ldb_result *pw_res;
+
+ int gr_cur;
+ struct ldb_result *gr_res;
+};
+
+NSS_STATUS _ldb_nss_init(void);
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg);
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members);
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist);
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr);
+
+#endif /* _LDB_NSS */
diff --git a/source4/lib/ldb/nssldb/ldb-pwd.c b/source4/lib/ldb/nssldb/ldb-pwd.c
new file mode 100644
index 0000000000..e4bafdcf7c
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-pwd.c
@@ -0,0 +1,241 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_pw_attrs[] = {
+ "uid",
+ "userPassword",
+ "uidNumber",
+ "gidNumber",
+ "gecos",
+ "homeDirectory",
+ "loginShell",
+ NULL
+};
+
+NSS_STATUS _nss_ldb_setpwent(void)
+{
+ int ret;
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res != NULL) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _LDB_NSS_PWENT_FILTER,
+ _ldb_nss_pw_attrs,
+ &_ldb_nss_ctx->pw_res);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endpwent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwent_r(struct passwd *result_buf,
+ char *buffer,
+ int buflen,
+ int *errnop)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->pw_cur >= _ldb_nss_ctx->pw_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->pw_res->msgs[_ldb_nss_ctx->pw_cur]);
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwuid_r(uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ if (uid == 0) { /* we don't serve root uid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWUID_FILTER, uid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ _ldb_nss_pw_attrs,
+ &res);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getpwnam_r(const char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ _ldb_nss_pw_attrs,
+ &res);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+
diff --git a/source4/lib/ldb/nssldb/nsstest.c b/source4/lib/ldb/nssldb/nsstest.c
new file mode 100644
index 0000000000..ea3964fc4e
--- /dev/null
+++ b/source4/lib/ldb/nssldb/nsstest.c
@@ -0,0 +1,433 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ nss tester
+
+ Copyright (C) Andrew Tridgell 2001-2004
+
+ This 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 of the License, or (at your option) any later version.
+
+ This 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 this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nss.h>
+#include <dlfcn.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+
+typedef enum nss_status NSS_STATUS;
+
+static const char *so_path = "lib/libnss_ldb.so.2";
+static char *nss_name;
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+ char s[1024];
+ static void *h;
+ void *res;
+
+ snprintf(s,sizeof(s), "_nss_%s_%s", nss_name, name);
+
+ if (!h) {
+ h = dlopen(so_path, RTLD_LAZY);
+ }
+ if (!h) {
+ printf("Can't open shared library %s : %s\n", so_path, dlerror());
+ exit(1);
+ }
+ res = dlsym(h, s);
+ if (!res) {
+ printf("Can't find function %s : %s\n", s, dlerror());
+ return NULL;
+ }
+ return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+ last_error = status;
+ total_errors++;
+ printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n",
+ who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+ NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *) = find_fn("getpwent_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwent", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+ NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *,
+ size_t , int *) = find_fn("getpwnam_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwnam", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+ NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *,
+ size_t , int *) = find_fn("getpwuid_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwuid", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static void nss_setpwent(void)
+{
+ NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent");
+ NSS_STATUS status;
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setpwent", status);
+ }
+}
+
+static void nss_endpwent(void)
+{
+ NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent");
+ NSS_STATUS status;
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endpwent", status);
+ }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+ NSS_STATUS (*_nss_getgrent_r)(struct group *, char *,
+ size_t , int *) = find_fn("getgrent_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1024;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc(buflen);
+
+again:
+ status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrent", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+ NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *,
+ size_t , int *) = find_fn("getgrnam_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc(buflen);
+again:
+ status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrnam", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+ NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *,
+ size_t , int *) = find_fn("getgrgid_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!buf) buf = malloc(buflen);
+again:
+ status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrgid", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static void nss_setgrent(void)
+{
+ NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent");
+ NSS_STATUS status;
+ status = _nss_setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setgrent", status);
+ }
+}
+
+static void nss_endgrent(void)
+{
+ NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent");
+ NSS_STATUS status;
+ status = _nss_endgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endgrent", status);
+ }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+ NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+ long int *, gid_t **, long int , int *) =
+ find_fn("initgroups_dyn");
+ NSS_STATUS status;
+
+ if (!_nss_initgroups) return NSS_STATUS_UNAVAIL;
+
+ status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("initgroups", status);
+ }
+ return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%s:%s:%d:%d:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ pwd->pw_uid,
+ pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%d: ",
+ grp->gr_name,
+ grp->gr_passwd,
+ grp->gr_gid);
+
+ if (!grp->gr_mem[0]) {
+ printf("\n");
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i+1]; i++) {
+ printf("%s, ", grp->gr_mem[i]);
+ }
+ printf("%s\n", grp->gr_mem[i]);
+}
+
+static void nss_test_initgroups(char *name, gid_t gid)
+{
+ long int size = 16;
+ long int start = 1;
+ gid_t *groups = NULL;
+ int i;
+ NSS_STATUS status;
+
+ groups = (gid_t *)malloc(size * sizeof(gid_t));
+ groups[0] = gid;
+
+ status = nss_initgroups(name, gid, &groups, &start, &size);
+ if (status == NSS_STATUS_UNAVAIL) {
+ printf("No initgroups fn\n");
+ return;
+ }
+
+ for (i=0; i<start-1; i++) {
+ printf("%d, ", groups[i]);
+ }
+ printf("%d\n", groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+ struct passwd *pwd;
+
+ nss_setpwent();
+ /* loop over all users */
+ while ((pwd = nss_getpwent())) {
+ printf("Testing user %s\n", pwd->pw_name);
+ printf("getpwent: "); print_passwd(pwd);
+ pwd = nss_getpwuid(pwd->pw_uid);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwuid\n");
+ continue;
+ }
+ printf("getpwuid: "); print_passwd(pwd);
+ pwd = nss_getpwnam(pwd->pw_name);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwnam\n");
+ continue;
+ }
+ printf("getpwnam: "); print_passwd(pwd);
+ printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+ printf("\n");
+ }
+ nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+ struct group *grp;
+
+ nss_setgrent();
+ /* loop over all groups */
+ while ((grp = nss_getgrent())) {
+ printf("Testing group %s\n", grp->gr_name);
+ printf("getgrent: "); print_group(grp);
+ grp = nss_getgrnam(grp->gr_name);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrnam\n");
+ continue;
+ }
+ printf("getgrnam: "); print_group(grp);
+ grp = nss_getgrgid(grp->gr_gid);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrgid\n");
+ continue;
+ }
+ printf("getgrgid: "); print_group(grp);
+ printf("\n");
+ }
+ nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = getpwnam("nosuchname");
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant user gave error %d\n", last_error);
+ }
+
+ pwd = getpwuid(0xFFF0);
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant uid gave error %d\n", last_error);
+ }
+
+ grp = getgrnam("nosuchgroup");
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant group gave error %d\n", last_error);
+ }
+
+ grp = getgrgid(0xFFF0);
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant gid gave error %d\n", last_error);
+ }
+}
+
+ int main(int argc, char *argv[])
+{
+ char *p;
+
+ if (argc > 1) so_path = argv[1];
+
+ p = strrchr(so_path, '_');
+ if (!p) {
+ printf("Badly formed name for .so - must be libnss_FOO.so\n");
+ exit(1);
+ }
+ nss_name = strdup(p+1);
+ p = strchr(nss_name, '.');
+ if (p) *p = 0;
+
+ printf("so_path=%s nss_name=%s\n\n", so_path, nss_name);
+
+ nss_test_users();
+ nss_test_groups();
+ nss_test_errors();
+
+ printf("total_errors=%d\n", total_errors);
+
+ return total_errors;
+}