5b4a29
From 053fb02f73220be53d1fb93511684a6f7aa3226f Mon Sep 17 00:00:00 2001
5b4a29
From: Thierry Bordaz <tbordaz@redhat.com>
5b4a29
Date: Thu, 10 Nov 2022 16:54:40 +0100
5b4a29
Subject: [PATCH 3/5] Issue 5440 - memberof is slow on update/fixup if there
5b4a29
 are several 'groupattr' (#5455)
5b4a29
5b4a29
Bug description:
5b4a29
	When there are several groupattr (attr_1, attr_2,..) in memberof config
5b4a29
	To fixup entry 'e1', memberof does an internal search
5b4a29
	"(|(attr_1=e1)(attr_2=e1)...(attr_n=e1))"
5b4a29
	This is not valid regarding membership relation and in
5b4a29
	addition it prevents the server to bypass the filter evaluation.
5b4a29
5b4a29
Fix description:
5b4a29
	To fixup an entry iterate several internal searches
5b4a29
	"(attr_1=e1)" , then "(attr_2=e1)", then "(attr_n=e1)"
5b4a29
5b4a29
relates: #5440
5b4a29
5b4a29
Reviewed by: Pierre Rogier, Mark Reynolds, Simon Pichugin (Thanks)
5b4a29
---
5b4a29
 ldap/servers/plugins/memberof/memberof.c | 155 +++++++++++------------
5b4a29
 1 file changed, 73 insertions(+), 82 deletions(-)
5b4a29
5b4a29
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
5b4a29
index b54eb3977..541b27250 100644
5b4a29
--- a/ldap/servers/plugins/memberof/memberof.c
5b4a29
+++ b/ldap/servers/plugins/memberof/memberof.c
5b4a29
@@ -704,8 +704,6 @@ memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn
5b4a29
     char *filter_str = NULL;
5b4a29
     char *cookie = NULL;
5b4a29
     int all_backends = config->allBackends;
5b4a29
-    int types_name_len = 0;
5b4a29
-    int num_types = 0;
5b4a29
     int dn_len = slapi_sdn_get_ndn_len(sdn);
5b4a29
     int free_it = 0;
5b4a29
     int rc = 0;
5b4a29
@@ -744,107 +742,100 @@ memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn
5b4a29
 #if MEMBEROF_CACHE_DEBUG
5b4a29
     slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s not cached\n", ndn);
5b4a29
 #endif
5b4a29
-    /* Count the number of types. */
5b4a29
-    for (num_types = 0; types && types[num_types]; num_types++) {
5b4a29
-        /* Add up the total length of all attribute names.
5b4a29
-         * We need to know this for building the filter. */
5b4a29
-        types_name_len += strlen(types[num_types]);
5b4a29
-    }
5b4a29
 
5b4a29
     /* Escape the dn, and build the search filter. */
5b4a29
     escaped_filter_val = slapi_escape_filter_value((char *)slapi_sdn_get_dn(sdn), dn_len);
5b4a29
     if (escaped_filter_val) {
5b4a29
-        dn_len = strlen(escaped_filter_val);
5b4a29
         free_it = 1;
5b4a29
     } else {
5b4a29
         escaped_filter_val = (char *)slapi_sdn_get_dn(sdn);
5b4a29
     }
5b4a29
 
5b4a29
-    if (num_types > 1) {
5b4a29
-        int bytes_out = 0;
5b4a29
-        int filter_str_len = types_name_len + (num_types * (3 + dn_len)) + 4;
5b4a29
-
5b4a29
-        /* Allocate enough space for the filter */
5b4a29
-        filter_str = slapi_ch_malloc(filter_str_len);
5b4a29
-
5b4a29
-        /* Add beginning of filter. */
5b4a29
-        bytes_out = snprintf(filter_str, filter_str_len - bytes_out, "(|");
5b4a29
-
5b4a29
-        /* Add filter section for each type. */
5b4a29
-        for (i = 0; types[i]; i++) {
5b4a29
-            bytes_out += snprintf(filter_str + bytes_out, filter_str_len - bytes_out,
5b4a29
-                                  "(%s=%s)", types[i], escaped_filter_val);
5b4a29
-        }
5b4a29
-
5b4a29
-        /* Add end of filter. */
5b4a29
-        snprintf(filter_str + bytes_out, filter_str_len - bytes_out, ")");
5b4a29
-    } else if (num_types == 1) {
5b4a29
-        filter_str = slapi_ch_smprintf("(%s=%s)", types[0], escaped_filter_val);
5b4a29
-    }
5b4a29
-
5b4a29
-    if (free_it)
5b4a29
-        slapi_ch_free_string(&escaped_filter_val);
5b4a29
+    for (i = 0; types[i]; i++) {
5b4a29
+        /* Triggers one internal search per membership attribute.
5b4a29
+         * Assuming the attribute is indexed (eq), the search will
5b4a29
+         * bypass the evaluation of the filter (nsslapd-search-bypass-filter-test)
5b4a29
+         * against the candidates. This is important to bypass the filter
5b4a29
+         * because on large valueset (static group) it is very expensive
5b4a29
+         */
5b4a29
+        filter_str = slapi_ch_smprintf("(%s=%s)", types[i], escaped_filter_val);
5b4a29
 
5b4a29
-    if (filter_str == NULL) {
5b4a29
-        return rc;
5b4a29
-    }
5b4a29
+        be = slapi_get_first_backend(&cookie);
5b4a29
+        while (be) {
5b4a29
+            PRBool do_suffix_search = PR_TRUE;
5b4a29
 
5b4a29
-    search_pb = slapi_pblock_new();
5b4a29
-    be = slapi_get_first_backend(&cookie);
5b4a29
-    while (be) {
5b4a29
-        Slapi_DN *scope_sdn = NULL;
5b4a29
+            if (!all_backends) {
5b4a29
+                be = slapi_be_select(sdn);
5b4a29
+                if (be == NULL) {
5b4a29
+                    break;
5b4a29
+                }
5b4a29
+            }
5b4a29
+            if ((base_sdn = (Slapi_DN *) slapi_be_getsuffix(be, 0)) == NULL) {
5b4a29
+                if (!all_backends) {
5b4a29
+                    break;
5b4a29
+                } else {
5b4a29
+                    /* its ok, goto the next backend */
5b4a29
+                    be = slapi_get_next_backend(cookie);
5b4a29
+                    continue;
5b4a29
+                }
5b4a29
+            }
5b4a29
 
5b4a29
-        if (!all_backends) {
5b4a29
-            be = slapi_be_select(sdn);
5b4a29
-            if (be == NULL) {
5b4a29
-                break;
5b4a29
+            search_pb = slapi_pblock_new();
5b4a29
+            if (config->entryScopes || config->entryScopeExcludeSubtrees) {
5b4a29
+                if (memberof_entry_in_scope(config, base_sdn)) {
5b4a29
+                    /* do nothing, entry scope is spanning
5b4a29
+                     * multiple suffixes, start at suffix */
5b4a29
+                } else if (config->entryScopes) {
5b4a29
+                    for (size_t i = 0; config->entryScopes[i]; i++) {
5b4a29
+                        if (slapi_sdn_issuffix(config->entryScopes[i], base_sdn)) {
5b4a29
+                            /* Search each include scope */
5b4a29
+                            slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(config->entryScopes[i]),
5b4a29
+                                                         LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
5b4a29
+                                                         memberof_get_plugin_id(), 0);
5b4a29
+                            slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
5b4a29
+                            /* We already did the search for this backend, don't
5b4a29
+                             * do it again when we fall through */
5b4a29
+                            do_suffix_search = PR_FALSE;
5b4a29
+                        }
5b4a29
+                    }
5b4a29
+                } else if (!all_backends) {
5b4a29
+                    slapi_pblock_destroy(search_pb);
5b4a29
+                    break;
5b4a29
+                } else {
5b4a29
+                    /* its ok, goto the next backend */
5b4a29
+                    be = slapi_get_next_backend(cookie);
5b4a29
+                    slapi_pblock_destroy(search_pb);
5b4a29
+                    continue;
5b4a29
+                }
5b4a29
             }
5b4a29
-        }
5b4a29
-        if ((base_sdn = (Slapi_DN *)slapi_be_getsuffix(be, 0)) == NULL) {
5b4a29
-            if (!all_backends) {
5b4a29
-                break;
5b4a29
-            } else {
5b4a29
-                /* its ok, goto the next backend */
5b4a29
-                be = slapi_get_next_backend(cookie);
5b4a29
-                continue;
5b4a29
+
5b4a29
+            if (do_suffix_search) {
5b4a29
+                slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
5b4a29
+                                             LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
5b4a29
+                                             memberof_get_plugin_id(), 0);
5b4a29
+                slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
5b4a29
+                slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
5b4a29
+                if (rc != LDAP_SUCCESS) {
5b4a29
+                    slapi_pblock_destroy(search_pb);
5b4a29
+                    break;
5b4a29
+                }
5b4a29
             }
5b4a29
-        }
5b4a29
 
5b4a29
-        if (config->entryScopes || config->entryScopeExcludeSubtrees) {
5b4a29
-            if (memberof_entry_in_scope(config, base_sdn)) {
5b4a29
-                /* do nothing, entry scope is spanning
5b4a29
-                 * multiple suffixes, start at suffix */
5b4a29
-            } else if ((scope_sdn = memberof_scope_is_child_of_dn(config, base_sdn))) {
5b4a29
-                /* scope is below suffix, set search base */
5b4a29
-                base_sdn = scope_sdn;
5b4a29
-            } else if (!all_backends) {
5b4a29
+            if (!all_backends) {
5b4a29
+                slapi_pblock_destroy(search_pb);
5b4a29
                 break;
5b4a29
-            } else {
5b4a29
-                /* its ok, goto the next backend */
5b4a29
-                be = slapi_get_next_backend(cookie);
5b4a29
-                continue;
5b4a29
             }
5b4a29
-        }
5b4a29
-
5b4a29
-        slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
5b4a29
-                                     LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, memberof_get_plugin_id(), 0);
5b4a29
-        slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
5b4a29
-        slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
5b4a29
-        if (rc != LDAP_SUCCESS) {
5b4a29
-            break;
5b4a29
-        }
5b4a29
 
5b4a29
-        if (!all_backends) {
5b4a29
-            break;
5b4a29
+            be = slapi_get_next_backend(cookie);
5b4a29
+            slapi_pblock_destroy(search_pb);
5b4a29
         }
5b4a29
-        slapi_pblock_init(search_pb);
5b4a29
-        be = slapi_get_next_backend(cookie);
5b4a29
+        slapi_ch_free((void **)&cookie);
5b4a29
+        slapi_ch_free_string(&filter_str);
5b4a29
     }
5b4a29
 
5b4a29
-    slapi_pblock_destroy(search_pb);
5b4a29
-    slapi_ch_free((void **)&cookie);
5b4a29
-    slapi_ch_free_string(&filter_str);
5b4a29
-
5b4a29
+    if (free_it) {
5b4a29
+        slapi_ch_free_string(&escaped_filter_val);
5b4a29
+    }
5b4a29
     return rc;
5b4a29
 }
5b4a29
 
5b4a29
-- 
5b4a29
2.39.1
5b4a29