summaryrefslogtreecommitdiffstats
path: root/src/db/sysdb_search.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/db/sysdb_search.c
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/db/sysdb_search.c')
-rw-r--r--src/db/sysdb_search.c2782
1 files changed, 2782 insertions, 0 deletions
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
new file mode 100644
index 0000000..e4c53b8
--- /dev/null
+++ b/src/db/sysdb_search.c
@@ -0,0 +1,2782 @@
+/*
+ SSSD
+
+ System Database
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ 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_private.h"
+#include "confdb/confdb.h"
+#include <time.h>
+#include <ctype.h>
+
+
+static inline bool is_sysdb_name(const char *attr)
+{
+ return (attr != NULL && strcmp(attr, SYSDB_NAME) == 0);
+}
+
+/* helpers */
+static errno_t merge_ts_attr(struct ldb_message *ts_msg,
+ struct ldb_message *sysdb_msg,
+ const char *ts_attr,
+ const char *want_attrs[])
+{
+ errno_t ret;
+ bool include = true;
+ struct ldb_message_element *ts_el;
+ struct ldb_message_element *sysdb_el;
+
+ if (want_attrs != NULL) {
+ /* Otherwise merge all ts attrs */
+ include = string_in_list(ts_attr, discard_const(want_attrs), true);
+ }
+ if (include == false) {
+ return EOK;
+ }
+
+ ts_el = ldb_msg_find_element(ts_msg, ts_attr);
+ if (ts_el == NULL || ts_el->num_values == 0) {
+ return EOK;
+ }
+
+ if (ts_el->num_values > 1) {
+ return EIO;
+ }
+
+ sysdb_el = ldb_msg_find_element(sysdb_msg, ts_attr);
+ if (sysdb_el == NULL || sysdb_el->num_values == 0) {
+ ret = ldb_msg_add_steal_value(sysdb_msg, ts_attr, &ts_el->values[0]);
+ if (ret != EOK) {
+ return sysdb_error_to_errno(ret);
+ }
+ } else {
+ /* Assumes the timestamps cache only holds single-valued
+ * attributes */
+ sysdb_el->values = talloc_steal(sysdb_el->values, ts_el->values);
+ }
+
+ return EOK;
+}
+
+static errno_t merge_all_ts_attrs(struct ldb_message *ts_msg,
+ struct ldb_message *sysdb_msg,
+ const char *want_attrs[])
+{
+ int ret;
+
+ /* Deliberately start from 2 in order to not merge
+ * objectclass/objectcategory and avoid breaking MPGs where the OC might
+ * be made up
+ */
+ for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
+ ret = merge_ts_attr(ts_msg, sysdb_msg,
+ sysdb_ts_cache_attrs[c], want_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
+ struct ldb_message *sysdb_msg,
+ const char *attrs[])
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **ts_msgs;
+ bool ts_dn;
+
+ ts_dn = is_ts_ldb_dn(sysdb_msg->dn);
+ if (ts_dn == false) {
+ return ERR_NO_TS;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_ts_entry(tmp_ctx, sysdb, sysdb_msg->dn,
+ LDB_SCOPE_BASE,
+ NULL,
+ sysdb_ts_cache_attrs,
+ &msgs_count,
+ &ts_msgs);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "No such DN in the timestamp cache: %s\n",
+ ldb_dn_get_linearized(sysdb_msg->dn));
+ ret = ERR_TS_CACHE_MISS;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Cannot find TS cache entry for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(sysdb_msg->dn),
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (msgs_count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected 1 result for base search, got %zu\n", msgs_count);
+ return EIO;
+ }
+
+ ret = merge_all_ts_attrs(ts_msgs[0], sysdb_msg, attrs);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t merge_msg_sysdb_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_message *ts_msg,
+ struct ldb_message **_sysdb_msg,
+ const char *attrs[])
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ size_t msgs_count;
+ struct ldb_message **sysdb_msgs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_cache_search_entry(tmp_ctx, sysdb->ldb, ts_msg->dn, LDB_SCOPE_BASE,
+ NULL, attrs, &msgs_count, &sysdb_msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (msgs_count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected 1 result for base search, got %zu\n", msgs_count);
+ goto done;
+ }
+
+ ret = merge_all_ts_attrs(ts_msg, sysdb_msgs[0], attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_sysdb_msg = talloc_steal(mem_ctx, sysdb_msgs[0]);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx,
+ struct ldb_result *res,
+ const char *attrs[])
+{
+ errno_t ret;
+
+ if (res == NULL || ctx->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ for (size_t c = 0; c < res->count; c++) {
+ ret = merge_msg_ts_attrs(ctx, res->msgs[c], attrs);
+ if (ret == ERR_NO_TS) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "TS cache doesn't handle this DN type, skipping\n");
+ continue;
+ } else if (ret == ERR_TS_CACHE_MISS) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "TS cache doesn't contain this DN, skipping\n");
+ continue;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge timestamp cache values for %s\n",
+ ldb_dn_get_linearized(res->msgs[c]->dn));
+ /* non-fatal */
+ continue;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *ctx,
+ struct ldb_result *ts_res,
+ struct ldb_result **_ts_cache_res,
+ const char *attrs[])
+{
+ errno_t ret;
+ size_t ts_cache_res_count = 0;
+ struct ldb_result *ts_cache_res = NULL;
+
+ if (ts_res == NULL || ctx->ldb_ts == NULL) {
+ return EOK;
+ }
+
+ ts_cache_res = talloc_zero(mem_ctx, struct ldb_result);
+ if (ts_cache_res == NULL) {
+ return ENOMEM;
+ }
+ ts_cache_res->msgs = talloc_zero_array(ts_cache_res,
+ struct ldb_message *,
+ ts_res->count);
+ if (ts_cache_res->msgs == NULL) {
+ talloc_free(ts_cache_res);
+ return ENOMEM;
+ }
+
+ for (size_t c = 0; c < ts_res->count; c++) {
+ ret = merge_msg_sysdb_attrs(ts_cache_res->msgs,
+ ctx,
+ ts_res->msgs[c],
+ &ts_cache_res->msgs[ts_cache_res_count],
+ attrs);
+ if ((ret != EOK) || (ts_cache_res->msgs[ts_cache_res_count] == NULL)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot merge sysdb cache values for %s\n",
+ ldb_dn_get_linearized(ts_res->msgs[c]->dn));
+ /* non-fatal, just skip */
+ continue;
+ }
+ ts_cache_res_count += 1;
+ }
+ ts_cache_res->count = ts_cache_res_count;
+
+ *_ts_cache_res = ts_cache_res;
+ return EOK;
+}
+
+errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
+ size_t msgs_count,
+ struct ldb_message **msgs,
+ const char *attrs[])
+{
+ struct ldb_result res;
+
+ res.count = msgs_count;
+ res.msgs = msgs;
+ return sysdb_merge_res_ts_attrs(ctx, &res, attrs);
+}
+
+/* users */
+
+int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_PWNAM_FILTER,
+ lc_sanitized_name,
+ sanitized_name, sanitized_name);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 1) {
+ /* We expected either 0 or 1 result for search with
+ * SYSDB_PWNAM_FILTER, but we got more. This error
+ * is handled individually depending on what function
+ * called sysdb_getpwnam, so we just print a message
+ * here and let the caller decide what error code to
+ * propagate based on res->count > 1. */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Search for [%s] returned multiple results. It can be an email "
+ "address shared among multiple users or an email address of a "
+ "user that conflicts with another user's fully qualified name. "
+ "SSSD will not be able to handle those users properly.\n",
+ sanitized_name);
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getpwnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_user_override_by_name(tmp_ctx, domain, name,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_override_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getpwnam failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getpwuid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ unsigned long int ul_uid = uid;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, SYSDB_PWUID_FILTER, ul_uid);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getpwuid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ struct ldb_result **res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_user_override_by_uid(tmp_ctx, domain, uid,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_user_override_by_uid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getpwuid(tmp_ctx, domain, uid, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getpwuid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static char *filter_attribute(TALLOC_CTX *mem_ctx,
+ const char *attr,
+ const char *attr_filter,
+ const char *domain,
+ char *filter)
+{
+ char *new_filter = NULL;
+ const char *value;
+
+ if (filter == NULL) {
+ return NULL;
+ }
+
+ if (domain != NULL && is_sysdb_name(attr)) {
+ value = sss_create_internal_fqname(mem_ctx, attr_filter, domain);
+ } else {
+ value = attr_filter;
+ }
+
+ if (value != NULL) {
+ new_filter = talloc_asprintf_append(filter, "(%s=%s)", attr, value);
+ }
+
+ return new_filter;
+}
+
+static char *enum_filter(TALLOC_CTX *mem_ctx,
+ const char *base_filter,
+ const char *attr,
+ const char *attr_filter,
+ const char *domain,
+ const char *addtl_filter)
+{
+ char *filter;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ if ((attr == NULL || attr_filter == NULL) && addtl_filter == NULL) {
+ filter = talloc_strdup(tmp_ctx, base_filter);
+ } else {
+ filter = talloc_asprintf(tmp_ctx, "(&%s", base_filter);
+
+ if (filter != NULL && attr != NULL && attr_filter != NULL) {
+ filter = filter_attribute(tmp_ctx, attr, attr_filter, domain, filter);
+ }
+
+ if (filter != NULL && addtl_filter != NULL) {
+ filter = talloc_asprintf_append(filter, "%s", addtl_filter);
+ }
+
+ if (filter != NULL) {
+ filter = talloc_asprintf_append(filter, ")");
+ }
+ }
+
+ if (filter) {
+ talloc_steal(mem_ctx, filter);
+ }
+
+ talloc_free(tmp_ctx);
+ return filter;
+}
+
+int sysdb_getpwupn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ bool domain_scope,
+ const char *upn,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_user_by_upn_res(tmp_ctx, domain, domain_scope, upn, attrs, &res);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn_res() failed.\n");
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ const char *attrs[],
+ struct ldb_result *ts_res,
+ const char *filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_result *res;
+ errno_t ret;
+
+ if (ts_res->count == 0) {
+ *_res = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_dn *base_dn,
+ enum ldb_scope scope,
+ enum sysdb_cache_type search_cache,
+ const char *filter,
+ const char *attrs[],
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct ldb_result *res;
+ errno_t ret;
+ struct ldb_message **ts_msgs = NULL;
+ struct ldb_result *ts_cache_res = NULL;
+ size_t ts_count;
+
+ if (filter == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (search_cache) {
+ case SYSDB_CACHE_TYPE_PERSISTENT: {
+ /* We only care about searching the persistent db */
+ ts_cache_res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (ts_cache_res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ts_cache_res->count = 0;
+ ts_cache_res->msgs = NULL;
+
+ break;
+ }
+
+ case SYSDB_CACHE_TYPE_TIMESTAMP:
+ /* FALLTHOUGH*/
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ default: {
+ /* Because the timestamp database does not contain all the
+ * attributes, we need to search the persistent db for each
+ * of the entries found and merge the results
+ */
+ struct ldb_result ts_res;
+
+ /* We assume that some of the attributes are more up-to-date in
+ * timestamps db and we're supposed to search by them, so let's
+ * first search the timestamp db
+ */
+ ret = sysdb_search_ts_entry(tmp_ctx, domain->sysdb, base_dn,
+ scope, filter, attrs,
+ &ts_count, &ts_msgs);
+ if (ret == ENOENT) {
+ ts_count = 0;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ memset(&ts_res, 0, sizeof(struct ldb_result));
+ ts_res.count = ts_count;
+ ts_res.msgs = ts_msgs;
+
+ /* Overlay the results from the main cache with the ts attrs */
+ ret = merge_res_sysdb_attrs(tmp_ctx,
+ domain->sysdb,
+ &ts_res,
+ &ts_cache_res,
+ attrs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ break;
+ }
+ }
+
+ switch (search_cache) {
+ case SYSDB_CACHE_TYPE_TIMESTAMP: {
+ /* The filter only contains timestamp attrs, no need to search the
+ * persistent db
+ */
+ if (ts_cache_res) {
+ res->count = ts_cache_res->count;
+ res->msgs = talloc_steal(res, ts_cache_res->msgs);
+ }
+
+ break;
+ }
+
+ case SYSDB_CACHE_TYPE_PERSISTENT:
+ /* FALLTHOUGH*/
+ SSS_ATTRIBUTE_FALLTHROUGH;
+ default: {
+ /* Because some of the attributes being searched might exist in the persistent
+ * database only, we also search the persistent db
+ */
+ size_t count;
+
+ ret = sysdb_search_entry(res, domain->sysdb, base_dn, scope,
+ filter, attrs, &count, &res->msgs);
+ if (ret == ENOENT) {
+ res->count = 0;
+ } else if (ret != EOK) {
+ goto done;
+ }
+ res->count = count; /* Just to cleanly assign size_t to unsigned */
+
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ break;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx,
+ struct ldb_result *ts_res,
+ const char *name_filter,
+ const char *domain,
+ char **_dn_filter)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *dn_filter;
+ const char *fqname;
+ errno_t ret;
+
+ if (ts_res->count == 0) {
+ *_dn_filter = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (name_filter == NULL) {
+ dn_filter = talloc_asprintf(tmp_ctx, "(|");
+ } else {
+ fqname = sss_create_internal_fqname(tmp_ctx, name_filter, domain);
+ if (fqname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, fqname);
+ }
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (size_t i = 0; i < ts_res->count; i++) {
+ dn_filter = talloc_asprintf_append(
+ dn_filter,
+ "(%s=%s)",
+ SYSDB_DN,
+ ldb_dn_get_linearized(ts_res->msgs[i]->dn));
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ dn_filter = talloc_asprintf_append(dn_filter, (name_filter == NULL ? ")" : "))"));
+ if (dn_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_dn_filter = talloc_steal(mem_ctx, dn_filter);
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ char *filter = NULL;
+ char *dn_filter = NULL;
+ const char *ts_filter = NULL;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_result *ts_cache_res = NULL;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Do not look for the user's attribute in the timestamp db as it could
+ * not be present. Only look for the name. */
+ if (attr == NULL || is_sysdb_name(attr)) {
+ ts_filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER,
+ NULL, NULL, NULL, addtl_filter);
+ if (ts_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n",
+ ts_filter);
+
+ ret = sysdb_search_ts_users(tmp_ctx, domain, ts_filter,
+ sysdb_ts_cache_attrs,
+ &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = ENOENT;
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, attr_filter, domain->name,
+ &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp entries with [%s]\n",
+ dn_filter);
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
+ dn_filter, &ts_cache_res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+ }
+
+ filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER,
+ attr, attr_filter, domain->name, addtl_filter);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching cache with [%s]\n", filter);
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ if (ts_cache_res != NULL) {
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumpwent_filter(mem_ctx, domain, NULL, NULL, NULL, _res);
+}
+
+int sysdb_enumpwent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *attr,
+ const char *attr_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ size_t c;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_enumpwent_filter(tmp_ctx, domain, attr, attr_filter,
+ addtl_filter, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumpwent failed.\n");
+ goto done;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ for (c = 0; c < res->count; c++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ /* enumeration assumes that the cache is up-to-date, hence we do not
+ * need to handle ENOENT separately. */
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumpwent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumpwent_filter_with_views(mem_ctx, domain, NULL, NULL, NULL, _res);
+}
+
+/* groups */
+
+static int mpg_convert(struct ldb_message *msg)
+{
+ struct ldb_message_element *el;
+ struct ldb_val *val = NULL;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+ if (!el) return EINVAL;
+
+ /* see if this is a user to convert to a group */
+ for (i = 0; i < el->num_values; i++) {
+ val = &(el->values[i]);
+ if (strncasecmp(SYSDB_USER_CLASS,
+ (char *)val->data, val->length) == 0) {
+ break;
+ }
+ }
+ /* no, leave as is */
+ if (i == el->num_values) return EOK;
+
+ /* yes, convert */
+ val->data = (uint8_t *)talloc_strdup(msg, SYSDB_GROUP_CLASS);
+ if (val->data == NULL) return ENOMEM;
+ val->length = strlen(SYSDB_GROUP_CLASS);
+
+ return EOK;
+}
+
+static int mpg_res_convert(struct ldb_result *res)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < res->count; i++) {
+ ret = mpg_convert(res->msgs[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return EOK;
+}
+
+int sysdb_getgrnam_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_group_override_by_name(tmp_ctx, domain, name,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_group_override_by_name failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getgrnam(tmp_ctx, domain, name, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getgrnam failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (orig_obj->count == 1) {
+ if (DOM_HAS_VIEWS(domain)) {
+ if (!is_local_view(domain->view_name)) {
+ el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
+ if (el != NULL && el->num_values != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
+ "entries which must be resolved before overrides can be "
+ "applied.\n",
+ ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ /* Must be called even without views to check to
+ * SYSDB_DEFAULT_OVERRIDE_NAME */
+ ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Returning empty result.\n");
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_GRSRC_ATTRS;
+ const char *fmt_filter;
+ char *sanitized_name;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res = NULL;
+ char *lc_sanitized_name;
+ const char *originalad_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* In case the domain supports magic private groups we *must*
+ * check whether the searched name is the very same as the
+ * originalADname attribute.
+ *
+ * In case those are not the same, we're dealing with an
+ * override and in order to return the proper overridden group
+ * we must use the very same search used by a non-mpg domain
+ */
+ fmt_filter = SYSDB_GRNAM_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter,
+ lc_sanitized_name, sanitized_name, sanitized_name);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 0) {
+ originalad_sanitized_name = ldb_msg_find_attr_as_string(
+ res->msgs[0], ORIGINALAD_PREFIX SYSDB_NAME, NULL);
+
+ if (originalad_sanitized_name != NULL
+ && !sss_string_equal(domain->case_sensitive,
+ originalad_sanitized_name,
+ sanitized_name)) {
+ fmt_filter = SYSDB_GRNAM_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ res = NULL;
+ }
+ }
+ } else {
+ fmt_filter = SYSDB_GRNAM_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We just do the ldb_search here in case domain is *not* a MPG *or*
+ * it's a MPG and we're dealing with a overridden group, which has to
+ * use the very same filter as a non MPG domain. */
+ if (res == NULL) {
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter,
+ lc_sanitized_name, sanitized_name, sanitized_name);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ struct ldb_message_element *el;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_group_override_by_gid(tmp_ctx, domain, gid,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_group_override_by_gid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_getgrgid(tmp_ctx, domain, gid, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_getgrgid failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (orig_obj->count == 1) {
+ if (DOM_HAS_VIEWS(domain)) {
+ if (!is_local_view(domain->view_name)) {
+ el = ldb_msg_find_element(orig_obj->msgs[0], SYSDB_GHOST);
+ if (el != NULL && el->num_values != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Group object [%s], contains ghost "
+ "entries which must be resolved before overrides can be "
+ "applied.\n",
+ ldb_dn_get_linearized(orig_obj->msgs[0]->dn));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ /* Must be called even without views to check to
+ * SYSDB_DEFAULT_OVERRIDE_NAME */
+ ret = sysdb_add_group_member_overrides(domain, orig_obj->msgs[0],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "Returning empty result.\n");
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid_attrs(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ const char **additional_attrs,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ unsigned long int ul_gid = gid;
+ unsigned long int ul_originalad_gid;
+ const char *fmt_filter;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res = NULL;
+ int ret;
+ static const char *default_attrs[] = SYSDB_GRSRC_ATTRS;
+ const char **attrs = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (additional_attrs == NULL) {
+ attrs = default_attrs;
+ } else {
+ ret = add_strings_lists(tmp_ctx, additional_attrs, default_attrs,
+ false, discard_const(&attrs));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
+ goto done;
+ }
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ /* In case the domain supports magic private groups we *must*
+ * check whether the searched gid is the very same as the
+ * originalADgidNumber attribute.
+ *
+ * In case those are not the same, we're dealing with an
+ * override and in order to return the proper overridden group
+ * we must use the very same search used by a non-mpg domain
+ */
+ fmt_filter = SYSDB_GRGID_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter, ul_gid, ul_gid, ul_gid);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (res->count > 0) {
+ ul_originalad_gid = ldb_msg_find_attr_as_uint64(
+ res->msgs[0], ORIGINALAD_PREFIX SYSDB_GIDNUM, 0);
+
+ if (ul_originalad_gid != 0 && ul_originalad_gid != ul_gid) {
+ fmt_filter = SYSDB_GRGID_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ res = NULL;
+ }
+ }
+ } else {
+ fmt_filter = SYSDB_GRGID_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We just do the ldb_search here in case domain is *not* a MPG *or*
+ * it's a MPG and we're dealing with a overridden group, which has to
+ * use the very same filter as a non MPG domain. */
+ if (res == NULL) {
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, fmt_filter, ul_gid);
+ if (ret != EOK) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ gid_t gid,
+ struct ldb_result **_res)
+{
+ return sysdb_getgrgid_attrs(mem_ctx, domain, gid, NULL, _res);
+}
+
+int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_GRSRC_ATTRS;
+ const char *filter = NULL;
+ const char *ts_filter = NULL;
+ const char *base_filter;
+ char *dn_filter = NULL;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ struct ldb_result ts_res;
+ struct ldb_result *ts_cache_res;
+ int ret, lret;
+
+ if (_res == NULL) {
+ return EINVAL;
+ }
+ *_res = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (sss_domain_is_mpg(domain)) {
+ base_filter = SYSDB_GRENT_MPG_FILTER;
+ base_dn = sysdb_domain_dn(tmp_ctx, domain);
+ } else {
+ base_filter = SYSDB_GRENT_FILTER;
+ base_dn = sysdb_group_base_dn(tmp_ctx, domain);
+ }
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ts_filter = enum_filter(tmp_ctx, base_filter,
+ NULL, NULL, NULL, addtl_filter);
+ if (ts_filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n", ts_filter);
+
+ ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_filter,
+ sysdb_ts_cache_attrs,
+ &ts_res);
+ if (ret == ERR_NO_TS) {
+ ret = ENOENT;
+ }
+
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ ret = sysdb_enum_dn_filter(tmp_ctx, &ts_res, name_filter, domain->name,
+ &dn_filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res,
+ dn_filter, &ts_cache_res);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ filter = enum_filter(tmp_ctx, base_filter,
+ SYSDB_NAME, name_filter, domain->name, addtl_filter);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Searching cache with [%s]\n", filter);
+
+ lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (lret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(lret);
+ goto done;
+ }
+
+ ret = mpg_res_convert(res);
+ if (ret) {
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ res = sss_merge_ldb_results(res, ts_cache_res);
+ if (res == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumgrent_filter(mem_ctx, domain, NULL, 0, _res);
+}
+
+int sysdb_enumgrent_filter_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_filter,
+ const char *addtl_filter,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ size_t c;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_enumgrent_filter(tmp_ctx, domain, name_filter, addtl_filter, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumgrent failed.\n");
+ goto done;
+ }
+
+ for (c = 0; c < res->count; c++) {
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ /* enumeration assumes that the cache is up-to-date, hence we do not
+ * need to handle ENOENT separately. */
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_add_group_member_overrides(domain, res->msgs[c],
+ DOM_HAS_VIEWS(domain));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_group_member_overrides failed.\n");
+ goto done;
+ }
+ }
+
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_enumgrent_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result **_res)
+{
+ return sysdb_enumgrent_filter_with_views(mem_ctx, domain, NULL, NULL, _res);
+}
+
+int sysdb_initgroups(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *user_dn;
+ struct ldb_request *req;
+ struct ldb_control **ctrl;
+ struct ldb_asq_control *control;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_getpwnam failed: [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* User is not cached yet */
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+ goto done;
+
+ } else if (res->count != 1) {
+ ret = EIO;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ goto done;
+ }
+
+ /* no need to steal the dn, we are not freeing the result */
+ user_dn = res->msgs[0]->dn;
+
+ /* note we count on the fact that the default search callback
+ * will just keep appending values. This is by design and can't
+ * change so it is ok to already have a result (from the getpwnam)
+ * even before we call the next search */
+
+ ctrl = talloc_array(tmp_ctx, struct ldb_control *, 2);
+ if (!ctrl) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[1] = NULL;
+ ctrl[0] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[0]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[0]->oid = LDB_CONTROL_ASQ_OID;
+ ctrl[0]->critical = 1;
+ control = talloc(ctrl[0], struct ldb_asq_control);
+ if (!control) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->request = 1;
+ control->source_attribute = talloc_strdup(control, SYSDB_INITGR_ATTR);
+ if (!control->source_attribute) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->src_attr_len = strlen(control->source_attribute);
+ ctrl[0]->data = control;
+
+ ret = ldb_build_search_req(&req, domain->sysdb->ldb, tmp_ctx,
+ user_dn, LDB_SCOPE_BASE,
+ SYSDB_INITGR_FILTER, attrs, ctrl,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_request(domain->sysdb->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_initgroups_by_upn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *upn,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ const char *sysdb_name;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ size_t i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_user_by_upn(tmp_ctx, domain, false, upn, attrs, &msg);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn() failed.\n");
+ goto done;
+ }
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ res->count = 0;
+ res->msgs = NULL;
+ } else {
+ sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (sysdb_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Sysdb entry does not have a name.\n");
+ return EINVAL;
+ }
+
+ ret = sysdb_initgroups(tmp_ctx, domain, sysdb_name, &res);
+ if (ret == EOK && DOM_HAS_VIEWS(domain)) {
+ for (i = 0; i < res->count; i++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[i],
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_overrides_to_object() failed.\n");
+ return ret;
+ }
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_initgroups_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *user_dn;
+ struct ldb_request *req;
+ struct ldb_control **ctrl;
+ struct ldb_asq_control *control;
+ static const char *attrs[] = SYSDB_INITGR_ATTRS;
+ int ret;
+ size_t c;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam_with_views(tmp_ctx, domain, name, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_getpwnam failed: [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ /* User is not cached yet */
+ *_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+ goto done;
+
+ } else if (res->count != 1) {
+ ret = EIO;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ goto done;
+ }
+
+ /* no need to steal the dn, we are not freeing the result */
+ user_dn = res->msgs[0]->dn;
+
+ /* note we count on the fact that the default search callback
+ * will just keep appending values. This is by design and can't
+ * change so it is ok to already have a result (from the getpwnam)
+ * even before we call the next search */
+
+ ctrl = talloc_array(tmp_ctx, struct ldb_control *, 2);
+ if (!ctrl) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[1] = NULL;
+ ctrl[0] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[0]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ctrl[0]->oid = LDB_CONTROL_ASQ_OID;
+ ctrl[0]->critical = 1;
+ control = talloc(ctrl[0], struct ldb_asq_control);
+ if (!control) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->request = 1;
+ control->source_attribute = talloc_strdup(control, SYSDB_INITGR_ATTR);
+ if (!control->source_attribute) {
+ ret = ENOMEM;
+ goto done;
+ }
+ control->src_attr_len = strlen(control->source_attribute);
+ ctrl[0]->data = control;
+
+ ret = ldb_build_search_req(&req, domain->sysdb->ldb, tmp_ctx,
+ user_dn, LDB_SCOPE_BASE,
+ SYSDB_INITGR_FILTER, attrs, ctrl,
+ res, ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_request(domain->sysdb->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ /* Skip user entry because it already has override values added */
+ for (c = 1; c < res->count; c++) {
+ ret = sysdb_add_overrides_to_object(domain, res->msgs[c], NULL,
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *res;
+ char *sanitized_name;
+ char *lc_sanitized_name;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, name, domain,
+ &sanitized_name, &lc_sanitized_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
+ LDB_SCOPE_SUBTREE, attributes,
+ SYSDB_PWNAM_FILTER, lc_sanitized_name, sanitized_name,
+ sanitized_name);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ /* Merge in the timestamps from the fast ts db */
+ ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attributes);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n");
+ /* non-fatal */
+ ret = EOK;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_user_attr_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ const char **attributes,
+ struct ldb_result **_res)
+{
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ const char **attrs = NULL;
+ const char *mandatory_override_attrs[] = {SYSDB_OVERRIDE_DN,
+ SYSDB_OVERRIDE_OBJECT_DN,
+ NULL};
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ attrs = attributes;
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = add_strings_lists(tmp_ctx, attributes, mandatory_override_attrs,
+ false, discard_const(&attrs));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_search_user_override_attrs_by_name(tmp_ctx, domain, name,
+ attrs, &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_user_override_attrs_by_name failed.\n");
+ return ret;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_get_user_attr(tmp_ctx, domain, name, attrs, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_user_attr failed.\n");
+ return ret;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj ->msgs[0],
+ attrs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ return ret;
+ }
+
+ if (ret == ENOENT) {
+ *_res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*_res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ }
+ }
+
+ *_res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+/* Get string until the first delimiter and strip out
+ * leading and trailing whitespaces.
+ */
+static errno_t sysdb_netgr_split_triple_string(TALLOC_CTX *mem_ctx,
+ const char **in,
+ const char delimiter,
+ char **out)
+{
+ size_t len;
+ const char *p = *in;
+ const char *begin;
+
+ /* Remove any leading whitespace */
+ while (*p && isspace(*p)) p++;
+ begin = p;
+
+ /* Find the delimiter */
+ while (*p && *p != delimiter) p++;
+
+ if (!*p) {
+ /* No delimiter was found: parse error */
+ return EINVAL;
+ }
+
+ len = p - begin;
+ /* Remove trailing spaces */
+ while (len > 0 && isspace(begin[len - 1])) len--;
+
+ *out = NULL;
+ if (len > 0) {
+ /* Copy the output string */
+ *out = talloc_strndup(mem_ctx, begin, len);
+ if (!*out) {
+ return ENOMEM;
+ }
+ }
+ p++;
+
+ *in = p;
+ return EOK;
+}
+
+
+
+/* This function splits a three-tuple into three strings
+ * It strips out any whitespace between the parentheses
+ * and commas. Leading and trailing whitespace is
+ * ignored.
+ *
+ * This behavior is compatible with nss_ldap's
+ * implementation.
+ */
+static errno_t sysdb_netgr_split_triple(TALLOC_CTX *mem_ctx,
+ const char *triple,
+ char **hostname,
+ char **username,
+ char **domainname)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ const char *p = triple;
+
+ char *host = NULL;
+ char *user = NULL;
+ char *domain = NULL;
+
+ /* Pre-set the values to NULL here so if they are not
+ * copied, we don't return garbage below.
+ */
+ *hostname = NULL;
+ *username = NULL;
+ *domainname = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* Remove any leading whitespace */
+ while (*p && isspace(*p)) p++;
+
+ if (*p != '(') {
+ /* Triple must start and end with parentheses */
+ ret = EINVAL;
+ goto done;
+ }
+ p++;
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ',', &host);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ',', &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_netgr_split_triple_string(tmp_ctx, &p, ')', &domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* skip trailing whitespace */
+ while (*p && isspace(*p)) p++;
+
+ if (*p) {
+ /* Extra data after the closing parenthesis
+ * is a parse error
+ */
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Return any non-NULL values */
+ if (host) {
+ *hostname = talloc_steal(mem_ctx, host);
+ }
+
+ if (user) {
+ *username = talloc_steal(mem_ctx, user);
+ }
+
+ if (domain) {
+ *domainname = talloc_steal(mem_ctx, domain);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_netgr_to_entries(TALLOC_CTX *mem_ctx,
+ struct ldb_result *res,
+ struct sysdb_netgroup_ctx ***entries,
+ size_t *netgroup_count)
+{
+ errno_t ret;
+ size_t size = 0;
+ size_t c = 0;
+ char *triple_str;
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_netgroup_ctx **tmp_entry = NULL;
+ struct ldb_message_element *el;
+ int i, j;
+
+ if(!res || res->count == 0) {
+ return ENOENT;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ for (i=0; i < res->count; i++) {
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
+ if (el != NULL) {
+ size += el->num_values;
+ }
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+ if (el != NULL) {
+ size += el->num_values;
+ }
+ }
+
+ tmp_entry = talloc_array(tmp_ctx, struct sysdb_netgroup_ctx *, size + 1);
+ if (tmp_entry == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (size != 0) {
+ for (i=0; i < res->count; i++) {
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_TRIPLE);
+ if (el != NULL) {
+ /* Copy in all of the entries */
+ for(j = 0; j < el->num_values; j++) {
+ triple_str = talloc_strndup(tmp_ctx,
+ (const char *)el->values[j].data,
+ el->values[j].length);
+ if (!triple_str) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c] = talloc_zero(tmp_entry,
+ struct sysdb_netgroup_ctx);
+ if (!tmp_entry[c]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c]->type = SYSDB_NETGROUP_TRIPLE_VAL;
+ ret = sysdb_netgr_split_triple(tmp_entry[c],
+ triple_str,
+ &tmp_entry[c]->value.triple.hostname,
+ &tmp_entry[c]->value.triple.username,
+ &tmp_entry[c]->value.triple.domainname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Cannot split netgroup triple [%s], "
+ "this attribute will be skipped \n",
+ triple_str);
+ continue;
+ }
+
+ c++;
+ }
+ }
+ el = ldb_msg_find_element(res->msgs[i], SYSDB_NETGROUP_MEMBER);
+ if (el != NULL) {
+ for(j = 0; j < el->num_values; j++) {
+ tmp_entry[c] = talloc_zero(tmp_entry,
+ struct sysdb_netgroup_ctx);
+ if (!tmp_entry[c]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_entry[c]->type = SYSDB_NETGROUP_GROUP_VAL;
+ tmp_entry[c]->value.groupname = talloc_strndup(tmp_entry[c],
+ (const char *)el->values[j].data,
+ el->values[j].length);
+ if (tmp_entry[c]->value.groupname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ c++;
+ }
+ }
+ }
+ }
+
+ /* Add NULL terminator */
+ tmp_entry[c] = NULL;
+
+ *entries = talloc_steal(mem_ctx, tmp_entry);
+ *netgroup_count = c;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgroup,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = SYSDB_NETGR_ATTRS;
+ struct ldb_dn *base_dn;
+ struct ldb_result *result = NULL;
+ char *sanitized_netgroup;
+ char *lc_sanitized_netgroup;
+ char *netgroup_dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE,
+ domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, netgroup, domain,
+ &sanitized_netgroup,
+ &lc_sanitized_netgroup);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ netgroup_dn = talloc_asprintf(tmp_ctx, SYSDB_TMPL_NETGROUP,
+ sanitized_netgroup, domain->name);
+ if (!netgroup_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ SSS_LDB_SEARCH(ret, domain->sysdb->ldb, tmp_ctx, &result, base_dn,
+ LDB_SCOPE_SUBTREE, attrs,
+ SYSDB_NETGR_TRIPLES_FILTER, lc_sanitized_netgroup,
+ sanitized_netgroup, sanitized_netgroup,
+ netgroup_dn);
+
+ if (ret == EOK || ret == ENOENT) {
+ *res = talloc_steal(mem_ctx, result);
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *netgrname,
+ const char **attributes,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *result;
+ char *sanitized_netgroup;
+ char *lc_sanitized_netgroup;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+ SYSDB_TMPL_NETGROUP_BASE, domain->name);
+ if (!base_dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, netgrname, domain,
+ &sanitized_netgroup,
+ &lc_sanitized_netgroup);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &result, base_dn,
+ LDB_SCOPE_SUBTREE, attributes,
+ SYSDB_NETGR_FILTER,
+ lc_sanitized_netgroup,
+ sanitized_netgroup,
+ sanitized_netgroup);
+ if (ret) {
+ ret = sysdb_error_to_errno(ret);
+ goto done;
+ }
+
+ *res = talloc_steal(mem_ctx, result);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ struct sss_domain_info *parent_dom,
+ enum sysdb_member_type mtype,
+ const char *name,
+ char ***_direct_parents)
+{
+ errno_t ret;
+ const char *dn;
+ char *sanitized_dn;
+ struct ldb_dn *basedn;
+ static const char *group_attrs[] = { SYSDB_NAME, NULL };
+ const char *member_filter;
+ size_t direct_sysdb_count = 0;
+ struct ldb_message **direct_sysdb_groups = NULL;
+ char **direct_parents = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i, pi;
+ const char *tmp_str;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ if (mtype == SYSDB_MEMBER_USER) {
+ dn = sysdb_user_strdn(tmp_ctx, dom->name, name);
+ } else if (mtype == SYSDB_MEMBER_GROUP) {
+ dn = sysdb_group_strdn(tmp_ctx, dom->name, name);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown member type %d\n", mtype);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, dn, &sanitized_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ member_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))",
+ SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS,
+ SYSDB_MEMBER, sanitized_dn);
+ if (!member_filter) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (parent_dom == NULL) {
+ basedn = sysdb_base_dn(dom->sysdb, tmp_ctx);
+ } else {
+ basedn = sysdb_group_base_dn(tmp_ctx, parent_dom);
+ }
+ if (!basedn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "searching sysdb with filter [%s]\n", member_filter);
+
+ ret = sysdb_search_entry(tmp_ctx, dom->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, member_filter, group_attrs,
+ &direct_sysdb_count, &direct_sysdb_groups);
+ if (ret == ENOENT) {
+ direct_sysdb_count = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed: [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* EOK */
+ /* Get the list of sysdb groups by name */
+ direct_parents = talloc_array(tmp_ctx, char *, direct_sysdb_count+1);
+ if (!direct_parents) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pi = 0;
+ for(i = 0; i < direct_sysdb_count; i++) {
+ tmp_str = ldb_msg_find_attr_as_string(direct_sysdb_groups[i],
+ SYSDB_NAME, NULL);
+ if (!tmp_str) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A group with no name?\n");
+ /* This should never happen, but if it does, just continue */
+ continue;
+ }
+
+ direct_parents[pi] = talloc_strdup(direct_parents, tmp_str);
+ if (!direct_parents[pi]) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = EIO;
+ goto done;
+ }
+ pi++;
+ }
+ direct_parents[pi] = NULL;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "%s is a member of %zu sysdb groups\n",
+ name, direct_sysdb_count);
+ *_direct_parents = talloc_steal(mem_ctx, direct_parents);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name_or_upn_or_sid,
+ const char **_cname)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ const char *cname;
+ struct ldb_message *msg;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_getpwnam(tmp_ctx, domain, name_or_upn_or_sid, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot canonicalize username\n");
+ goto done;
+ }
+
+ if (res->count == 0) {
+ ret = sysdb_search_user_by_upn(tmp_ctx, domain, false, name_or_upn_or_sid,
+ NULL, &msg);
+ if (ret == ENOENT) {
+ ret = sysdb_search_user_by_sid_str(tmp_ctx, domain,
+ name_or_upn_or_sid, NULL, &msg);
+ if (ret == ENOENT) {
+ ret = sysdb_search_object_by_uuid(tmp_ctx, domain,
+ name_or_upn_or_sid, NULL,
+ &res);
+ if (ret == EOK && res->count == 1) {
+ msg = res->msgs[0];
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_object_by_uuid failed or returned "
+ "more than one result [%d][%s].\n",
+ ret, sss_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+ }
+ if (ret != EOK) {
+ /* User cannot be found in cache */
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to find user [%s] in cache: %d\n",
+ name_or_upn_or_sid, ret);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "User [%s] is missing in cache\n",
+ name_or_upn_or_sid);
+ }
+ goto done;
+ }
+ } else if (res->count == 1) {
+ msg = res->msgs[0];
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_getpwnam returned count: [%d]\n", res->count);
+ ret = EIO;
+ goto done;
+ }
+
+ cname = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (!cname) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "User '%s' without a name?\n", name_or_upn_or_sid);
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ *_cname = talloc_steal(mem_ctx, cname);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sysdb_search_user_by_cert_with_views(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *cert,
+ struct ldb_result **res)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *orig_obj = NULL;
+ struct ldb_result *override_obj = NULL;
+ const char *attrs[] = SYSDB_PW_ATTRS;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ /* If there are views we first have to search the overrides for matches */
+ if (DOM_HAS_VIEWS(domain)) {
+ ret = sysdb_search_override_by_cert(tmp_ctx, domain, cert, attrs,
+ &override_obj, &orig_obj);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_search_override_by_cert failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are no views or nothing was found in the overrides the
+ * original objects are searched. */
+ if (orig_obj == NULL) {
+ ret = sysdb_search_user_by_cert(tmp_ctx, domain, cert, &orig_obj);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_cert failed.\n");
+ goto done;
+ }
+ }
+
+ /* If there are views we have to check if override values must be added to
+ * the original object. */
+ if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+ ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+ override_obj == NULL ? NULL : override_obj->msgs[0],
+ NULL);
+ if (ret == ENOENT) {
+ *res = talloc_zero(mem_ctx, struct ldb_result);
+ if (*res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ } else {
+ ret = EOK;
+ }
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+ goto done;
+ }
+ }
+
+ *res = talloc_steal(mem_ctx, orig_obj);
+ ret = EOK;
+
+done:
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ldb_result *sss_merge_ldb_results(struct ldb_result *sysdb_res,
+ struct ldb_result *ts_res)
+{
+ size_t i, ii, count, total;
+ int ret;
+
+ if (ts_res == NULL || ts_res->count == 0) {
+ return sysdb_res;
+ }
+
+ total = sysdb_res->count + ts_res->count;
+ sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs,
+ struct ldb_message *,
+ total);
+ if (sysdb_res->msgs == NULL) {
+ return NULL;
+ }
+
+ /* FIXME - this is O(2), so inefficient for large sets! */
+ count = sysdb_res->count;
+ for (i = 0; i < ts_res->count; i++) {
+ for (ii = 0; ii < sysdb_res->count; ii++) {
+ ret = ldb_dn_compare(ts_res->msgs[i]->dn, sysdb_res->msgs[ii]->dn);
+ if (ret == 0) {
+ break;
+ }
+ }
+
+ if (ii < sysdb_res->count) {
+ /* We already have this DN but ts_res might be more up-to-date
+ * wrt timestamps */
+ sysdb_res->msgs[ii] = talloc_steal(sysdb_res, ts_res->msgs[i]);
+ continue;
+ }
+ /* new DN, merge */
+ sysdb_res->msgs[count] = talloc_steal(sysdb_res, ts_res->msgs[i]);
+ count++;
+ }
+
+ if (count < total) {
+ sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs,
+ struct ldb_message *,
+ count);
+ if (sysdb_res->msgs == NULL) {
+ return NULL;
+ }
+ }
+ sysdb_res->count = count;
+ return sysdb_res;
+}
+
+bool is_ts_cache_attr(const char *attrname)
+{
+ size_t i;
+
+ for (i = 0; sysdb_ts_cache_attrs[i] != NULL; i++) {
+ if (strcmp(attrname, sysdb_ts_cache_attrs[i]) == 0) {
+ break;
+ }
+ }
+
+ if (sysdb_ts_cache_attrs[i] == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+struct sysdb_attrs *sysdb_filter_ts_attrs(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *attrs)
+{
+ struct sysdb_attrs *ts_attrs;
+ int ti = 0;
+
+ ts_attrs = sysdb_new_attrs(mem_ctx);
+ if (ts_attrs == NULL) {
+ return NULL;
+ }
+
+ ts_attrs->a = talloc_zero_array(ts_attrs,
+ struct ldb_message_element,
+ attrs->num);
+ if (ts_attrs->a == NULL) {
+ talloc_free(ts_attrs);
+ return NULL;
+ }
+
+ for (int i = 0; i < attrs->num; i++) {
+ if (is_ts_cache_attr(attrs->a[i].name)) {
+ ts_attrs->a[ti] = attrs->a[i];
+ ti++;
+ }
+ }
+ ts_attrs->num = ti;
+
+ return ts_attrs;
+}