From c4a432b948ca43e34165fc2860d98e16ebe42183 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Fri, 29 Apr 2011 16:23:52 +0200
Subject: [PATCH 8/9] Refactor RFC2307bis nested group processing

This patch splits checking cache and hash tables into standalone
functions. This will make it easy to reuse the code in a new branch that
uses dereferencing.
---
 src/providers/ldap/sdap_async_accounts.c |  311 ++++++++++++++++++------------
 1 files changed, 188 insertions(+), 123 deletions(-)

diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index d2913e2..89feff0 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -3405,156 +3405,188 @@ immediate:
     return req;
 }
 
+static errno_t sdap_nested_group_check_hash(struct sdap_nested_group_ctx *);
+static errno_t sdap_nested_group_check_cache(TALLOC_CTX *mem_ctx,
+                                    struct sysdb_ctx *sysdb,
+                                    struct sss_domain_info *domain,
+                                    struct sdap_options *opts,
+                                    char *member_dn,
+                                    struct ldb_message ***_msgs,
+                                    enum sysdb_member_type *_mtype);
 static void sdap_nested_group_process_ldap_user(struct tevent_req *subreq);
 static void sdap_nested_group_process_user(struct tevent_req *subreq);
 static errno_t sdap_nested_group_lookup_user(struct tevent_req *req,
                                              tevent_req_fn fn);
 static errno_t sdap_nested_group_lookup_group(struct tevent_req *req);
+
 static errno_t sdap_nested_group_process_step(struct tevent_req *req)
 {
     errno_t ret;
     struct sdap_nested_group_ctx *state =
             tevent_req_data(req, struct sdap_nested_group_ctx);
-    char *member_dn;
-    char *filter;
-    static const char *attrs[] = SYSDB_PW_ATTRS;
-    size_t count;
-    struct ldb_message **msgs;
-    uint64_t expiration;
-    uint64_t create_time;
-    bool has_key = false;
-    hash_key_t key;
-    uint8_t *data;
-    time_t now = time(NULL);
-    uid_t user_uid;
+    struct ldb_message **msgs = NULL;
+    enum sysdb_member_type mtype;
 
     while (true) {
         /* Continue to loop through until all entries have been
          * processed.
          */
-        do {
-            if (state->member_index >= state->members->num_values) {
-                /* No more entries to check. Return success */
-                return EOK;
-            }
+        ret = sdap_nested_group_check_hash(state);
+        if (ret == EOK) {
+            talloc_zfree(state->member_dn);
+            return EOK; /* All members in hash */
+        } else if (ret != ENOENT) {
+            goto error; /* Unexpected error */
+        }
 
-            /* First check whether this origDN is present (and not expired)
-             * in the sysdb
-             */
-            data = state->members->values[state->member_index].data;
-            state->member_dn = talloc_strdup(state, (const char *)data);
-            if (!state->member_dn) {
-                ret = ENOMEM;
+        ret = sdap_nested_group_check_cache(state, state->sysdb,
+                                            state->domain, state->opts,
+                                            state->member_dn,
+                                            &msgs, &mtype);
+        if (ret == EOK) {
+            /* The entry is cached and valid */
+            state->member_index++;
+            talloc_zfree(state->member_dn);
+            continue;
+        } else if (ret == EAGAIN) {
+            /* The entry is cached but needs refresh */
+            switch(mtype) {
+            case SYSDB_MEMBER_GROUP:
+                DEBUG(6, ("Refreshing cached group from LDAP\n"));
+                ret = sdap_nested_group_lookup_group(req);
+                if (ret != EOK) goto error;
+                break;
+            case SYSDB_MEMBER_USER:
+                DEBUG(6, ("Refreshing cached user from LDAP\n"));
+                ret = sdap_nested_group_lookup_user(
+                        req, sdap_nested_group_process_user);
+                if (ret != EOK) goto error;
+                break;
+            default:
+                DEBUG(2, ("Unknown member value\n"));
+                ret = EINVAL;
                 goto error;
             }
 
-            /* Check the user hash
-             * If it's there, we can save ourselves a trip to the
-             * sysdb and possibly LDAP as well
+            return EAGAIN;
+        } else if (ret == ENOENT) {
+            /* It wasn't found in the cache either
+             * We'll have to do a blind lookup in LDAP
              */
-            key.type = HASH_KEY_STRING;
-            key.str = state->member_dn;
-            has_key = hash_has_key(state->users, &key);
-            if (has_key) {
-                talloc_zfree(state->member_dn);
-                state->member_index++;
-                continue;
-            }
-
 
-        } while (has_key);
-
-        ret = sss_filter_sanitize(state, state->member_dn, &member_dn);
-        if (ret != EOK) {
-            goto error;
+            /* Try users first */
+            ret = sdap_nested_group_lookup_user(
+                    req, sdap_nested_group_process_ldap_user);
+            if (ret != EOK) {
+                goto error;
+            }
+            return EAGAIN;
         }
 
-        /* Check for the specified origDN in the sysdb */
-        filter = talloc_asprintf(NULL, "(%s=%s)",
-                                 SYSDB_ORIG_DN,
-                                 member_dn);
-        if (!filter) {
-            ret = ENOMEM;
-            goto error;
-        }
+        /* Unexpected error, skip this entry */
+        state->member_index++;
+        talloc_zfree(state->member_dn);
+        continue;
+    } /* while (true) */
 
-        /* Try users first */
-        ret = sysdb_search_users(state, state->sysdb, state->domain, filter,
-                                 attrs, &count, &msgs);
-        talloc_zfree(filter);
-        if (ret != EOK && ret != ENOENT) {
-            goto error;
-        } if (ret == ENOENT || count == 0) {
-            /* It wasn't a user. Check whether it's a group */
-            if (ret == EOK) talloc_zfree(msgs);
+error:
+    talloc_zfree(state->member_dn);
+    return ret;
+}
 
-            filter = talloc_asprintf(NULL, "(%s=%s)",
-                                     SYSDB_ORIG_DN,
-                                     member_dn);
-            talloc_zfree(member_dn);
-            if (!filter) {
-                ret = ENOMEM;
-                goto error;
-            }
+static errno_t
+sdap_nested_group_check_hash(struct sdap_nested_group_ctx *state)
+{
+    hash_key_t key;
+    bool has_key = false;
+    uint8_t *data;
 
-            ret = sysdb_search_groups(state, state->sysdb, state->domain,
-                                      filter, attrs, &count, &msgs);
-            talloc_zfree(filter);
-            if (ret != EOK && ret != ENOENT) {
-                ret = EIO;
-                goto error;
-            } else if (ret == ENOENT || count == 0) {
-                if (ret == EOK) talloc_zfree(msgs);
+    do {
+        if (state->member_index >= state->members->num_values) {
+            /* No more entries to check. Return success */
+            return EOK;
+        }
 
-                /* It wasn't found in the groups either
-                 * We'll have to do a blind lookup for both
-                 */
+        data = state->members->values[state->member_index].data;
+        state->member_dn = talloc_strdup(state, (const char *)data);
+        if (!state->member_dn) {
+            return ENOMEM;
+        }
 
-                /* Try users first */
-                ret = sdap_nested_group_lookup_user(
-                        req, sdap_nested_group_process_ldap_user);
-                if (ret != EOK) {
-                    goto error;
-                }
-                return EAGAIN;
-            }
+        /* Check the user hash
+         * If it's there, we can save ourselves a trip to the
+         * sysdb and possibly LDAP as well
+         */
+        key.type = HASH_KEY_STRING;
+        key.str = state->member_dn;
+        has_key = hash_has_key(state->users, &key);
+        if (has_key) {
+            talloc_zfree(state->member_dn);
+            state->member_index++;
+            continue;
+        }
+    } while (has_key);
 
-            /* We found a group with this origDN in the sysdb */
+    return ENOENT;
+}
 
-            /* Check whether the entry is valid */
-            if (count != 1) {
-                DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
-                state->member_index++;
-                talloc_zfree(state->member_dn);
-                continue;
-            }
+static errno_t
+sdap_nested_group_check_cache(TALLOC_CTX *mem_ctx,
+                              struct sysdb_ctx *sysdb,
+                              struct sss_domain_info *domain,
+                              struct sdap_options *opts,
+                              char *dn,
+                              struct ldb_message ***_msgs,
+                              enum sysdb_member_type *_mtype)
+{
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    struct ldb_message **msgs = NULL;
+    char *member_dn;
+    uint64_t expiration;
+    uint64_t create_time;
+    uid_t user_uid;
+    time_t now = time(NULL);
+    static const char *attrs[] = { SYSDB_CACHE_EXPIRE, SYSDB_UIDNUM,
+                                   SYSDB_CREATE_TIME, SYSDB_NAME,
+                                   NULL };
+    char *filter;
+    enum sysdb_member_type mtype;
+    size_t count;
 
-            expiration = ldb_msg_find_attr_as_uint64(msgs[0],
-                                                     SYSDB_CACHE_EXPIRE,
-                                                     0);
-            if (expiration && expiration > now) {
-                DEBUG(6, ("Cached values are still valid. Skipping\n"));
-                state->member_index++;
-                talloc_zfree(state->member_dn);
-                continue;
-            }
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
 
-            /* Refresh the group from LDAP */
-            ret = sdap_nested_group_lookup_group(req);
-            if (ret != EOK)  goto error;
+    ret = sss_filter_sanitize(tmp_ctx, dn, &member_dn);
+    if (ret != EOK) {
+        goto done;
+    }
 
-            return EAGAIN;
-        }
-        talloc_zfree(member_dn);
+    /* Check for the specified origDN in the sysdb */
+    filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
+                             SYSDB_ORIG_DN,
+                             member_dn);
+    if (!filter) {
+        ret = ENOMEM;
+        goto done;
+    }
 
-        /* We found a user with this origDN in the sysdb */
+    /* Try users first */
+    ret = sysdb_search_users(tmp_ctx, sysdb, domain, filter,
+                             attrs, &count, &msgs);
+    if (ret != EOK && ret != ENOENT) {
+        ret = EIO;
+        goto done;
+    } else if (ret == EOK && count > 0) {
+        /* We found a user with this origDN in the sysdb. Check if it is valid
+         */
+        mtype = SYSDB_MEMBER_USER;
 
         /* Check whether the entry is valid */
         if (count != 1) {
             DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
-            state->member_index++;
-            talloc_zfree(state->member_dn);
-            continue;
+            ret = EIO;
+            goto done;
         }
 
         user_uid = ldb_msg_find_attr_as_uint64(msgs[0], SYSDB_UIDNUM, 0);
@@ -3564,7 +3596,7 @@ static errno_t sdap_nested_group_process_step(struct tevent_req *req)
                                                     SYSDB_CREATE_TIME,
                                                     0);
             expiration = create_time +
-                            dp_opt_get_int(state->opts->basic,
+                            dp_opt_get_int(opts->basic,
                                         SDAP_ENTRY_CACHE_TIMEOUT);
         } else {
             /* Regular user, check if we need a refresh */
@@ -3575,21 +3607,54 @@ static errno_t sdap_nested_group_process_step(struct tevent_req *req)
 
         if (expiration && expiration > now) {
             DEBUG(6, ("Cached values are still valid. Skipping\n"));
-            state->member_index++;
-            talloc_zfree(state->member_dn);
-            continue;
+            ret = EOK;
+            goto done;
         }
 
         /* Refresh the user from LDAP */
-        ret = sdap_nested_group_lookup_user(
-                req, sdap_nested_group_process_user);
-        if (ret != EOK)  goto error;
+        ret = EAGAIN;
+        goto done;
+    }
 
-        return EAGAIN;
-    } /* while (true) */
+    /* It wasn't a user. Check whether it's a group */
+    if (ret == EOK) talloc_zfree(msgs);
 
-error:
-    talloc_zfree(state->member_dn);
+    ret = sysdb_search_groups(tmp_ctx, sysdb, domain,
+                              filter, attrs, &count, &msgs);
+    if (ret != EOK && ret != ENOENT) {
+        ret = EIO;
+        goto done;
+    } else if (ret == EOK && count > 0) {
+        /* We found a group with this origDN in the sysdb */
+        mtype = SYSDB_MEMBER_GROUP;
+
+        /* Check whether the entry is valid */
+        if (count != 1) {
+            DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
+            ret = EIO;
+            goto done;
+        }
+
+        expiration = ldb_msg_find_attr_as_uint64(msgs[0],
+                                                 SYSDB_CACHE_EXPIRE,
+                                                 0);
+        if (expiration && expiration > now) {
+            DEBUG(6, ("Cached values are still valid.\n"));
+            ret = EOK;
+            goto done;
+        }
+
+        /* Refresh the group from LDAP */
+        ret = EAGAIN;
+        goto done;
+    }
+
+    /* It wasn't found in the groups either */
+    ret = ENOENT;
+done:
+    *_msgs = talloc_steal(mem_ctx, msgs);
+    *_mtype = mtype;
+    talloc_zfree(tmp_ctx);
     return ret;
 }
 
-- 
1.7.4.4

