[SSSD] [PATCHES] Netgroup support for LDAP provider

Sumit Bose sbose at redhat.com
Tue Oct 12 14:18:28 UTC 2010


On Mon, Oct 11, 2010 at 02:32:59PM -0400, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 10/05/2010 10:21 AM, Sumit Bose wrote:
> > Hi,
> > 
> > this series of patches continue the work Stephen has started in
> > "[PATCHES] Support for netgroups in the NSS client and responder".
> > 
> > We decided to try to be as compatible to nss_ldap as possible, i.e. we
> > do not any group unrolling or loop detection inside of sssd, but rely on
> > glibc. To achieve this I added support to return netgroup member groups
> > to the client and glibc. This is mostly done in 0003 and 0006. 0007 and
> > 0008 add the necessary support to the LDAP provider.
> > 
> > There is one difference to nss_ldap. If a netgroup member is not
> > specified by a plain name but by a DN nss_ldap just returns the DN
> > string to glibc and then glibc searches for a netgroup where the name is
> > the returned DN. Even nss_ldap cannot find a matching netgroup for this
> > name. If sssd detects a DN in the member list it tries to translate it
> > to the corresponding name of the netgroup. If this fails it will return
> > the full DN.
> > 
> > The patches 0001 and 0002 fixes two errors in Stephen's patches.
> 
> 
> Patch 0001: Nack. Please reverse the order of these two functions. The
> request should be set to "done" before we post it.

done

> 
> Patch 0002: Ack
> 
> Patch 0003: Please update the comment about the wire protocol to mention
> that byte 8 describes the 8-bit value for the reply type.

done

> 
> Also, I'm not sure that this is safe for aligned platforms like MIPS. It
> might be wisest to just make this a 32-bit value and use the alignment
> code here.

Currently it is handled as char/uint8_t, so it shouldn't be an issue. But
if you prefer a uint32_t here I can change it. One other reason to
change it would be that we always use uint32_t for such things in the
wire protocol. What do you think?

> 
> Patch 0004: Ack
> 
> Patch 0005: Ack
> 
> Patch 0006: To avoid doing two calls to talloc_realloc() (which can be
> expensive, especially for very large netgroups) it might be best to do
> both element checks first and realloc with the results for both ahead of
> populating them.

done

> 
> In lookup_netgr_step() you're still using &netgr->triples as the return
> variable. I think you were correct elsewhere to change this to entries,
> and the same should be done here.
> 

done

> Also, I'm not sure if we want to return EINVAL for an empty member. It's
> probably enough to just skip it.

done

> 
> Patch 0007: Ack
> 
> Patch 0008: Nack
> netgr_translate_members_send():
> After calling sysdb_search_entry, please free sysdb_filter and
> netgr_basedn since we won't need them again.
> Spelling mistake: all_resovled should be all_resolved. Also in
> update_dn_list()

done

> 
> You can't return from netgr_translate_members_ldap_step() with
> tevent_req_done() (such as when we hit ENOMEM) safely.
> 
> If it happens on the first call (from within
> netgr_translate_members_send()) then the callback will never fire. You
> should change netgr_translate_members_ldap_step() to return errno_t types.
> 
> EOK should mean immediately call tevent_req_done() and
> tevent_req_post(). If it returns EAGAIN, reenter the mainloop. Any other
> error should call tevent_req_error() and tevent_req_post().
> 

good catch, done

> Please steal cn_attr onto the subreq so that it's cleaned up once the
> subreq is freed in netgr_translate_members_ldap_done(). Otherwise, we're
> carrying an extra cn_attrs array around in memory for every nested
> netgroup we're processing.

done

> 
> 
> Patch 0009: Ack
> 
> 

New versions attached.

bye,
Sumit

> 
> - -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkyzWFsACgkQeiVVYja6o6MtXQCfcy91N7g1iINZ5Ss8/NpqE+uU
> lOAAnRZ1xnLiNNzhHtaijS7axY/396eQ
> =h8/P
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From c9dc01d880ccaa30477288e4ec353720af33af3c Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 4 Oct 2010 13:24:18 +0200
Subject: [PATCH 1/9] Add missing tevent_req_done()

---
 src/responder/nss/nsssrv_netgroup.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index be99de7..8c6de84 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -296,6 +296,7 @@ static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
             /* An unexpected error occurred */
             goto error;
         }
+        tevent_req_done(req);
         tevent_req_post(req, cmdctx->cctx->ev);
         /* Will return control below */
     } else {
-- 
1.7.2.3

-------------- next part --------------
From 5ef9a6fd267fb69a11367f0c085d125d923fbd54 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 5 Oct 2010 11:05:08 +0200
Subject: [PATCH 2/9] Return NSS_STATUS_RETURN instead of NSS_STATUS_NOTFOUND

NSS_STATUS_RETURN needs to be returned to glibc otherwise nested groups
are not resolved by glibc.
---
 src/sss_client/nss_netgroup.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/sss_client/nss_netgroup.c b/src/sss_client/nss_netgroup.c
index f70283f..edc5020 100644
--- a/src/sss_client/nss_netgroup.c
+++ b/src/sss_client/nss_netgroup.c
@@ -261,7 +261,7 @@ enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result,
     /* no results if not found */
     if ((((uint32_t *)repbuf)[0] == 0) || (replen <= NETGR_METADATA_COUNT)) {
         free(repbuf);
-        return NSS_STATUS_NOTFOUND;
+        return NSS_STATUS_RETURN;
     }
 
     sss_nss_getnetgrent_data.data = repbuf;
-- 
1.7.2.3

-------------- next part --------------
From edcbcc32562a101f10acaa342c2cf45ed4dda634 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 1 Oct 2010 11:30:44 +0200
Subject: [PATCH 3/9] Add handling of nested netgroups to nss client

---
 src/responder/nss/nsssrv_netgroup.c |    5 +-
 src/sss_client/nss_netgroup.c       |  176 +++++++++++++++++++++--------------
 src/sss_client/sss_cli.h            |    5 +
 3 files changed, 114 insertions(+), 72 deletions(-)

diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 8c6de84..706a660 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -797,13 +797,16 @@ static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
             domainlen += strlen(triples[client->netgrent_cur]->domainname);
         }
 
-        len = hostlen + userlen + domainlen;
+        len = 1 + hostlen + userlen + domainlen;
         ret = sss_packet_grow(packet, len);
         if (ret != EOK) {
             return ret;
         }
         sss_packet_get_body(packet, &body, &blen);
 
+        body[rp] = SSS_NETGR_REP_TRIPLE;
+        rp++;
+
         if (hostlen == 1) {
             body[rp] = '\0';
         } else {
diff --git a/src/sss_client/nss_netgroup.c b/src/sss_client/nss_netgroup.c
index edc5020..acc1897 100644
--- a/src/sss_client/nss_netgroup.c
+++ b/src/sss_client/nss_netgroup.c
@@ -45,11 +45,14 @@ static struct sss_nss_getnetgrent_data {
 /*
  * Replies:
  *
- * 0-3: 32bit unsigned number of results
+ * 0-3: 32bit unsigned number of results N
  * 4-7: 32bit unsigned (reserved/padding)
  *  For each result:
- *  8-X: sequence of \0 terminated strings representing tuple
+ *  8:   8bit unsigned type of result
+ *  9-X: \0 terminated string representing a tuple
  *       (host, user, domain)
+ *  or a netgroup, depending on the type indicator
+ *  ... repeated N times
  */
 #define NETGR_METADATA_COUNT 2 * sizeof(uint32_t)
 struct sss_nss_netgr_rep {
@@ -78,83 +81,114 @@ static int sss_nss_getnetgr_readrep(struct sss_nss_netgr_rep *pr,
     size_t i, slen;
     ssize_t dlen;
 
-    if (*len < 3) {
+    if (*len < 4) {
         /* Not enough space for data, bad packet */
         return EBADMSG;
     }
 
-    sbuf = (char *)&buf[0];
+    sbuf = (char *)&buf[1];
     slen = *len;
     dlen = pr->buflen;
 
-    /* Host value */
+    /* Type */
     i = 0;
-    pr->result->val.triple.host = &(pr->buffer[i]);
-    while (slen > i && dlen > 0) {
-        pr->buffer[i] = sbuf[i];
-        if (pr->buffer[i] == '\0') break;
-        i++;
-        dlen--;
-    }
-    if (slen <= i) { /* premature end of buf */
-        return EBADMSG;
-    }
-    if (dlen <= 0) { /* not enough memory */
-        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
-    }
-    i++;
-    dlen--;
-
-    /* libc expects NULL instead of empty string */
-    if (strlen(pr->result->val.triple.host) == 0) {
-        pr->result->val.triple.host = NULL;
-    }
-
-    /* User value */
-    pr->result->val.triple.user = &(pr->buffer[i]);
-    while (slen > i && dlen > 0) {
-        pr->buffer[i] = sbuf[i];
-        if (pr->buffer[i] == '\0') break;
-        i++;
-        dlen--;
-    }
-    if (slen <= i) { /* premature end of buf */
-        return EBADMSG;
-    }
-    if (dlen <= 0) { /* not enough memory */
-        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
-    }
-    i++;
-    dlen--;
-
-    /* libc expects NULL instead of empty string */
-    if (strlen(pr->result->val.triple.user) == 0) {
-        pr->result->val.triple.user = NULL;
+    switch (buf[0]) {
+        case SSS_NETGR_REP_TRIPLE:
+            pr->result->type = triple_val;
+
+            /* Host value */
+            pr->result->val.triple.host = &(pr->buffer[i]);
+            while (slen > i && dlen > 0) {
+                pr->buffer[i] = sbuf[i];
+                if (pr->buffer[i] == '\0') break;
+                i++;
+                dlen--;
+            }
+            if (slen <= i) { /* premature end of buf */
+                return EBADMSG;
+            }
+            if (dlen <= 0) { /* not enough memory */
+                return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+            }
+            i++;
+            dlen--;
+
+            /* libc expects NULL instead of empty string */
+            if (strlen(pr->result->val.triple.host) == 0) {
+                pr->result->val.triple.host = NULL;
+            }
+
+            /* User value */
+            pr->result->val.triple.user = &(pr->buffer[i]);
+            while (slen > i && dlen > 0) {
+                pr->buffer[i] = sbuf[i];
+                if (pr->buffer[i] == '\0') break;
+                i++;
+                dlen--;
+            }
+            if (slen <= i) { /* premature end of buf */
+                return EBADMSG;
+            }
+            if (dlen <= 0) { /* not enough memory */
+                return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+            }
+            i++;
+            dlen--;
+
+            /* libc expects NULL instead of empty string */
+            if (strlen(pr->result->val.triple.user) == 0) {
+                pr->result->val.triple.user = NULL;
+            }
+
+            /* Domain value */
+            pr->result->val.triple.domain = &(pr->buffer[i]);
+            while (slen > i && dlen > 0) {
+                pr->buffer[i] = sbuf[i];
+                if (pr->buffer[i] == '\0') break;
+                i++;
+                dlen--;
+            }
+            if (slen <= i) { /* premature end of buf */
+                return EBADMSG;
+            }
+            if (dlen <= 0) { /* not enough memory */
+                return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+            }
+            i++;
+            dlen--;
+
+            /* libc expects NULL instead of empty string */
+            if (strlen(pr->result->val.triple.domain) == 0) {
+                pr->result->val.triple.domain = NULL;
+            }
+
+            break;
+        case SSS_NETGR_REP_GROUP:
+            pr->result->type = group_val;
+
+            pr->result->val.group = &(pr->buffer[i]);
+            while (slen > i && dlen > 0) {
+                pr->buffer[i] = sbuf[i];
+                if (pr->buffer[i] == '\0') break;
+                i++;
+                dlen--;
+            }
+            if (slen <= i) { /* premature end of buf */
+                return EBADMSG;
+            }
+            if (dlen <= 0) { /* not enough memory */
+                return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+            }
+            i++;
+            dlen--;
+
+            break;
+        default:
+            return EBADMSG;
     }
 
-    /* Domain value */
-    pr->result->val.triple.domain = &(pr->buffer[i]);
-    while (slen > i && dlen > 0) {
-        pr->buffer[i] = sbuf[i];
-        if (pr->buffer[i] == '\0') break;
-        i++;
-        dlen--;
-    }
-    if (slen <= i) { /* premature end of buf */
-        return EBADMSG;
-    }
-    if (dlen <= 0) { /* not enough memory */
-        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
-    }
-    i++;
-    dlen--;
-
-    /* libc expects NULL instead of empty string */
-    if (strlen(pr->result->val.triple.domain) == 0) {
-        pr->result->val.triple.domain = NULL;
-    }
 
-    *len = slen -i;
+    *len = slen -i -1;
 
     return 0;
 }
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 1a068d5..fcea8e6 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -410,6 +410,11 @@ enum user_info_type {
  * @}
  */ /* end of group sss_pam_cli */
 
+enum sss_netgr_rep_type {
+    SSS_NETGR_REP_TRIPLE = 1,
+    SSS_NETGR_REP_GROUP
+};
+
 enum sss_cli_error_codes {
     ESSS_SSS_CLI_ERROR_START = 0x1000,
     ESSS_BAD_PRIV_SOCKET,
-- 
1.7.2.3

-------------- next part --------------
From 2d5a45dd9953db8bc9f23ec76c38cb60a6ecf0a3 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 5 Oct 2010 13:22:38 +0200
Subject: [PATCH 4/9] Do not fail if netgroup exists just update the attributes

---
 src/db/sysdb_ops.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 373ce37..68f4e88 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -1198,7 +1198,7 @@ int sysdb_add_netgroup(struct sysdb_ctx *ctx,
 
     /* try to add the netgroup */
     ret = sysdb_add_basic_netgroup(ctx, domain, name, description);
-    if (ret) goto done;
+    if (ret && ret != EEXIST) goto done;
 
     if (!attrs) {
         attrs = sysdb_new_attrs(tmp_ctx);
-- 
1.7.2.3

-------------- next part --------------
From ed2d2d94aebf3d335496654e7777610ed02a1881 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 5 Oct 2010 13:28:21 +0200
Subject: [PATCH 5/9] Add sysdb_netgroup_base_dn()

---
 src/db/sysdb.c |    6 ++++++
 src/db/sysdb.h |    2 ++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 4549226..b6ef8df 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -58,6 +58,12 @@ struct ldb_dn *sysdb_netgroup_dn(struct sysdb_ctx *ctx, void *memctx,
     return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_NETGROUP, name, domain);
 }
 
+struct ldb_dn *sysdb_netgroup_base_dn(struct sysdb_ctx *ctx, void *memctx,
+                                 const char *domain)
+{
+    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_NETGROUP_BASE, domain);
+}
+
 errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
                             const char *_dn, char **_name)
 {
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index a2b842f..561d37b 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -217,6 +217,8 @@ struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
                               const char *domain, const char *name);
 struct ldb_dn *sysdb_netgroup_dn(struct sysdb_ctx *ctx, void *memctx,
                                  const char *domain, const char *name);
+struct ldb_dn *sysdb_netgroup_base_dn(struct sysdb_ctx *ctx, void *memctx,
+                                      const char *domain);
 errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
                             const char *dn_str, char **name);
 struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx,
-- 
1.7.2.3

-------------- next part --------------
From eb47d43f4884f87a8fe2a9122be4095697222111 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 5 Oct 2010 13:38:43 +0200
Subject: [PATCH 6/9] Also return member groups to the client

---
 src/db/sysdb.h                      |   17 +++-
 src/db/sysdb_search.c               |  126 ++++++++++++++++++--------------
 src/responder/nss/nsssrv_netgroup.c |  140 +++++++++++++++++++++-------------
 src/responder/nss/nsssrv_private.h  |    2 +-
 src/tests/sysdb-tests.c             |   88 +++++++++++-----------
 5 files changed, 214 insertions(+), 159 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 561d37b..a1d6c91 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -302,10 +302,17 @@ int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
                     struct ldb_result **res);
 
 struct sysdb_netgroup_ctx {
-    char *hostname;
-    char *username;
-    char *domainname;
+    enum {SYSDB_NETGROUP_TRIPLE_VAL, SYSDB_NETGROUP_GROUP_VAL} type;
+    union {
+        struct {
+            char *hostname;
+            char *username;
+            char *domainname;
+        } triple;
+        char *groupname;
+    } value;
 };
+
 errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
                        struct sysdb_ctx *ctx,
                        struct sss_domain_info *domain,
@@ -665,8 +672,8 @@ errno_t sysdb_attrs_to_list(TALLOC_CTX *memctx,
                             const char *attr_name,
                             char ***_list);
 
-errno_t sysdb_netgr_to_triples(TALLOC_CTX *mem_ctx,
+errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
                                struct ldb_result *res,
-                               struct sysdb_netgroup_ctx ***triples);
+                               struct sysdb_netgroup_ctx ***entries);
 
 #endif /* __SYS_DB_H__ */
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 91519e3..e983b39 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -650,15 +650,16 @@ done:
     return ret;
 }
 
-errno_t sysdb_netgr_to_triples(TALLOC_CTX *mem_ctx,
+errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
                                struct ldb_result *res,
-                               struct sysdb_netgroup_ctx ***triples)
+                               struct sysdb_netgroup_ctx ***entries)
 {
     errno_t ret;
     size_t size = 0;
+    size_t c = 0;
     char *triple_str;
     TALLOC_CTX *tmp_ctx;
-    struct sysdb_netgroup_ctx **tmp_triples = NULL;
+    struct sysdb_netgroup_ctx **tmp_entry = NULL;
     struct ldb_message_element *el;
     int i, j;
 
@@ -673,69 +674,84 @@ errno_t sysdb_netgr_to_triples(TALLOC_CTX *mem_ctx,
 
     for (i=0; i < res->count; i++) {
         el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
-        if (!el) {
-            /* No triples in this netgroup. It might be a nesting
-             * container only.
-             * Skip it and continue on.
-             */
-            continue;
+        if (el != NULL) {
+            size += el->num_values;
         }
-
-        /* Enlarge the array by the value count
-         * Always keep one extra entry for the NULL terminator
-         */
-        tmp_triples = talloc_realloc(tmp_ctx, tmp_triples,
-                                     struct sysdb_netgroup_ctx *,
-                                     size+el->num_values+1);
-        if (!tmp_triples) {
-            ret = ENOMEM;
-            goto done;
+        el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+        if (el != NULL) {
+            size += el->num_values;
         }
+    }
 
-        /* Copy in all of the triples */
-        for(j = 0; j < el->num_values; j++) {
-            triple_str = talloc_strndup(tmp_ctx,
-                                       (const char *)el->values[j].data,
-                                       el->values[j].length);
-            if (!triple_str) {
-                ret = ENOMEM;
-                goto done;
-            }
+    tmp_entry = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, size + 1);
+    if (tmp_entry == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
 
-            tmp_triples[size] = talloc_zero(tmp_triples,
-                                            struct sysdb_netgroup_ctx);
-            if (!tmp_triples[size]) {
-                ret = ENOMEM;
-                goto done;
+    if (size != 0) {
+        for (i=0; i < res->count; i++) {
+            el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
+            if (el != NULL) {
+                /* Copy in all of the entries */
+                for(j = 0; j < el->num_values; j++) {
+                    triple_str = talloc_strndup(tmp_ctx,
+                                                (const char *)el->values[j].data,
+                                                el->values[j].length);
+                    if (!triple_str) {
+                        ret = ENOMEM;
+                        goto done;
+                    }
+
+                    tmp_entry[c] = talloc_zero(tmp_entry,
+                                                  struct sysdb_netgroup_ctx);
+                    if (!tmp_entry[c]) {
+                        ret = ENOMEM;
+                        goto done;
+                    }
+
+                    tmp_entry[c]->type = SYSDB_NETGROUP_TRIPLE_VAL;
+                    ret = sysdb_netgr_split_triple(tmp_entry[c],
+                                        triple_str,
+                                        &tmp_entry[c]->value.triple.hostname,
+                                        &tmp_entry[c]->value.triple.username,
+                                        &tmp_entry[c]->value.triple.domainname);
+                    if (ret != EOK) {
+                        goto done;
+                    }
+
+                    c++;
+                }
             }
-
-            ret = sysdb_netgr_split_triple(tmp_triples[size],
-                                           triple_str,
-                                           &tmp_triples[size]->hostname,
-                                           &tmp_triples[size]->username,
-                                           &tmp_triples[size]->domainname);
-            if (ret != EOK) {
-                goto done;
+            el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+            if (el != NULL) {
+                for(j = 0; j < el->num_values; j++) {
+                    tmp_entry[c] = talloc_zero(tmp_entry,
+                                                  struct sysdb_netgroup_ctx);
+                    if (!tmp_entry[c]) {
+                        ret = ENOMEM;
+                        goto done;
+                    }
+
+                    tmp_entry[c]->type = SYSDB_NETGROUP_GROUP_VAL;
+                    tmp_entry[c]->value.groupname = talloc_strndup(tmp_entry[c],
+                                               (const char *)el->values[j].data,
+                                               el->values[j].length);
+                    if (tmp_entry[c]->value.groupname == NULL) {
+                        ret = ENOMEM;
+                        goto done;
+                    }
+
+                    c++;
+                }
             }
-
-            size++;
         }
     }
 
-    if (!tmp_triples) {
-        /* No entries were found
-         * Create a dummy reply
-         */
-        tmp_triples = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, 1);
-        if (!tmp_triples) {
-            ret = ENOMEM;
-            goto done;
-        }
-    }
     /* Add NULL terminator */
-    tmp_triples[size] = NULL;
+    tmp_entry[c] = NULL;
 
-    *triples = talloc_steal(mem_ctx, tmp_triples);
+    *entries = talloc_steal(mem_ctx, tmp_entry);
     ret = EOK;
 
 done:
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 706a660..18fb6f4 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -401,9 +401,9 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
              return ret;
          }
 
-         /* Convert the result to a list of triples */
-         ret = sysdb_netgr_to_triples(netgr, step_ctx->dctx->res,
-                                      &netgr->triples);
+         /* Convert the result to a list of entries */
+         ret = sysdb_netgr_to_entries(netgr, step_ctx->dctx->res,
+                                      &netgr->entries);
          if (ret == ENOENT) {
              /* This netgroup was not found in this domain */
              if (!step_ctx->dctx->check_provider) {
@@ -417,7 +417,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
          }
 
          if (ret != EOK) {
-             DEBUG(1, ("Failed to convert results into triples\n"));
+             DEBUG(1, ("Failed to convert results into entries\n"));
              return EIO;
          }
 
@@ -465,7 +465,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
     DEBUG(2, ("No matching domain found for [%s], fail!\n",
               step_ctx->name));
     netgr->ready = true;
-    netgr->triples = NULL;
+    netgr->entries = NULL;
     return ENOENT;
 }
 
@@ -714,7 +714,7 @@ static void setnetgrent_implicit_done(struct tevent_req *req)
 }
 
 static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
-                                   struct sysdb_netgroup_ctx **triples,
+                                   struct sysdb_netgroup_ctx **entries,
                                    int num);
 static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
                                            struct getent_ctx *netgr)
@@ -740,9 +740,9 @@ static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
         return ret;
     }
 
-    if (!netgr->triples || netgr->triples[0] == NULL) {
+    if (!netgr->entries || netgr->entries[0] == NULL) {
         /* No entries */
-        DEBUG(5, ("No triples found\n"));
+        DEBUG(5, ("No entries found\n"));
         ret = fill_empty(client->creq->out);
         if (ret != EOK) {
             return nss_cmd_done(cmdctx, ret);
@@ -750,7 +750,7 @@ static errno_t nss_cmd_getnetgrent_process(struct nss_cmd_ctx *cmdctx,
         goto done;
     }
 
-    ret = nss_cmd_retnetgrent(client, netgr->triples, num);
+    ret = nss_cmd_retnetgrent(client, netgr->entries, num);
 
 done:
     sss_packet_set_error(client->creq->out, ret);
@@ -760,13 +760,14 @@ done:
 }
 
 static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
-                                   struct sysdb_netgroup_ctx **triples,
+                                   struct sysdb_netgroup_ctx **entries,
                                    int count)
 {
     size_t len;
     size_t hostlen = 0;
     size_t userlen = 0;
     size_t domainlen = 0;
+    size_t grouplen = 0;
     uint8_t *body;
     size_t blen, rp;
     errno_t ret;
@@ -780,59 +781,90 @@ static errno_t nss_cmd_retnetgrent(struct cli_ctx *client,
 
     start = client->netgrent_cur;
     num = 0;
-    while (triples[client->netgrent_cur] &&
-            (client->netgrent_cur - start) < count) {
-        hostlen = 1;
-        if (triples[client->netgrent_cur]->hostname) {
-            hostlen += strlen(triples[client->netgrent_cur]->hostname);
-        }
+    while (entries[client->netgrent_cur] &&
+           (client->netgrent_cur - start) < count) {
+        if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_TRIPLE_VAL) {
+            hostlen = 1;
+            if (entries[client->netgrent_cur]->value.triple.hostname) {
+                hostlen += strlen(entries[client->netgrent_cur]->value.triple.hostname);
+            }
 
-        userlen = 1;
-        if (triples[client->netgrent_cur]->username) {
-            userlen += strlen(triples[client->netgrent_cur]->username);
-        }
+            userlen = 1;
+            if (entries[client->netgrent_cur]->value.triple.username) {
+                userlen += strlen(entries[client->netgrent_cur]->value.triple.username);
+            }
 
-        domainlen = 1;
-        if (triples[client->netgrent_cur]->domainname) {
-            domainlen += strlen(triples[client->netgrent_cur]->domainname);
-        }
+            domainlen = 1;
+            if (entries[client->netgrent_cur]->value.triple.domainname) {
+                domainlen += strlen(entries[client->netgrent_cur]->value.triple.domainname);
+            }
 
-        len = 1 + hostlen + userlen + domainlen;
-        ret = sss_packet_grow(packet, len);
-        if (ret != EOK) {
-            return ret;
-        }
-        sss_packet_get_body(packet, &body, &blen);
+            len = 1 + hostlen + userlen + domainlen;
+            ret = sss_packet_grow(packet, len);
+            if (ret != EOK) {
+                return ret;
+            }
+            sss_packet_get_body(packet, &body, &blen);
 
-        body[rp] = SSS_NETGR_REP_TRIPLE;
-        rp++;
+            body[rp] = SSS_NETGR_REP_TRIPLE;
+            rp++;
 
-        if (hostlen == 1) {
-            body[rp] = '\0';
-        } else {
-            memcpy(&body[rp],
-                   triples[client->netgrent_cur]->hostname,
-                   hostlen);
-        }
-        rp += hostlen;
+            if (hostlen == 1) {
+                body[rp] = '\0';
+            } else {
+                memcpy(&body[rp],
+                       entries[client->netgrent_cur]->value.triple.hostname,
+                       hostlen);
+            }
+            rp += hostlen;
+
+            if (userlen == 1) {
+                body[rp] = '\0';
+            } else {
+                memcpy(&body[rp],
+                       entries[client->netgrent_cur]->value.triple.username,
+                       userlen);
+            }
+            rp += userlen;
+
+            if (domainlen == 1) {
+                body[rp] = '\0';
+            } else {
+                memcpy(&body[rp],
+                       entries[client->netgrent_cur]->value.triple.domainname,
+                       domainlen);
+            }
+            rp += domainlen;
+        } else if (entries[client->netgrent_cur]->type == SYSDB_NETGROUP_GROUP_VAL) {
+            if (entries[client->netgrent_cur]->value.groupname == NULL ||
+                entries[client->netgrent_cur]->value.groupname[0] == '\0') {
+                DEBUG(1, ("Empty netgroup member. Please check your cache.\n"));
+                continue;
+            }
 
-        if (userlen == 1) {
-            body[rp] = '\0';
-        } else {
-            memcpy(&body[rp],
-                   triples[client->netgrent_cur]->username,
-                   userlen);
-        }
-        rp += userlen;
+            grouplen = 1 + strlen(entries[client->netgrent_cur]->value.groupname);
+
+            len = 1 + grouplen;
+
+            ret = sss_packet_grow(packet, len);
+            if (ret != EOK) {
+                return ret;
+            }
+
+            sss_packet_get_body(packet, &body, &blen);
+
+            body[rp] = SSS_NETGR_REP_GROUP;
+            rp++;
 
-        if (domainlen == 1) {
-            body[rp] = '\0';
-        } else {
             memcpy(&body[rp],
-                   triples[client->netgrent_cur]->domainname,
-                   domainlen);
+                   entries[client->netgrent_cur]->value.groupname,
+                   grouplen);
+            rp += grouplen;
+        } else {
+            DEBUG(1, ("Unexpected value type for netgroup entry. "
+                      "Please check your cache.\n"));
+            continue;
         }
-        rp += domainlen;
 
         num++;
         client->netgrent_cur++;
diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h
index 15cb630..4d9f947 100644
--- a/src/responder/nss/nsssrv_private.h
+++ b/src/responder/nss/nsssrv_private.h
@@ -61,7 +61,7 @@ struct getent_ctx {
 
     /* Netgroup-specific */
     hash_table_t *lookup_table;
-    struct sysdb_netgroup_ctx **triples;
+    struct sysdb_netgroup_ctx **entries;
     char *name;
     char *domain;
 };
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 2a47082..c2e800b 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -2424,7 +2424,7 @@ START_TEST(test_sysdb_add_netgroup_tuple)
     const char *username;
     const char *domainname;
     struct ldb_result *res;
-    struct sysdb_netgroup_ctx **triples;
+    struct sysdb_netgroup_ctx **entries;
 
     /* Setup */
     ret = setup_sysdb_tests(&test_ctx);
@@ -2455,23 +2455,23 @@ START_TEST(test_sysdb_add_netgroup_tuple)
                          &res);
     fail_unless(ret == EOK, "Failed to retrieve netgr information");
 
-    ret = sysdb_netgr_to_triples(test_ctx, res, &triples);
-    fail_unless(ret == EOK, "Failed to convert triples");
+    ret = sysdb_netgr_to_entries(test_ctx, res, &entries);
+    fail_unless(ret == EOK, "Failed to convert entries");
 
-    fail_unless(triples && triples[0] && !triples[1],
+    fail_unless(entries && entries[0] && !entries[1],
                 "Got more than one triple back");
 
-    fail_unless(strcmp(triples[0]->hostname, hostname) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.hostname, hostname) == 0,
                 "Got [%s], expected [%s] for hostname",
-                triples[0]->hostname, hostname);
+                entries[0]->value.triple.hostname, hostname);
 
-    fail_unless(strcmp(triples[0]->username, username) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.username, username) == 0,
                 "Got [%s], expected [%s] for username",
-                triples[0]->username, username);
+                entries[0]->value.triple.username, username);
 
-    fail_unless(strcmp(triples[0]->domainname, domainname) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.domainname, domainname) == 0,
                 "Got [%s], expected [%s] for domainname",
-                triples[0]->domainname, domainname);
+                entries[0]->value.triple.domainname, domainname);
 
     talloc_free(test_ctx);
 }
@@ -2486,7 +2486,7 @@ START_TEST(test_sysdb_remove_netgroup_tuple)
     const char *username;
     const char *domainname;
     struct ldb_result *res;
-    struct sysdb_netgroup_ctx **triples;
+    struct sysdb_netgroup_ctx **entries;
 
     /* Setup */
     ret = setup_sysdb_tests(&test_ctx);
@@ -2517,10 +2517,10 @@ START_TEST(test_sysdb_remove_netgroup_tuple)
                          &res);
     fail_unless(ret == EOK, "Failed to retrieve netgr information");
 
-    ret = sysdb_netgr_to_triples(test_ctx, res, &triples);
-    fail_unless(ret == EOK, "Failed to convert triples");
+    ret = sysdb_netgr_to_entries(test_ctx, res, &entries);
+    fail_unless(ret == EOK, "Failed to convert entries");
 
-    fail_unless(triples && !triples[0],"Found triples unexpectedly");
+    fail_unless(entries && !entries[0],"Found entries unexpectedly");
 
     talloc_free(test_ctx);
 }
@@ -2533,7 +2533,7 @@ START_TEST(test_sysdb_add_netgroup_member)
     const char *netgrname;
     const char *membername;
     struct ldb_result *res;
-    struct sysdb_netgroup_ctx **triples;
+    struct sysdb_netgroup_ctx **entries;
 
     char *hostname1;
     char *username1;
@@ -2574,37 +2574,37 @@ START_TEST(test_sysdb_add_netgroup_member)
                          &res);
     fail_unless(ret == EOK, "Failed to retrieve netgr information");
 
-    ret = sysdb_netgr_to_triples(test_ctx, res, &triples);
-    fail_unless(ret == EOK, "Failed to convert triples");
+    ret = sysdb_netgr_to_entries(test_ctx, res, &entries);
+    fail_unless(ret == EOK, "Failed to convert entries");
 
-    fail_if(!triples, "Received a NULL triple");
-    fail_if(!triples[0], "Did not get any responses");
-    fail_unless(triples[0] && triples[1] && !triples[2],
+    fail_if(!entries, "Received a NULL triple");
+    fail_if(!entries[0], "Did not get any responses");
+    fail_unless(entries[0] && entries[1] && !entries[2],
             "Did not get exactly two responses");
 
-    fail_unless(strcmp(triples[0]->hostname, hostname1) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.hostname, hostname1) == 0,
                 "Got [%s], expected [%s] for hostname",
-                triples[0]->hostname, hostname1);
+                entries[0]->value.triple.hostname, hostname1);
 
-    fail_unless(strcmp(triples[0]->username, username1) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.username, username1) == 0,
                 "Got [%s], expected [%s] for username",
-                triples[0]->username, username1);
+                entries[0]->value.triple.username, username1);
 
-    fail_unless(strcmp(triples[0]->domainname, domainname1) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.domainname, domainname1) == 0,
                 "Got [%s], expected [%s] for domainname",
-                triples[0]->domainname, domainname1);
+                entries[0]->value.triple.domainname, domainname1);
 
-    fail_unless(strcmp(triples[1]->hostname, hostname2) == 0,
+    fail_unless(strcmp(entries[1]->value.triple.hostname, hostname2) == 0,
                 "Got [%s], expected [%s] for hostname",
-                triples[0]->hostname, hostname2);
+                entries[0]->value.triple.hostname, hostname2);
 
-    fail_unless(strcmp(triples[1]->username, username2) == 0,
+    fail_unless(strcmp(entries[1]->value.triple.username, username2) == 0,
                 "Got [%s], expected [%s] for username",
-                triples[0]->username, username2);
+                entries[0]->value.triple.username, username2);
 
-    fail_unless(strcmp(triples[1]->domainname, domainname2) == 0,
+    fail_unless(strcmp(entries[1]->value.triple.domainname, domainname2) == 0,
                 "Got [%s], expected [%s] for domainname",
-                triples[0]->domainname, domainname2);
+                entries[0]->value.triple.domainname, domainname2);
 
     talloc_free(test_ctx);
 }
@@ -2617,7 +2617,7 @@ START_TEST(test_sysdb_remove_netgroup_member)
     const char *netgrname;
     const char *membername;
     struct ldb_result *res;
-    struct sysdb_netgroup_ctx **triples;
+    struct sysdb_netgroup_ctx **entries;
 
     char *hostname;
     char *username;
@@ -2649,25 +2649,25 @@ START_TEST(test_sysdb_remove_netgroup_member)
                          &res);
     fail_unless(ret == EOK, "Failed to retrieve netgr information");
 
-    ret = sysdb_netgr_to_triples(test_ctx, res, &triples);
-    fail_unless(ret == EOK, "Failed to convert triples");
+    ret = sysdb_netgr_to_entries(test_ctx, res, &entries);
+    fail_unless(ret == EOK, "Failed to convert entries");
 
-    fail_if(!triples, "Received a NULL triple");
-    fail_if(!triples[0], "Did not get any responses");
-    fail_unless(triples[0] && !triples[1],
+    fail_if(!entries, "Received a NULL triple");
+    fail_if(!entries[0], "Did not get any responses");
+    fail_unless(entries[0] && !entries[1],
                 "Did not get exactly one response");
 
-    fail_unless(strcmp(triples[0]->hostname, hostname) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.hostname, hostname) == 0,
                 "Got [%s], expected [%s] for hostname",
-                triples[0]->hostname, hostname);
+                entries[0]->value.triple.hostname, hostname);
 
-    fail_unless(strcmp(triples[0]->username, username) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.username, username) == 0,
                 "Got [%s], expected [%s] for username",
-                triples[0]->username, username);
+                entries[0]->value.triple.username, username);
 
-    fail_unless(strcmp(triples[0]->domainname, domainname) == 0,
+    fail_unless(strcmp(entries[0]->value.triple.domainname, domainname) == 0,
                 "Got [%s], expected [%s] for domainname",
-                triples[0]->domainname, domainname);
+                entries[0]->value.triple.domainname, domainname);
 
     talloc_free(test_ctx);
 }
-- 
1.7.2.3

-------------- next part --------------
From 0fc4a072783d8531d3fcc2072000351c880b9237 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 28 Sep 2010 15:43:31 +0200
Subject: [PATCH 7/9] Add infrastructure to LDAP provider for netgroup support

---
 src/config/etc/sssd.api.d/sssd-ipa.conf  |    7 ++
 src/config/etc/sssd.api.d/sssd-ldap.conf |    8 +++
 src/config/upgrade_config.py             |    7 ++
 src/man/sssd-ldap.5.xml                  |   91 ++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.c           |   35 +++++++++++-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/ldap/ldap_common.c         |   40 ++++++++++++-
 src/providers/ldap/sdap.h                |   13 ++++
 8 files changed, 199 insertions(+), 4 deletions(-)

diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index ac68171..88ff0f0 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -70,6 +70,13 @@ ldap_group_member = str, None, false
 ldap_group_uuid = str, None, false
 ldap_group_modify_timestamp = str, None, false
 ldap_force_upper_case_realm = bool, None, false
+ldap_netgroup_search_base = str, None, false
+ldap_netgroup_object_class = str, None, false
+ldap_netgroup_name = str, None, false
+ldap_netgroup_member = str, None, false
+ldap_netgroup_triple = str, None, false
+ldap_netgroup_uuid = str, None, false
+ldap_netgroup_modify_timestamp = str, None, false
 
 [provider/ipa/auth]
 krb5_ccachedir = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 7f0c360..38c75b2 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -63,6 +63,14 @@ ldap_group_member = str, None, false
 ldap_group_uuid = str, None, false
 ldap_group_modify_timestamp = str, None, false
 ldap_force_upper_case_realm = bool, None, false
+ldap_netgroup_search_base = str, None, false
+ldap_netgroup_object_class = str, None, false
+ldap_netgroup_name = str, None, false
+ldap_netgroup_member = str, None, false
+ldap_netgroup_triple = str, None, false
+ldap_netgroup_uuid = str, None, false
+ldap_netgroup_modify_timestamp = str, None, false
+
 
 [provider/ldap/auth]
 ldap_pwd_policy = str, None, false
diff --git a/src/config/upgrade_config.py b/src/config/upgrade_config.py
index ff22b48..62ffe52 100644
--- a/src/config/upgrade_config.py
+++ b/src/config/upgrade_config.py
@@ -193,6 +193,13 @@ class SSSDConfigFile(SSSDChangeConf):
                     'ldap_stale_time' : 'stale_time',
                     'ldap_opt_timeout' : 'opt_timeout',
                     'ldap_tls_reqcert' : 'tls_reqcert',
+                    'ldap_netgroup_search_base' : 'netgroupSearchBase',
+                    'ldap_netgroup_object_class' : 'netgroupObjectClass',
+                    'ldap_netgroup_name' : 'netgroupName',
+                    'ldap_netgroup_member' : 'netgroupMember',
+                    'ldap_netgroup_triple' : 'netgroupTriple',
+                    'ldap_netgroup_uuid' : 'netgroupUUID',
+                    'ldap_netgroup_modify_timestamp' : 'netgroupModifyTimestamp',
                    }
         krb5_kw = { 'krb5_kdcip' : 'krb5KDCIP',
                     'krb5_realm'  : 'krb5REALM',
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index b32096d..6442b8f 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -576,6 +576,97 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_netgroup_search_base (string)</term>
+                    <listitem>
+                        <para>
+                            An optional base DN to restrict netgroup searches
+                            to a specific subtree.
+                        </para>
+                        <para>
+                            Default: the value of
+                            <emphasis>ldap_search_base</emphasis>
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_object_class (string)</term>
+                    <listitem>
+                        <para>
+                            The object class of a netgroup entry in LDAP.
+                        </para>
+                        <para>
+                            Default: nisNetgroup
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_name (string)</term>
+                    <listitem>
+                        <para>
+                            The LDAP attribute that corresponds to
+                            the netgroup name.
+                        </para>
+                        <para>
+                            Default: cn
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_member (string)</term>
+                    <listitem>
+                        <para>
+                            The LDAP attribute that contains the names of
+                            the netgroup's members.
+                        </para>
+                        <para>
+                            Default: memberNisNetgroup
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_triple (string)</term>
+                    <listitem>
+                        <para>
+                            The LDAP attribute that contains the (host, user,
+                            domain) netgroup triples.
+                        </para>
+                        <para>
+                            Default: nisNetgroupTriple
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_uuid (string)</term>
+                    <listitem>
+                        <para>
+                            The LDAP attribute that contains the UUID/GUID of
+                            an LDAP netgroup object.
+                        </para>
+                        <para>
+                            Default: nsUniqueId
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>ldap_netgroup_modify_timestamp (string)</term>
+                    <listitem>
+                        <para>
+                            The LDAP attribute that contains timestamp of the
+                            last modification of the parent object.
+                        </para>
+                        <para>
+                            Default: modifyTimestamp
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ldap_search_timeout (integer)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 99b1508..70b2af8 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -72,7 +72,8 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
     { "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
     { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
-    { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 struct sdap_attr_map ipa_attr_map[] = {
@@ -116,6 +117,15 @@ struct sdap_attr_map ipa_group_map[] = {
     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
 };
 
+struct sdap_attr_map ipa_netgroup_map[] = {
+    { "ldap_netgroup_object_class", "nisNetgroup", SYSDB_NETGROUP_CLASS, NULL },
+    { "ldap_netgroup_name", "cn", SYSDB_NAME, NULL },
+    { "ldap_netgroup_member", "memberNisNetgroup", SYSDB_ORIG_NETGROUP_MEMBER, NULL },
+    { "ldap_netgroup_triple", "nisNetgroupTriple", SYSDB_NETGROUP_TRIPLE, NULL },
+    { "ldap_netgroup_uuid", "nsUniqueId", SYSDB_UUID, NULL },
+    { "ldap_netgroup_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+};
+
 struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_kdcip", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
@@ -333,6 +343,20 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
                                     SDAP_GROUP_SEARCH_BASE)));
     }
 
+    if (NULL == dp_opt_get_string(ipa_opts->id->basic,
+                                  SDAP_NETGROUP_SEARCH_BASE)) {
+        ret = dp_opt_set_string(ipa_opts->id->basic, SDAP_NETGROUP_SEARCH_BASE,
+                                dp_opt_get_string(ipa_opts->id->basic,
+                                                  SDAP_SEARCH_BASE));
+        if (ret != EOK) {
+            goto done;
+        }
+        DEBUG(6, ("Option %s set to %s\n",
+                  ipa_opts->id->basic[SDAP_NETGROUP_SEARCH_BASE].opt_name,
+                  dp_opt_get_string(ipa_opts->id->basic,
+                                    SDAP_NETGROUP_SEARCH_BASE)));
+    }
+
     ret = sdap_get_map(ipa_opts->id, cdb, conf_path,
                        ipa_attr_map,
                        SDAP_AT_GENERAL,
@@ -359,6 +383,15 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
         goto done;
     }
 
+    ret = sdap_get_map(ipa_opts->id,
+                       cdb, conf_path,
+                       ipa_netgroup_map,
+                       SDAP_OPTS_NETGROUP,
+                       &ipa_opts->id->netgroup_map);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = EOK;
     *_opts = ipa_opts->id;
 
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 3b40e41..1638f2a 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -35,7 +35,7 @@ struct ipa_service {
 /* the following defines are used to keep track of the options in the ldap
  * module, so that if they change and ipa is not updated correspondingly
  * this will trigger a runtime abort error */
-#define IPA_OPTS_BASIC_TEST 35
+#define IPA_OPTS_BASIC_TEST 36
 
 /* the following define is used to keep track of the options in the krb5
  * module, so that if they change and ipa is not updated correspondingly
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 14bdd28..168ec9e 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -67,7 +67,8 @@ struct dp_option default_basic_opts[] = {
     { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
     { "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
     { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
-    { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "ldap_netgroup_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
 };
 
 struct sdap_attr_map generic_attr_map[] = {
@@ -159,6 +160,16 @@ struct sdap_attr_map rfc2307bis_group_map[] = {
     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
 };
 
+struct sdap_attr_map netgroup_map[] = {
+    { "ldap_netgroup_object_class", "nisNetgroup", SYSDB_NETGROUP_CLASS, NULL },
+    { "ldap_netgroup_name", "cn", SYSDB_NAME, NULL },
+    { "ldap_netgroup_member", "memberNisNetgroup", SYSDB_ORIG_NETGROUP_MEMBER, NULL },
+    { "ldap_netgroup_triple", "nisNetgroupTriple", SYSDB_NETGROUP_TRIPLE, NULL },
+    /* FIXME: this is 389ds specific */
+    { "ldap_netgroup_uuid", "nsUniqueId", SYSDB_UUID, NULL },
+    { "ldap_netgroup_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+};
+
 int ldap_get_options(TALLOC_CTX *memctx,
                      struct confdb_ctx *cdb,
                      const char *conf_path,
@@ -167,6 +178,7 @@ int ldap_get_options(TALLOC_CTX *memctx,
     struct sdap_attr_map *default_attr_map;
     struct sdap_attr_map *default_user_map;
     struct sdap_attr_map *default_group_map;
+    struct sdap_attr_map *default_netgroup_map;
     struct sdap_options *opts;
     char *schema;
     const char *pwd_policy;
@@ -185,7 +197,7 @@ int ldap_get_options(TALLOC_CTX *memctx,
         goto done;
     }
 
-    /* set user/group search bases if they are not */
+    /* set user/group/netgroup search bases if they are not */
     if (NULL == dp_opt_get_string(opts->basic, SDAP_USER_SEARCH_BASE)) {
         ret = dp_opt_set_string(opts->basic, SDAP_USER_SEARCH_BASE,
                                 dp_opt_get_string(opts->basic,
@@ -210,6 +222,18 @@ int ldap_get_options(TALLOC_CTX *memctx,
                   dp_opt_get_string(opts->basic, SDAP_GROUP_SEARCH_BASE)));
     }
 
+    if (NULL == dp_opt_get_string(opts->basic, SDAP_NETGROUP_SEARCH_BASE)) {
+        ret = dp_opt_set_string(opts->basic, SDAP_NETGROUP_SEARCH_BASE,
+                                dp_opt_get_string(opts->basic,
+                                                  SDAP_SEARCH_BASE));
+        if (ret != EOK) {
+            goto done;
+        }
+        DEBUG(6, ("Option %s set to %s\n",
+                  opts->basic[SDAP_NETGROUP_SEARCH_BASE].opt_name,
+                  dp_opt_get_string(opts->basic, SDAP_NETGROUP_SEARCH_BASE)));
+    }
+
     pwd_policy = dp_opt_get_string(opts->basic, SDAP_PWD_POLICY);
     if (pwd_policy == NULL) {
         DEBUG(1, ("Missing password policy, this may not happen.\n"));
@@ -285,24 +309,28 @@ int ldap_get_options(TALLOC_CTX *memctx,
         default_attr_map = generic_attr_map;
         default_user_map = rfc2307_user_map;
         default_group_map = rfc2307_group_map;
+        default_netgroup_map = netgroup_map;
     } else
     if (strcasecmp(schema, "rfc2307bis") == 0) {
         opts->schema_type = SDAP_SCHEMA_RFC2307BIS;
         default_attr_map = generic_attr_map;
         default_user_map = rfc2307bis_user_map;
         default_group_map = rfc2307bis_group_map;
+        default_netgroup_map = netgroup_map;
     } else
     if (strcasecmp(schema, "IPA") == 0) {
         opts->schema_type = SDAP_SCHEMA_IPA_V1;
         default_attr_map = gen_ipa_attr_map;
         default_user_map = rfc2307bis_user_map;
         default_group_map = rfc2307bis_group_map;
+        default_netgroup_map = netgroup_map;
     } else
     if (strcasecmp(schema, "AD") == 0) {
         opts->schema_type = SDAP_SCHEMA_AD;
         default_attr_map = gen_ad_attr_map;
         default_user_map = rfc2307bis_user_map;
         default_group_map = rfc2307bis_group_map;
+        default_netgroup_map = netgroup_map;
     } else {
         DEBUG(0, ("Unrecognized schema type: %s\n", schema));
         ret = EINVAL;
@@ -333,6 +361,14 @@ int ldap_get_options(TALLOC_CTX *memctx,
         goto done;
     }
 
+    ret = sdap_get_map(opts, cdb, conf_path,
+                       default_netgroup_map,
+                       SDAP_OPTS_NETGROUP,
+                       &opts->netgroup_map);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = EOK;
     *_opts = opts;
 
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 4426dac..75df8b0 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -174,6 +174,7 @@ enum sdap_basic_opt {
     SDAP_DNS_SERVICE_NAME,
     SDAP_KRB5_TICKET_LIFETIME,
     SDAP_ACCESS_FILTER,
+    SDAP_NETGROUP_SEARCH_BASE,
 
     SDAP_OPTS_BASIC /* opts counter */
 };
@@ -231,6 +232,17 @@ enum sdap_group_attrs {
     SDAP_OPTS_GROUP /* attrs counter */
 };
 
+enum sdap_netgroup_attrs {
+    SDAP_OC_NETGROUP = 0,
+    SDAP_AT_NETGROUP_NAME,
+    SDAP_AT_NETGROUP_MEMBER,
+    SDAP_AT_NETGROUP_TRIPLE,
+    SDAP_AT_NETGROUP_UUID,
+    SDAP_AT_NETGROUP_MODSTAMP,
+
+    SDAP_OPTS_NETGROUP /* attrs counter */
+};
+
 struct sdap_attr_map {
     const char *opt_name;
     const char *def_name;
@@ -243,6 +255,7 @@ struct sdap_options {
     struct sdap_attr_map *gen_map;
     struct sdap_attr_map *user_map;
     struct sdap_attr_map *group_map;
+    struct sdap_attr_map *netgroup_map;
 
     /* supported schema types */
     enum schema_type {
-- 
1.7.2.3

-------------- next part --------------
From 76f7b26a7181fb19a1e0a43d4f08c5769a9cab99 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 5 Oct 2010 13:51:05 +0200
Subject: [PATCH 8/9] Implement netgroup support for LDAP provider

---
 Makefile.am                               |    4 +
 src/db/sysdb.h                            |    3 +
 src/providers/ldap/ldap_common.h          |    6 +
 src/providers/ldap/ldap_id.c              |   26 +
 src/providers/ldap/ldap_id_netgroup.c     |  224 +++++++++
 src/providers/ldap/sdap_async.h           |   14 +-
 src/providers/ldap/sdap_async_netgroups.c |  713 +++++++++++++++++++++++++++++
 7 files changed, 989 insertions(+), 1 deletions(-)
 create mode 100644 src/providers/ldap/ldap_id_netgroup.c
 create mode 100644 src/providers/ldap/sdap_async_netgroups.c

diff --git a/Makefile.am b/Makefile.am
index 0dc53d3..436ddd5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -765,6 +765,7 @@ libsss_ldap_la_SOURCES = \
     src/providers/ldap/ldap_id.c \
     src/providers/ldap/ldap_id_enum.c \
     src/providers/ldap/ldap_id_cleanup.c \
+    src/providers/ldap/ldap_id_netgroup.c \
     src/providers/ldap/sdap_access.c \
     src/providers/ldap/ldap_auth.c \
     src/providers/ldap/ldap_init.c \
@@ -772,6 +773,7 @@ libsss_ldap_la_SOURCES = \
     src/providers/ldap/sdap_async.c \
     src/providers/ldap/sdap_async_accounts.c \
     src/providers/ldap/sdap_async_connection.c \
+    src/providers/ldap/sdap_async_netgroups.c \
     src/providers/ldap/sdap_child_helpers.c \
     src/providers/ldap/sdap_fd_events.c \
     src/providers/ldap/sdap_id_op.c \
@@ -848,11 +850,13 @@ libsss_ipa_la_SOURCES = \
     src/providers/ldap/ldap_id.c \
     src/providers/ldap/ldap_id_enum.c \
     src/providers/ldap/ldap_id_cleanup.c \
+    src/providers/ldap/ldap_id_netgroup.c \
     src/providers/ldap/ldap_auth.c \
     src/providers/ldap/ldap_common.c \
     src/providers/ldap/sdap_async.c \
     src/providers/ldap/sdap_async_accounts.c \
     src/providers/ldap/sdap_async_connection.c \
+    src/providers/ldap/sdap_async_netgroups.c \
     src/providers/ldap/sdap_child_helpers.c \
     src/providers/ldap/sdap_fd_events.c \
     src/providers/ldap/sdap_id_op.c \
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index a1d6c91..60c0ae8 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -79,6 +79,8 @@
 #define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp"
 
 #define SYSDB_NETGROUP_TRIPLE "netgroupTriple"
+#define SYSDB_ORIG_NETGROUP_MEMBER "originalMemberNisNetgroup"
+#define SYSDB_NETGROUP_MEMBER "memberNisNetgroup"
 #define SYSDB_DESCRIPTION   "description"
 
 #define SYSDB_CACHEDPWD "cachedPassword"
@@ -142,6 +144,7 @@
                            NULL}
 
 #define SYSDB_NETGR_ATTRS {SYSDB_NAME, SYSDB_NETGROUP_TRIPLE, \
+                           SYSDB_NETGROUP_MEMBER, \
                            SYSDB_DEFAULT_ATTRS, \
                            NULL}
 
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 2a0e976..3ebd481 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -113,6 +113,12 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    int attrs_type);
 int groups_get_recv(struct tevent_req *req, int *dp_error_out);
 
+
+struct tevent_req *netgroup_get_send(TALLOC_CTX *memctx,
+                                                      struct tevent_context *ev,
+                                                      struct sdap_id_ctx *ctx,
+                                                      const char *name);
+int netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
 /* setup child logging */
 int setup_child(struct sdap_id_ctx *ctx);
 
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 0c90773..be4642a 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -647,6 +647,7 @@ int groups_by_user_recv(struct tevent_req *req, int *dp_error_out)
 static void sdap_account_info_users_done(struct tevent_req *req);
 static void sdap_account_info_groups_done(struct tevent_req *req);
 static void sdap_account_info_initgr_done(struct tevent_req *req);
+static void sdap_account_info_netgroups_done(struct tevent_req *req);
 
 void sdap_account_info_handler(struct be_req *breq)
 {
@@ -728,6 +729,21 @@ void sdap_account_info_handler(struct be_req *breq)
 
         break;
 
+    case BE_REQ_NETGROUP:
+        if (ar->filter_type != BE_FILTER_NAME) {
+            ret = EINVAL;
+            err = "Invalid filter type";
+            break;
+        }
+
+        req = netgroup_get_send(breq, breq->be_ctx->ev, ctx, ar->filter_value);
+        if (!req) {
+            return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
+        }
+
+        tevent_req_set_callback(req, sdap_account_info_netgroups_done, breq);
+        break;
+
     default: /*fail*/
         ret = EINVAL;
         err = "Invalid request type";
@@ -793,3 +809,13 @@ static void sdap_account_info_initgr_done(struct tevent_req *req)
     sdap_account_info_complete(breq, dp_error, ret, "Init Groups Failed");
 }
 
+static void sdap_account_info_netgroups_done(struct tevent_req *req)
+{
+    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+    int ret, dp_error;
+
+    ret = netgroup_get_recv(req, &dp_error);
+    talloc_zfree(req);
+
+    sdap_account_info_complete(breq, dp_error, ret, "Netgroup lookup failed");
+}
diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c
new file mode 100644
index 0000000..c82ccc4
--- /dev/null
+++ b/src/providers/ldap/ldap_id_netgroup.c
@@ -0,0 +1,224 @@
+/*
+    SSSD
+
+    LDAP Identity Backend Module - Netgroup support
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async.h"
+
+
+struct netgroup_get_state {
+    struct tevent_context *ev;
+    struct sdap_id_ctx *ctx;
+    struct sdap_id_op *op;
+    struct sysdb_ctx *sysdb;
+    struct sss_domain_info *domain;
+
+    const char *name;
+
+    char *filter;
+    const char **attrs;
+
+    size_t count;
+    struct sysdb_attrs **netgroups;
+
+    int dp_error;
+};
+
+static int netgroup_get_retry(struct tevent_req *req);
+static void netgroup_get_connect_done(struct tevent_req *subreq);
+static void netgroup_get_done(struct tevent_req *subreq);
+
+struct tevent_req *netgroup_get_send(TALLOC_CTX *memctx,
+                                     struct tevent_context *ev,
+                                     struct sdap_id_ctx *ctx,
+                                     const char *name)
+{
+    struct tevent_req *req;
+    struct netgroup_get_state *state;
+    int ret;
+
+    req = tevent_req_create(memctx, &state, struct netgroup_get_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->ctx = ctx;
+    state->dp_error = DP_ERR_FATAL;
+
+    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    if (!state->op) {
+        DEBUG(2, ("sdap_id_op_create failed\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    state->sysdb = ctx->be->sysdb;
+    state->domain = state->ctx->be->domain;
+    state->name = name;
+
+    state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+                            ctx->opts->netgroup_map[SDAP_AT_NETGROUP_NAME].name,
+                            name,
+                            ctx->opts->netgroup_map[SDAP_OC_NETGROUP].name);
+    if (!state->filter) {
+        DEBUG(2, ("Failed to build filter\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    ret = build_attrs_from_map(state, ctx->opts->netgroup_map,
+                               SDAP_OPTS_NETGROUP, &state->attrs);
+    if (ret != EOK) goto fail;
+
+    ret = netgroup_get_retry(req);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int netgroup_get_retry(struct tevent_req *req)
+{
+    struct netgroup_get_state *state = tevent_req_data(req,
+                                                    struct netgroup_get_state);
+    struct tevent_req *subreq;
+    int ret = EOK;
+
+    subreq = sdap_id_op_connect_send(state->op, state, &ret);
+    if (!subreq) {
+        return ret;
+    }
+
+    tevent_req_set_callback(subreq, netgroup_get_connect_done, req);
+    return EOK;
+}
+
+static void netgroup_get_connect_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct netgroup_get_state *state = tevent_req_data(req,
+                                                    struct netgroup_get_state);
+    int dp_error = DP_ERR_FATAL;
+    int ret;
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        state->dp_error = dp_error;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    subreq = sdap_get_netgroups_send(state, state->ev,
+                                     state->domain, state->sysdb,
+                                     state->ctx->opts,
+                                     sdap_id_op_handle(state->op),
+                                     state->attrs, state->filter);
+    if (!subreq) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, netgroup_get_done, req);
+
+    return;
+}
+
+static void netgroup_get_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct netgroup_get_state *state = tevent_req_data(req,
+                                                    struct netgroup_get_state);
+    int dp_error = DP_ERR_FATAL;
+    int ret;
+
+    ret = sdap_get_netgroups_recv(subreq, state, NULL, &state->count,
+                                  &state->netgroups);
+    talloc_zfree(subreq);
+    ret = sdap_id_op_done(state->op, ret, &dp_error);
+
+    if (dp_error == DP_ERR_OK && ret != EOK) {
+        /* retry */
+        ret = netgroup_get_retry(req);
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        return;
+    }
+
+    if (ret && ret != ENOENT) {
+        state->dp_error = dp_error;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (ret == EOK && state->count > 1) {
+        DEBUG(1, ("Found more than one netgroup with the name [%s].\n",
+                  state->name));
+        tevent_req_error(req, EINVAL);
+        return;
+    }
+
+    if (ret == ENOENT) {
+        ret = sysdb_delete_netgroup(state->sysdb, state->domain, state->name);
+        if (ret) {
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        state->dp_error = DP_ERR_OK;
+        tevent_req_error(req, ENOENT);
+        return;
+    }
+
+    state->dp_error = DP_ERR_OK;
+    tevent_req_done(req);
+    return;
+}
+
+int netgroup_get_recv(struct tevent_req *req, int *dp_error_out)
+{
+    struct netgroup_get_state *state = tevent_req_data(req,
+                                                    struct netgroup_get_state);
+
+    if (dp_error_out) {
+        *dp_error_out = state->dp_error;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index e5a60da..182042a 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -59,6 +59,19 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
 int sdap_get_groups_recv(struct tevent_req *req,
                          TALLOC_CTX *mem_ctx, char **timestamp);
 
+struct tevent_req *sdap_get_netgroups_send(TALLOC_CTX *memctx,
+                                           struct tevent_context *ev,
+                                           struct sss_domain_info *dom,
+                                           struct sysdb_ctx *sysdb,
+                                           struct sdap_options *opts,
+                                           struct sdap_handle *sh,
+                                           const char **attrs,
+                                           const char *wildcard);
+int sdap_get_netgroups_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx, char **timestamp,
+                            size_t *reply_count,
+                            struct sysdb_attrs ***reply);
+
 struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_handle *sh,
@@ -132,5 +145,4 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
 int sdap_get_generic_recv(struct tevent_req *req,
                          TALLOC_CTX *mem_ctx, size_t *reply_count,
                          struct sysdb_attrs ***reply_list);
-
 #endif /* _SDAP_ASYNC_H_ */
diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
new file mode 100644
index 0000000..5f6ad05
--- /dev/null
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -0,0 +1,713 @@
+/*
+    SSSD
+
+    Async LDAP Helper routines for netgroups
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/sdap_async_private.h"
+
+static bool is_dn(const char *str)
+{
+    int ret;
+    LDAPDN dn;
+
+    ret = ldap_str2dn(str, &dn, LDAP_DN_FORMAT_LDAPV3);
+    return (ret == LDAP_SUCCESS ? true : false);
+}
+
+static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
+                                  struct sysdb_ctx *ctx,
+                                  struct sdap_options *opts,
+                                  struct sss_domain_info *dom,
+                                  struct sysdb_attrs *attrs,
+                                  char **_timestamp)
+{
+    struct ldb_message_element *el;
+    struct sysdb_attrs *netgroup_attrs;
+    const char *name = NULL;
+    int ret;
+    char *timestamp = NULL;
+    size_t c;
+
+    ret = sysdb_attrs_get_el(attrs,
+                             opts->netgroup_map[SDAP_AT_NETGROUP_NAME].sys_name,
+                             &el);
+    if (ret) goto fail;
+    if (el->num_values == 0) {
+        ret = EINVAL;
+        goto fail;
+    }
+    name = (const char *)el->values[0].data;
+
+    netgroup_attrs = sysdb_new_attrs(memctx);
+    if (!netgroup_attrs) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
+    if (ret) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("Original DN is not available for [%s].\n", name));
+    } else {
+        DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
+                  el->values[0].data, name));
+        ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_ORIG_DN,
+                                     (const char *)el->values[0].data);
+        if (ret) {
+            goto fail;
+        }
+    }
+
+    ret = sysdb_attrs_get_el(attrs,
+                         opts->netgroup_map[SDAP_AT_NETGROUP_MODSTAMP].sys_name,
+                         &el);
+    if (ret) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n",
+                  name));
+    } else {
+        ret = sysdb_attrs_add_string(netgroup_attrs,
+                         opts->netgroup_map[SDAP_AT_NETGROUP_MODSTAMP].sys_name,
+                         (const char*)el->values[0].data);
+        if (ret) {
+            goto fail;
+        }
+        timestamp = talloc_strdup(memctx, (const char*)el->values[0].data);
+        if (!timestamp) {
+            ret = ENOMEM;
+            goto fail;
+        }
+    }
+
+    ret = sysdb_attrs_get_el(attrs,
+                           opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name,
+                           &el);
+    if (ret) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("No netgroup triples for netgroup [%s].\n", name));
+    } else {
+        for(c = 0; c < el->num_values; c++) {
+            ret = sysdb_attrs_add_string(netgroup_attrs,
+                           opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name,
+                            (const char*)el->values[c].data);
+            if (ret) {
+                goto fail;
+            }
+        }
+    }
+
+    ret = sysdb_attrs_get_el(attrs,
+                       opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name,
+                       &el);
+    if (ret != EOK) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("No original members for netgroup [%s]\n", name));
+
+    } else {
+        DEBUG(7, ("Adding original members to netgroup [%s]\n", name));
+        for(c = 0; c < el->num_values; c++) {
+            ret = sysdb_attrs_add_string(netgroup_attrs,
+                       opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name,
+                       (const char*)el->values[c].data);
+            if (ret) {
+                goto fail;
+            }
+        }
+    }
+
+
+    ret = sysdb_attrs_get_el(attrs, SYSDB_NETGROUP_MEMBER, &el);
+    if (ret != EOK) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("No members for netgroup [%s]\n", name));
+
+    } else {
+        DEBUG(7, ("Adding members to netgroup [%s]\n", name));
+        for(c = 0; c < el->num_values; c++) {
+            ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_NETGROUP_MEMBER,
+                                         (const char*)el->values[c].data);
+            if (ret) {
+                goto fail;
+            }
+        }
+    }
+
+    DEBUG(6, ("Storing info for netgroup %s\n", name));
+
+    ret = sysdb_add_netgroup(ctx, dom, name, NULL, netgroup_attrs,
+                             dp_opt_get_int(opts->basic,
+                                            SDAP_ENTRY_CACHE_TIMEOUT));
+    if (ret) goto fail;
+
+    if (_timestamp) {
+        *_timestamp = timestamp;
+    }
+
+    return EOK;
+
+fail:
+    DEBUG(2, ("Failed to save netgroup %s\n", name));
+    return ret;
+}
+
+struct dn_item {
+    const char *dn;
+    struct sysdb_attrs *netgroup;
+    char *cn;
+    struct dn_item *next;
+    struct dn_item *prev;
+};
+
+static errno_t update_dn_list(struct dn_item *dn_list, const size_t count,
+                              struct ldb_message **res, bool *all_resolved)
+{
+    struct dn_item *dn_item;
+    size_t c;
+    const char *dn;
+    const char *cn;
+    bool not_resolved = false;
+
+    *all_resolved = false;
+
+    DLIST_FOR_EACH(dn_item, dn_list) {
+        if (dn_item->cn != NULL) {
+            continue;
+        }
+
+        for(c = 0; c < count; c++) {
+            dn = ldb_msg_find_attr_as_string(res[c], SYSDB_ORIG_DN, NULL);
+            if (dn == NULL) {
+                DEBUG(1, ("Missing original DN.\n"));
+                return EINVAL;
+            }
+            if (strcmp(dn, dn_item->dn) == 0) {
+                DEBUG(9, ("Found matching entry for [%s].\n", dn_item->dn));
+                cn = ldb_msg_find_attr_as_string(res[c], SYSDB_NAME, NULL);
+                if (cn == NULL) {
+                    DEBUG(1, ("Missing name.\n"));
+                    return EINVAL;
+                }
+                dn_item->cn = talloc_strdup(dn_item, cn);
+                break;
+            }
+        }
+
+        not_resolved = true;
+    }
+
+    *all_resolved = !not_resolved;
+
+    return EOK;
+}
+
+struct netgr_translate_members_state {
+    struct tevent_context *ev;
+    struct sdap_options *opts;
+    struct sdap_handle *sh;
+    struct sss_domain_info *dom;
+    struct sysdb_ctx *sysdb;
+
+    struct sysdb_attrs **netgroups;
+    size_t count;
+    struct dn_item *dn_list;
+    struct dn_item *dn_item;
+    struct dn_item *dn_idx;
+};
+
+static errno_t netgr_translate_members_ldap_step(struct tevent_req *req);
+static void netgr_translate_members_ldap_done(struct tevent_req *subreq);
+
+struct tevent_req *netgr_translate_members_send(TALLOC_CTX *memctx,
+                                                struct tevent_context *ev,
+                                                struct sdap_options *opts,
+                                                struct sdap_handle *sh,
+                                                struct sss_domain_info *dom,
+                                                struct sysdb_ctx *sysdb,
+                                                const size_t count,
+                                                struct sysdb_attrs **netgroups)
+{
+    struct tevent_req *req;
+    struct netgr_translate_members_state *state;
+    size_t c;
+    size_t mc;
+    const char **member_list;
+    size_t sysdb_count;
+    int ret;
+    struct ldb_message **sysdb_res;
+    struct dn_item *dn_item;
+    char *dn_filter;
+    char *sysdb_filter;
+    struct ldb_dn *netgr_basedn;
+    bool all_resolved;
+    const char *cn_attr[] = { SYSDB_NAME, SYSDB_ORIG_DN, NULL };
+
+    req = tevent_req_create(memctx, &state,
+                            struct netgr_translate_members_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->opts = opts;
+    state->dom = dom;
+    state->sh = sh;
+    state->sysdb = sysdb;
+    state->netgroups = netgroups;
+    state->count = count;
+    state->dn_list = NULL;
+    state->dn_item = NULL;
+    state->dn_idx = NULL;
+
+    for (c = 0; c < count; c++) {
+        ret = sysdb_attrs_get_string_array(netgroups[c],
+                                           SYSDB_ORIG_NETGROUP_MEMBER, state,
+                                           &member_list);
+        if (ret != EOK) {
+            DEBUG(7, ("Missing netgroup members.\n"));
+            continue;
+        }
+
+        for (mc = 0; member_list[mc] != NULL; mc++) {
+            if (is_dn(member_list[mc])) {
+                dn_item = talloc_zero(state, struct dn_item);
+                if (dn_item == NULL) {
+                    DEBUG(1, ("talloc failed.\n"));
+                    ret = ENOMEM;
+                    goto fail;
+                }
+
+                DEBUG(9, ("Adding [%s] to DN list.\n", member_list[mc]));
+                dn_item->netgroup = netgroups[c];
+                dn_item->dn = member_list[mc];
+                DLIST_ADD(state->dn_list, dn_item);
+            } else {
+                ret = sysdb_attrs_add_string(netgroups[c], SYSDB_NETGROUP_MEMBER,
+                                             member_list[mc]);
+                if (ret != EOK) {
+                    DEBUG(1, ("sysdb_attrs_add_string failed.\n"));
+                    goto fail;
+                }
+            }
+        }
+    }
+
+    if (state->dn_list == NULL) {
+        DEBUG(9, ("No DNs found among netgroup members.\n"));
+        tevent_req_done(req);
+        tevent_req_post(req, ev);
+        return req;
+    }
+
+    dn_filter = talloc_strdup(state, "(|");
+    if (dn_filter == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        ret = ENOMEM;;
+        goto fail;
+    }
+
+    DLIST_FOR_EACH(dn_item, state->dn_list) {
+            dn_filter = talloc_asprintf_append(dn_filter, "(%s=%s)",
+                                               SYSDB_ORIG_DN, dn_item->dn);
+            if (dn_filter == NULL) {
+                DEBUG(1, ("talloc_asprintf_append failed.\n"));
+                ret = ENOMEM;
+                goto fail;
+            }
+    }
+
+    dn_filter = talloc_asprintf_append(dn_filter, ")");
+    if (dn_filter == NULL) {
+        DEBUG(1, ("talloc_asprintf_append failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    sysdb_filter = talloc_asprintf(state, "(&(%s)%s)", SYSDB_NC, dn_filter);
+    if (sysdb_filter == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    netgr_basedn = sysdb_netgroup_base_dn(sysdb, state, dom->name);
+    if (netgr_basedn == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    ret = sysdb_search_entry(state, sysdb, netgr_basedn, LDB_SCOPE_BASE,
+                             sysdb_filter, cn_attr, &sysdb_count, &sysdb_res);
+    talloc_zfree(netgr_basedn);
+    talloc_zfree(sysdb_filter);
+    if (ret != EOK && ret != ENOENT) {
+        DEBUG(1, ("sysdb_search_entry failed.\n"));
+        goto fail;
+    }
+
+    if (ret == EOK) {
+        ret = update_dn_list(state->dn_list, sysdb_count, sysdb_res,
+                             &all_resolved);
+        if (ret != EOK) {
+            DEBUG(1, ("update_dn_list failed.\n"));
+            goto fail;
+        }
+
+        if (all_resolved) {
+            DLIST_FOR_EACH(dn_item, state->dn_list) {
+                    ret = sysdb_attrs_add_string(dn_item->netgroup,
+                                                 SYSDB_NETGROUP_MEMBER,
+                                                 dn_item->cn);
+                    if (ret != EOK) {
+                        DEBUG(1, ("sysdb_attrs_add_string failed.\n"));
+                        goto fail;
+                    }
+            }
+
+            tevent_req_done(req);
+            tevent_req_post(req, ev);
+            return req;
+        }
+    }
+
+    state->dn_idx = state->dn_list;
+    ret = netgr_translate_members_ldap_step(req);
+    if (ret != EOK && ret != EAGAIN) {
+        DEBUG(1, ("netgr_translate_members_ldap_step failed.\n"));
+        goto fail;
+    }
+
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, ev);
+    }
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+/* netgr_translate_members_ldap_step() returns
+ *   EOK: if everthing is translated, the caller can call tevent_req_done
+ *   EAGAIN: if there are still members waiting to be translated, the caller
+ *   should return to the mainloop
+ *   Exyz: every other return code indicates an error and tevent_req_error
+ *   should be called
+ */
+static errno_t netgr_translate_members_ldap_step(struct tevent_req *req)
+{
+    struct netgr_translate_members_state *state = tevent_req_data(req,
+                                          struct netgr_translate_members_state);
+    const char **cn_attr;
+    struct tevent_req *subreq;
+    int ret;
+
+    DLIST_FOR_EACH(state->dn_item, state->dn_idx) {
+        if (state->dn_item->cn == NULL) {
+            break;
+        }
+    }
+    if (state->dn_item == NULL) {
+        DLIST_FOR_EACH(state->dn_item, state->dn_list) {
+                ret = sysdb_attrs_add_string(state->dn_item->netgroup,
+                                             SYSDB_NETGROUP_MEMBER,
+                                             state->dn_item->cn);
+                if (ret != EOK) {
+                    DEBUG(1, ("sysdb_attrs_add_string failed.\n"));
+                    tevent_req_error(req, ret);
+                    return ret;
+                }
+        }
+
+        return EOK;
+    }
+
+    cn_attr = talloc_array(state, const char *, 3);
+    if (cn_attr == NULL) {
+        DEBUG(1, ("talloc_array failed.\n"));
+        return ENOMEM;
+    }
+    cn_attr[0] = state->opts->netgroup_map[SDAP_AT_NETGROUP_NAME].name;
+    cn_attr[1] = "objectclass";
+    cn_attr[2] = NULL;
+
+    DEBUG(9, ("LDAP base search for [%s].\n", state->dn_item->dn));
+    subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+                                   state->dn_item->dn, LDAP_SCOPE_BASE, NULL,
+                                   cn_attr, state->opts->netgroup_map,
+                                   SDAP_OPTS_NETGROUP);
+    if (!subreq) {
+        DEBUG(1, ("sdap_get_generic_send failed.\n"));
+        return ENOMEM;
+    }
+    talloc_steal(subreq, cn_attr);
+
+    tevent_req_set_callback(subreq, netgr_translate_members_ldap_done, req);
+    return EAGAIN;
+}
+
+static void netgr_translate_members_ldap_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct netgr_translate_members_state *state = tevent_req_data(req,
+                                          struct netgr_translate_members_state);
+    int ret;
+    size_t count;
+    struct sysdb_attrs **netgroups;
+    const char *str;
+
+    ret = sdap_get_generic_recv(subreq, state, &count, &netgroups);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(1, ("sdap_get_generic request failed.\n"));
+        goto fail;
+    }
+
+    switch (count) {
+        case 0:
+            DEBUG(0, ("sdap_get_generic_recv found no entry for [%s].\n",
+                      state->dn_item->dn));
+            break;
+        case 1:
+            ret = sysdb_attrs_get_string(netgroups[0], SYSDB_NAME, &str);
+            if (ret != EOK) {
+                DEBUG(1, ("sysdb_attrs_add_string failed.\n"));
+                break;
+            }
+            state->dn_item->cn = talloc_strdup(state->dn_item, str);
+            if (state->dn_item->cn == NULL) {
+                DEBUG(1, ("talloc_strdup failed.\n"));
+            }
+            break;
+        default:
+            DEBUG(1, ("Unexpected number of results [%d] for base search.\n",
+                      count));
+    }
+
+    if (state->dn_item->cn == NULL) {
+        DEBUG(1, ("Failed to resolve netgroup name for DN [%s], using DN.\n",
+                  state->dn_item->dn));
+        state->dn_item->cn = talloc_strdup(state->dn_item, state->dn_item->dn);
+    }
+
+    state->dn_idx = state->dn_item->next;
+    ret = netgr_translate_members_ldap_step(req);
+    if (ret != EOK && ret != EAGAIN) {
+        DEBUG(1, ("netgr_translate_members_ldap_step failed.\n"));
+        goto fail;
+    }
+
+    if (ret == EOK) {
+        tevent_req_done(req);
+    }
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static errno_t netgroup_translate_ldap_members_recv(struct tevent_req *req,
+                                                TALLOC_CTX *mem_ctx,
+                                                size_t *count,
+                                                struct sysdb_attrs ***netgroups)
+{
+    struct netgr_translate_members_state *state = tevent_req_data(req,
+                                          struct netgr_translate_members_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *count = state->count;
+    *netgroups = talloc_steal(mem_ctx, state->netgroups);
+
+    return EOK;
+}
+
+/* ==Search-Netgroups-with-filter============================================ */
+
+struct sdap_get_netgroups_state {
+    struct tevent_context *ev;
+    struct sdap_options *opts;
+    struct sdap_handle *sh;
+    struct sss_domain_info *dom;
+    struct sysdb_ctx *sysdb;
+    const char **attrs;
+    const char *filter;
+
+    char *higher_timestamp;
+    struct sysdb_attrs **netgroups;
+    size_t count;
+};
+
+static void sdap_get_netgroups_process(struct tevent_req *subreq);
+static void netgr_translate_members_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_netgroups_send(TALLOC_CTX *memctx,
+                                           struct tevent_context *ev,
+                                           struct sss_domain_info *dom,
+                                           struct sysdb_ctx *sysdb,
+                                           struct sdap_options *opts,
+                                           struct sdap_handle *sh,
+                                           const char **attrs,
+                                           const char *filter)
+{
+    struct tevent_req *req, *subreq;
+    struct sdap_get_netgroups_state *state;
+
+    req = tevent_req_create(memctx, &state, struct sdap_get_netgroups_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->opts = opts;
+    state->dom = dom;
+    state->sh = sh;
+    state->sysdb = sysdb;
+    state->filter = filter;
+    state->attrs = attrs;
+    state->higher_timestamp = NULL;
+    state->netgroups =  NULL;
+    state->count = 0;
+
+    subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+                                   dp_opt_get_string(state->opts->basic,
+                                                     SDAP_SEARCH_BASE),
+                                   LDAP_SCOPE_SUBTREE,
+                                   state->filter, state->attrs,
+                                   state->opts->netgroup_map,
+                                   SDAP_OPTS_NETGROUP);
+    if (!subreq) {
+        talloc_zfree(req);
+        return NULL;
+    }
+    tevent_req_set_callback(subreq, sdap_get_netgroups_process, req);
+
+    return req;
+}
+
+static void sdap_get_netgroups_process(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_get_netgroups_state *state = tevent_req_data(req,
+                                               struct sdap_get_netgroups_state);
+    int ret;
+
+    ret = sdap_get_generic_recv(subreq, state,
+                                &state->count, &state->netgroups);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(6, ("Search for netgroups, returned %d results.\n", state->count));
+
+    if (state->count == 0) {
+        tevent_req_error(req, ENOENT);
+        return;
+    }
+
+    subreq = netgr_translate_members_send(state, state->ev, state->opts,
+                                          state->sh, state->dom, state->sysdb,
+                                          state->count, state->netgroups);
+    if (!subreq) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, netgr_translate_members_done, req);
+
+    return;
+
+}
+
+static void netgr_translate_members_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_get_netgroups_state *state = tevent_req_data(req,
+                                               struct sdap_get_netgroups_state);
+    int ret;
+    size_t c;
+
+    ret = netgroup_translate_ldap_members_recv(subreq, state, &state->count,
+                                               &state->netgroups);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    for (c = 0; c < state->count; c++) {
+        ret = sdap_save_netgroup(state, state->sysdb,
+                                 state->opts, state->dom,
+                                 state->netgroups[c],
+                                 &state->higher_timestamp);
+        if (ret) {
+            DEBUG(2, ("Failed to store netgroups.\n"));
+            tevent_req_error(req, ret);
+            return;
+        }
+    }
+
+    DEBUG(9, ("Saving %d Netgroups - Done\n", state->count));
+
+    tevent_req_done(req);
+}
+
+int sdap_get_netgroups_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx, char **timestamp,
+                            size_t *reply_count,
+                            struct sysdb_attrs ***reply)
+{
+    struct sdap_get_netgroups_state *state = tevent_req_data(req,
+                                               struct sdap_get_netgroups_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (timestamp) {
+        *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+    }
+
+    if (reply_count) {
+        *reply_count = state->count;
+    }
+
+    if (reply) {
+        *reply = talloc_steal(mem_ctx, state->netgroups);
+    }
+
+    return EOK;
+}
-- 
1.7.2.3

-------------- next part --------------
From fdb1b6b9067528bb0c2a76c52258e4b2f0dd0555 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Oct 2010 13:15:52 +0200
Subject: [PATCH 9/9] Avoid a global variable in netgroup client.

The structure which is used to store the result also provides elements to
store a context for the netgroup enumeration call.
---
 src/sss_client/nss_compat.h   |    2 +-
 src/sss_client/nss_netgroup.c |   62 ++++++++++++++++------------------------
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/src/sss_client/nss_compat.h b/src/sss_client/nss_compat.h
index 37871c2..97fbfeb 100644
--- a/src/sss_client/nss_compat.h
+++ b/src/sss_client/nss_compat.h
@@ -57,7 +57,7 @@ struct __netgrent
   {
     char *cursor;
     unsigned long int position;
-  } insertedname; /* added name to union to avoid warning */
+  } idx;  /* added name to union to avoid warning */
   int first;
   struct name_list *known_groups;
   struct name_list *needed_groups;
diff --git a/src/sss_client/nss_netgroup.c b/src/sss_client/nss_netgroup.c
index acc1897..c47f3a6 100644
--- a/src/sss_client/nss_netgroup.c
+++ b/src/sss_client/nss_netgroup.c
@@ -35,12 +35,12 @@
 
 #define MAX_NETGR_NAME_LENGTH 2048
 
-static struct sss_nss_getnetgrent_data {
-    char *name;
-    size_t len;
-    size_t ptr;
-    uint8_t *data;
-} sss_nss_getnetgrent_data;
+#define CLEAR_NETGRENT_DATA(netgrent) do { \
+        free(netgrent->data); \
+        netgrent->data = NULL; \
+        netgrent->idx.position = 0; \
+        netgrent->data_size = 0; \
+} while (0);
 
 /*
  * Replies:
@@ -61,19 +61,6 @@ struct sss_nss_netgr_rep {
     size_t buflen;
 };
 
-static void sss_nss_getnetgrent_data_clean(void) {
-    if (sss_nss_getnetgrent_data.name != NULL) {
-        free(sss_nss_getnetgrent_data.name);
-        sss_nss_getnetgrent_data.name = NULL;
-    }
-    if (sss_nss_getnetgrent_data.data != NULL) {
-        free(sss_nss_getnetgrent_data.data);
-        sss_nss_getnetgrent_data.data = NULL;
-    }
-    sss_nss_getnetgrent_data.len = 0;
-    sss_nss_getnetgrent_data.ptr = 0;
-}
-
 static int sss_nss_getnetgr_readrep(struct sss_nss_netgr_rep *pr,
                                     uint8_t *buf, size_t *len)
 {
@@ -201,28 +188,30 @@ enum nss_status _nss_sss_setnetgrent(const char *netgroup,
     enum nss_status nret;
     struct sss_cli_req_data rd;
     int errnop;
+    char *name;
     size_t name_len;
     errno_t ret;
 
     if (!netgroup) return NSS_STATUS_NOTFOUND;
 
     /* make sure we do not have leftovers, and release memory */
-    sss_nss_getnetgrent_data_clean();
+    CLEAR_NETGRENT_DATA(result);
 
     ret = sss_strnlen(netgroup, MAX_NETGR_NAME_LENGTH, &name_len);
     if (ret != 0) return NSS_STATUS_NOTFOUND;
 
-    sss_nss_getnetgrent_data.name = malloc(sizeof(char)*name_len + 1);
-    if (sss_nss_getnetgrent_data.name == NULL) {
+    name = malloc(sizeof(char)*name_len + 1);
+    if (name == NULL) {
         return NSS_STATUS_TRYAGAIN;
     }
-    strncpy(sss_nss_getnetgrent_data.name, netgroup, name_len + 1);
+    strncpy(name, netgroup, name_len + 1);
 
-    rd.data = sss_nss_getnetgrent_data.name;
+    rd.data = name;
     rd.len = name_len + 1;
 
     nret = sss_nss_make_request(SSS_NSS_SETNETGRENT, &rd,
                                 &repbuf, &replen, &errnop);
+    free(name);
     if (nret != NSS_STATUS_SUCCESS) {
         errno = errnop;
         return nret;
@@ -234,6 +223,7 @@ enum nss_status _nss_sss_setnetgrent(const char *netgroup,
         return NSS_STATUS_NOTFOUND;
     }
 
+    free(repbuf);
     return NSS_STATUS_SUCCESS;
 }
 
@@ -255,13 +245,11 @@ enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result,
     /* If we're already processing result data, continue to
      * return it.
      */
-    if (sss_nss_getnetgrent_data.data != NULL &&
-        sss_nss_getnetgrent_data.ptr < sss_nss_getnetgrent_data.len) {
+    if (result->data != NULL &&
+        result->idx.position < result->data_size) {
 
-        repbuf = (uint8_t *)sss_nss_getnetgrent_data.data +
-                sss_nss_getnetgrent_data.ptr;
-        replen = sss_nss_getnetgrent_data.len -
-                    sss_nss_getnetgrent_data.ptr;
+        repbuf = (uint8_t *) result->data + result->idx.position;
+        replen = result->data_size - result->idx.position;
 
         netgrrep.result = result;
         netgrrep.buffer = buffer;
@@ -273,13 +261,13 @@ enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result,
             return NSS_STATUS_TRYAGAIN;
         }
 
-        sss_nss_getnetgrent_data.ptr = sss_nss_getnetgrent_data.len - replen;
+        result->idx.position = result->data_size - replen;
 
         return NSS_STATUS_SUCCESS;
     }
 
     /* Release memory, if any */
-    sss_nss_getnetgrent_data_clean();
+    CLEAR_NETGRENT_DATA(result);
 
     /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
     num_entries = SSS_NSS_MAX_ENTRIES;
@@ -298,22 +286,22 @@ enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result,
         return NSS_STATUS_RETURN;
     }
 
-    sss_nss_getnetgrent_data.data = repbuf;
-    sss_nss_getnetgrent_data.len = replen;
+    result->data = (char *) repbuf;
+    result->data_size = replen;
     /* skip metadata fields */
-    sss_nss_getnetgrent_data.ptr = NETGR_METADATA_COUNT;
+    result->idx.position = NETGR_METADATA_COUNT;
 
     /* call again ourselves, this will return the first result */
     return _nss_sss_getnetgrent_r(result, buffer, buflen, errnop);
 }
 
-enum nss_status _nss_sss_endnetgrent(void)
+enum nss_status _nss_sss_endnetgrent(struct __netgrent *result)
 {
     enum nss_status nret;
     int errnop;
 
     /* make sure we do not have leftovers, and release memory */
-    sss_nss_getnetgrent_data_clean();
+    CLEAR_NETGRENT_DATA(result);
 
     nret = sss_nss_make_request(SSS_NSS_ENDNETGRENT,
                                 NULL, NULL, NULL, &errnop);
-- 
1.7.2.3



More information about the sssd-devel mailing list