r19456: Add an example application for ldb using the tdb backend
authorSimo Sorce <idra@samba.org>
Sun, 22 Oct 2006 21:18:43 +0000 (21:18 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:21:35 +0000 (14:21 -0500)
source/lib/ldb/Makefile.in
source/lib/ldb/nssldb/ldb-grp.c [new file with mode: 0644]
source/lib/ldb/nssldb/ldb-nss.c [new file with mode: 0644]
source/lib/ldb/nssldb/ldb-nss.h [new file with mode: 0644]
source/lib/ldb/nssldb/ldb-pwd.c [new file with mode: 0644]
source/lib/ldb/nssldb/nsstest.c [new file with mode: 0644]

index ce23f309d3984cdc6900b7ecb29f993e15ccf352..c90f41afd79731c219d187d9c7cb51aab6c6b015 100644 (file)
@@ -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/source/lib/ldb/nssldb/ldb-grp.c b/source/lib/ldb/nssldb/ldb-grp.c
new file mode 100644 (file)
index 0000000..f33ec65
--- /dev/null
@@ -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/source/lib/ldb/nssldb/ldb-nss.c b/source/lib/ldb/nssldb/ldb-nss.c
new file mode 100644 (file)
index 0000000..3206f38
--- /dev/null
@@ -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/source/lib/ldb/nssldb/ldb-nss.h b/source/lib/ldb/nssldb/ldb-nss.h
new file mode 100644 (file)
index 0000000..c780a21
--- /dev/null
@@ -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/source/lib/ldb/nssldb/ldb-pwd.c b/source/lib/ldb/nssldb/ldb-pwd.c
new file mode 100644 (file)
index 0000000..e4bafdc
--- /dev/null
@@ -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/source/lib/ldb/nssldb/nsstest.c b/source/lib/ldb/nssldb/nsstest.c
new file mode 100644 (file)
index 0000000..ea3964f
--- /dev/null
@@ -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;
+}