From 3779367329646215065e1608ef065930b581e854 Mon Sep 17 00:00:00 2001 From: Tim Beale Date: Tue, 15 May 2018 14:02:32 +1200 Subject: [PATCH] dsdb: Avoid performance hit if PSOs aren't actually used The new PSO code adds some additional overhead in extra lookups. To avoid penalizing existing setups, we can short-circuit the PSO processing and return early if there are no actual PSO objects in the DB. The one-level search should be very quick, and it avoids the need to do more complicated PSO processing (i.e. expanding the nested groups). The longer-term plan is to rework the tokenGroups lookup so that it only gets done once, and the result can then be reused by the resultant-PSO code (rather than computing the nested-groups again). However, in the short-term, a slight decrease in performance is the price for any users that want to deploy PSOs. Signed-off-by: Tim Beale Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- source4/dsdb/samdb/ldb_modules/operational.c | 56 +++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/source4/dsdb/samdb/ldb_modules/operational.c b/source4/dsdb/samdb/ldb_modules/operational.c index c2968a4b802..c99adb1dcd7 100644 --- a/source4/dsdb/samdb/ldb_modules/operational.c +++ b/source4/dsdb/samdb/ldb_modules/operational.c @@ -981,6 +981,43 @@ static bool pso_is_supported(struct ldb_context *ldb, struct ldb_message *msg) return true; } +/* + * Returns the number of PSO objects that exist in the DB + */ +static int get_pso_count(struct ldb_module *module, TALLOC_CTX *mem_ctx, + struct ldb_request *parent, int *pso_count) +{ + static const char * const attrs[] = { NULL }; + int ret; + struct ldb_dn *domain_dn = NULL; + struct ldb_dn *psc_dn = NULL; + struct ldb_result *res = NULL; + struct ldb_context *ldb = ldb_module_get_ctx(module); + + domain_dn = ldb_get_default_basedn(ldb); + psc_dn = ldb_dn_new_fmt(mem_ctx, ldb, + "CN=Password Settings Container,CN=System,%s", + ldb_dn_get_linearized(domain_dn)); + if (psc_dn == NULL) { + return ldb_oom(ldb); + } + + /* get the number of PSO children */ + ret = dsdb_module_search(module, mem_ctx, &res, psc_dn, + LDB_SCOPE_ONELEVEL, attrs, + DSDB_FLAG_NEXT_MODULE, parent, + "(objectClass=msDS-PasswordSettings)"); + if (ret != LDB_SUCCESS) { + return ret; + } + + *pso_count = res->count; + talloc_free(res); + talloc_free(psc_dn); + + return LDB_SUCCESS; +} + /* * Compares two PSO objects returned by a search, to work out the better PSO. * The PSO with the lowest precedence is better, otherwise (if the precedence @@ -1111,6 +1148,7 @@ static int get_pso_for_user(struct ldb_module *module, int ret; struct ldb_message_element *el = NULL; TALLOC_CTX *tmp_ctx = NULL; + int pso_count = 0; *pso_msg = NULL; @@ -1150,8 +1188,24 @@ static int get_pso_for_user(struct ldb_module *module, /* * If no valid PSO applies directly to the user, then try its groups. - * Work out the SIDs of any account groups the user is a member of + * The group expansion is expensive, so check there are actually + * PSOs in the DB first (which is a quick search). Note in the above + * case we could tell that a PSO applied to the user, based on info + * already retrieved by the user search. */ + ret = get_pso_count(module, tmp_ctx, parent, &pso_count); + if (ret != LDB_SUCCESS) { + DBG_ERR("Error %d determining PSOs in system\n", ret); + talloc_free(tmp_ctx); + return ret; + } + + if (pso_count == 0) { + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } + + /* Work out the SIDs of any account groups the user is a member of */ ret = get_group_sids(ldb, tmp_ctx, user_msg, "msDS-ResultantPSO", ACCOUNT_GROUPS, &groupSIDs, &num_groupSIDs); -- 2.47.2