[SSSD] [PATCHES] Netgroup support for LDAP provider

Sumit Bose sbose at redhat.com
Tue Oct 5 14:21:01 UTC 2010


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.

bye,
Sumit
-------------- next part --------------
From 87ca7c441e58c782cd14b06bc0c931e161ebe5e1 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/8] 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..0989c9a 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -297,6 +297,7 @@ static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
             goto error;
         }
         tevent_req_post(req, cmdctx->cctx->ev);
+        tevent_req_done(req);
         /* Will return control below */
     } else {
         /* Unexpected error from hash_lookup */
-- 
1.7.2.3

-------------- next part --------------
From a04828b7bc4907e31a5bb366c1b97c20be449e82 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/8] 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 d6ef3bfcb0793d36d4db1b53d6e02372062554ef 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/8] Add handling of nested netgroups to nss client

---
 src/responder/nss/nsssrv_netgroup.c |    5 +-
 src/sss_client/nss_netgroup.c       |  173 +++++++++++++++++++++--------------
 src/sss_client/sss_cli.h            |    5 +
 3 files changed, 112 insertions(+), 71 deletions(-)

diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 0989c9a..c1c1901 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..1b213c6 100644
--- a/src/sss_client/nss_netgroup.c
+++ b/src/sss_client/nss_netgroup.c
@@ -48,8 +48,10 @@ static struct sss_nss_getnetgrent_data {
  * 0-3: 32bit unsigned number of results
  * 4-7: 32bit unsigned (reserved/padding)
  *  For each result:
- *  8-X: sequence of \0 terminated strings representing tuple
+ *  8-X: sequence of a type indicator and \0 terminated strings representing
+ *  tuple
  *       (host, user, domain)
+ * or a netgroup, depending on the type indicator
  */
 #define NETGR_METADATA_COUNT 2 * sizeof(uint32_t)
 struct sss_nss_netgr_rep {
@@ -78,83 +80,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 e17667d10848976e86d9223b413b0237c25530c2 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/8] 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 ffd524d0b73ffb194a54b3b3448d51e005a50cb5 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/8] 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 824a6462c23b001e9a0210b51f5a32be5ac118a4 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/8] Also return member groups to the client

---
 src/db/sysdb.h                      |   17 ++++--
 src/db/sysdb_search.c               |  113 +++++++++++++++++++------------
 src/responder/nss/nsssrv_netgroup.c |  123 ++++++++++++++++++++++-------------
 src/tests/sysdb-tests.c             |   88 ++++++++++++------------
 4 files changed, 202 insertions(+), 139 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..fb3b7d0 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -650,15 +650,15 @@ 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;
     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 +673,94 @@ 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.
+        if (el != NULL) {
+            /* Enlarge the array by the value count
+             * Always keep one extra entry for the NULL terminator
              */
-            continue;
-        }
-
-        /* 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;
-        }
-
-        /* 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) {
+            tmp_entry = talloc_realloc(tmp_ctx, tmp_entry,
+                                       struct sysdb_netgroup_ctx *,
+                                       size+el->num_values+1);
+            if (!tmp_entry) {
                 ret = ENOMEM;
                 goto done;
             }
 
-            tmp_triples[size] = talloc_zero(tmp_triples,
-                                            struct sysdb_netgroup_ctx);
-            if (!tmp_triples[size]) {
+            /* 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[size] = talloc_zero(tmp_entry,
+                                              struct sysdb_netgroup_ctx);
+                if (!tmp_entry[size]) {
+                    ret = ENOMEM;
+                    goto done;
+                }
+
+                tmp_entry[size]->type = SYSDB_NETGROUP_TRIPLE_VAL;
+                ret = sysdb_netgr_split_triple(tmp_entry[size],
+                                     triple_str,
+                                     &tmp_entry[size]->value.triple.hostname,
+                                     &tmp_entry[size]->value.triple.username,
+                                     &tmp_entry[size]->value.triple.domainname);
+                if (ret != EOK) {
+                    goto done;
+                }
+
+                size++;
+            }
+        }
+        el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+        if (el != NULL) {
+            tmp_entry = talloc_realloc(tmp_ctx, tmp_entry,
+                                       struct sysdb_netgroup_ctx *,
+                                       size+el->num_values+1);
+            if (!tmp_entry) {
                 ret = ENOMEM;
                 goto done;
             }
 
-            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;
+            for(j = 0; j < el->num_values; j++) {
+                tmp_entry[size] = talloc_zero(tmp_entry,
+                                              struct sysdb_netgroup_ctx);
+                if (!tmp_entry[size]) {
+                    ret = ENOMEM;
+                    goto done;
+                }
+
+                tmp_entry[size]->type = SYSDB_NETGROUP_GROUP_VAL;
+                tmp_entry[size]->value.groupname = talloc_strndup(tmp_entry[size],
+                                               (const char *)el->values[j].data,
+                                               el->values[j].length);
+                if (tmp_entry[size]->value.groupname == NULL) {
+                    ret = ENOMEM;
+                    goto done;
+                }
+
+                size++;
             }
-
-            size++;
         }
     }
 
-    if (!tmp_triples) {
+    if (!tmp_entry) {
         /* No entries were found
          * Create a dummy reply
          */
-        tmp_triples = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, 1);
-        if (!tmp_triples) {
+        tmp_entry = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, 1);
+        if (!tmp_entry) {
             ret = ENOMEM;
             goto done;
         }
     }
     /* Add NULL terminator */
-    tmp_triples[size] = NULL;
+    tmp_entry[size] = 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 c1c1901..64daa9a 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -402,7 +402,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
          }
 
          /* Convert the result to a list of triples */
-         ret = sysdb_netgr_to_triples(netgr, step_ctx->dctx->res,
+         ret = sysdb_netgr_to_entries(netgr, step_ctx->dctx->res,
                                       &netgr->triples);
          if (ret == ENOENT) {
              /* This netgroup was not found in this domain */
@@ -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,89 @@ 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.\n"));
+                return EINVAL;
+            }
 
-        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.\n"));
+            return EINVAL;
         }
-        rp += domainlen;
 
         num++;
         client->netgrent_cur++;
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 fb948f83636aaa8d91854e3818026120cc84f4c1 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/8] 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 1477a82bcb2211966666bd02376c616981221706 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/8] 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 |  684 +++++++++++++++++++++++++++++
 7 files changed, 960 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 63d5913..865529b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -764,6 +764,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 \
@@ -771,6 +772,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 \
@@ -847,11 +849,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..f42d523
--- /dev/null
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -0,0 +1,684 @@
+/*
+    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_resovled)
+{
+    struct dn_item *dn_item;
+    size_t c;
+    const char *dn;
+    const char *cn;
+    bool not_resolved = false;
+
+    *all_resovled = 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_resovled = !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 void 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_resovled;
+    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);
+    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_resovled);
+        if (ret != EOK) {
+            DEBUG(1, ("update_dn_list failed.\n"));
+            goto fail;
+        }
+
+        if (all_resovled) {
+            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;
+    netgr_translate_members_ldap_step(req);
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void 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;
+                }
+        }
+
+        tevent_req_done(req);
+        return;
+    }
+
+    cn_attr = talloc_array(state, const char *, 3);
+    if (cn_attr == NULL) {
+        DEBUG(1, ("talloc_array failed.\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    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) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    tevent_req_set_callback(subreq, netgr_translate_members_ldap_done, req);
+    return;
+}
+
+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) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    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;
+    netgr_translate_members_ldap_step(req);
+    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



More information about the sssd-devel mailing list