summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/sdap.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/providers/ldap/sdap.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/providers/ldap/sdap.c')
-rw-r--r--src/providers/ldap/sdap.c2129
1 files changed, 2129 insertions, 0 deletions
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
new file mode 100644
index 0000000..f5637c5
--- /dev/null
+++ b/src/providers/ldap/sdap.c
@@ -0,0 +1,2129 @@
+/*
+ SSSD
+
+ LDAP Helper routines
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com>
+
+ 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 "util/crypto/sss_crypto.h"
+#include "confdb/confdb.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap.h"
+#include "providers/ldap/sdap_range.h"
+#include "util/probes.h"
+
+/* =Retrieve-Options====================================================== */
+
+errno_t sdap_copy_map_entry(const struct sdap_attr_map *src_map,
+ struct sdap_attr_map *dst_map,
+ int entry_index)
+{
+ if (src_map[entry_index].name != NULL) {
+ dst_map[entry_index].name = talloc_strdup(dst_map,
+ src_map[entry_index].name);
+ if (dst_map[entry_index].name == NULL) {
+ return ENOMEM;
+ }
+ } else {
+ dst_map->name = NULL;
+ }
+
+ return EOK;
+}
+
+int sdap_copy_map(TALLOC_CTX *memctx,
+ struct sdap_attr_map *src_map,
+ int num_entries,
+ struct sdap_attr_map **_map)
+{
+ struct sdap_attr_map *map;
+ int i;
+
+ map = talloc_array(memctx, struct sdap_attr_map, num_entries + 1);
+ if (!map) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ map[i].opt_name = talloc_strdup(map, src_map[i].opt_name);
+ map[i].sys_name = talloc_strdup(map, src_map[i].sys_name);
+ if (map[i].opt_name == NULL || map[i].sys_name == NULL) {
+ return ENOMEM;
+ }
+
+ if (src_map[i].def_name != NULL) {
+ map[i].def_name = talloc_strdup(map, src_map[i].def_name);
+ map[i].name = talloc_strdup(map, src_map[i].def_name);
+ if (map[i].def_name == NULL || map[i].name == NULL) {
+ return ENOMEM;
+ }
+ } else {
+ map[i].def_name = NULL;
+ map[i].name = NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option %s has%s value %s\n",
+ map[i].opt_name, map[i].name ? "" : " no",
+ map[i].name ? map[i].name : "");
+ }
+
+ /* Include the sentinel */
+ memset(&map[num_entries], 0, sizeof(struct sdap_attr_map));
+
+ *_map = map;
+ return EOK;
+}
+
+static errno_t split_extra_attr(TALLOC_CTX *mem_ctx,
+ const char *conf_attr,
+ char **_sysdb_attr,
+ char **_ldap_attr)
+{
+ char *ldap_attr;
+ char *sysdb_attr;
+ char *sep;
+
+ sep = strchr(conf_attr, ':');
+ if (sep == NULL) {
+ sysdb_attr = talloc_strdup(mem_ctx, conf_attr);
+ ldap_attr = talloc_strdup(mem_ctx, conf_attr);
+ } else {
+ if (sep == conf_attr || *(sep + 1) == '\0') {
+ return ERR_INVALID_EXTRA_ATTR;
+ }
+
+ sysdb_attr = talloc_strndup(mem_ctx, conf_attr,
+ sep - conf_attr);
+ ldap_attr = talloc_strdup(mem_ctx, sep+1);
+ }
+
+ if (sysdb_attr == NULL || ldap_attr == NULL) {
+ return ENOMEM;
+ }
+
+ *_sysdb_attr = sysdb_attr;
+ *_ldap_attr = ldap_attr;
+ return EOK;
+}
+
+enum duplicate_t {
+ NOT_FOUND = 0,
+ ALREADY_IN_MAP, /* nothing to add */
+ CONFLICT_WITH_MAP /* attempt to redefine attribute */
+};
+
+static enum duplicate_t check_duplicate(struct sdap_attr_map *map,
+ int num_entries,
+ const char *sysdb_attr,
+ const char *ldap_attr)
+{
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ if (strcmp(map[i].sys_name, sysdb_attr) == 0) {
+ if (map[i].name != NULL && strcmp(map[i].name, ldap_attr) == 0) {
+ return ALREADY_IN_MAP;
+ } else {
+ return CONFLICT_WITH_MAP;
+ }
+ }
+ }
+
+ return NOT_FOUND;
+}
+
+int sdap_extend_map(TALLOC_CTX *memctx,
+ struct sdap_attr_map *src_map,
+ size_t num_entries,
+ char **extra_attrs,
+ struct sdap_attr_map **_map,
+ size_t *_new_size)
+{
+ struct sdap_attr_map *map;
+ size_t nextra = 0;
+ size_t i;
+ char *ldap_attr;
+ char *sysdb_attr;
+ errno_t ret;
+
+ *_map = src_map;
+ if (extra_attrs == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA, "No extra attributes\n");
+ *_new_size = num_entries;
+ return EOK;
+ }
+
+ for (nextra = 0; extra_attrs[nextra]; nextra++) ;
+ DEBUG(SSSDBG_FUNC_DATA, "%zu extra attributes\n", nextra);
+
+ map = talloc_realloc(memctx, src_map, struct sdap_attr_map,
+ num_entries + nextra + 1);
+ if (map == NULL) {
+ return ENOMEM;
+ }
+ *_map = map;
+
+ for (i = 0; *extra_attrs != NULL; extra_attrs++) {
+ ret = split_extra_attr(map, *extra_attrs, &sysdb_attr, &ldap_attr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot split %s\n", *extra_attrs);
+ continue;
+ }
+
+ ret = check_duplicate(map, num_entries, sysdb_attr, ldap_attr);
+ if (ret == ALREADY_IN_MAP) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Attribute %s (%s in LDAP) is already in map.\n",
+ sysdb_attr, ldap_attr);
+ continue;
+ } else if (ret == CONFLICT_WITH_MAP) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Attribute %s (%s in LDAP) is already used by SSSD, please "
+ "choose a different cache name\n", sysdb_attr, ldap_attr);
+ return ERR_DUP_EXTRA_ATTR;
+ }
+
+ map[num_entries+i].name = ldap_attr;
+ map[num_entries+i].sys_name = sysdb_attr;
+ map[num_entries+i].opt_name = talloc_strdup(map,
+ map[num_entries+i].name);
+ map[num_entries+i].def_name = talloc_strdup(map,
+ map[num_entries+i].name);
+ if (map[num_entries+i].opt_name == NULL ||
+ map[num_entries+i].sys_name == NULL ||
+ map[num_entries+i].name == NULL ||
+ map[num_entries+i].def_name == NULL) {
+ return ENOMEM;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Extending map with %s\n", *extra_attrs);
+
+ /* index must be incremented only for appended entry. */
+ i++;
+ }
+
+ nextra = i;
+
+ /* Sentinel */
+ memset(&map[num_entries+nextra], 0, sizeof(struct sdap_attr_map));
+
+ *_new_size = num_entries + nextra;
+ return EOK;
+}
+
+int sdap_extend_map_with_list(TALLOC_CTX *mem_ctx,
+ const struct sdap_options *opts,
+ int extra_attr_index,
+ struct sdap_attr_map *src_map,
+ size_t num_entries,
+ struct sdap_attr_map **_map,
+ size_t *_new_size)
+{
+ const char *extra_attrs;
+ char **extra_attrs_list;
+ errno_t ret;
+
+ *_map = src_map;
+ extra_attrs = dp_opt_get_string(opts->basic, extra_attr_index);
+ if (extra_attrs == NULL) {
+ *_new_size = num_entries;
+ return EOK;
+ }
+
+ /* split server parm into a list */
+ ret = split_on_separator(mem_ctx, extra_attrs, ',', true, true,
+ &extra_attrs_list, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to parse server list!\n");
+ return ret;
+ }
+
+
+ ret = sdap_extend_map(mem_ctx, src_map,
+ num_entries, extra_attrs_list,
+ _map, _new_size);
+ talloc_free(extra_attrs_list);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static void sdap_inherit_basic_options(char **inherit_opt_list,
+ struct dp_option *parent_opts,
+ struct dp_option *subdom_opts)
+{
+ int inherit_options[] = {
+ SDAP_SEARCH_TIMEOUT,
+ SDAP_NETWORK_TIMEOUT,
+ SDAP_OPT_TIMEOUT,
+ SDAP_OFFLINE_TIMEOUT,
+ SDAP_ENUM_REFRESH_TIMEOUT,
+ SDAP_ENUM_REFRESH_OFFSET,
+ SDAP_PURGE_CACHE_TIMEOUT,
+ SDAP_PURGE_CACHE_OFFSET,
+ SDAP_KRB5_KEYTAB,
+ SDAP_KRB5_TICKET_LIFETIME,
+ SDAP_ENUM_SEARCH_TIMEOUT,
+ SDAP_EXPIRE_TIMEOUT,
+ SDAP_EXPIRE_OFFSET,
+ SDAP_IDLE_TIMEOUT,
+ SDAP_AD_USE_TOKENGROUPS,
+ SDAP_OPTS_BASIC /* sentinel */
+ };
+ int i;
+
+ for (i = 0; inherit_options[i] != SDAP_OPTS_BASIC; i++) {
+ dp_option_inherit_match(inherit_opt_list,
+ inherit_options[i],
+ parent_opts,
+ subdom_opts);
+ }
+}
+
+static void sdap_inherit_user_options(char **inherit_opt_list,
+ struct sdap_attr_map *parent_user_map,
+ struct sdap_attr_map *child_user_map)
+{
+ int inherit_options[] = {
+ SDAP_AT_USER_PRINC,
+ SDAP_OPTS_USER /* sentinel */
+ };
+ int i;
+ int opt_index;
+ bool inherit_option;
+
+ for (i = 0; inherit_options[i] != SDAP_OPTS_USER; i++) {
+ opt_index = inherit_options[i];
+
+ inherit_option = string_in_list(parent_user_map[opt_index].opt_name,
+ inherit_opt_list,
+ false);
+ if (inherit_option == false) {
+ continue;
+ }
+
+ sdap_copy_map_entry(parent_user_map, child_user_map, opt_index);
+ }
+}
+
+void sdap_inherit_options(char **inherit_opt_list,
+ struct sdap_options *parent_sdap_opts,
+ struct sdap_options *child_sdap_opts)
+{
+ sdap_inherit_basic_options(inherit_opt_list,
+ parent_sdap_opts->basic,
+ child_sdap_opts->basic);
+
+ sdap_inherit_user_options(inherit_opt_list,
+ parent_sdap_opts->user_map,
+ child_sdap_opts->user_map);
+}
+
+int sdap_get_map(TALLOC_CTX *memctx,
+ struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct sdap_attr_map *def_map,
+ int num_entries,
+ struct sdap_attr_map **_map)
+{
+ struct sdap_attr_map *map;
+ char *name;
+ int i, ret;
+
+ map = talloc_zero_array(memctx, struct sdap_attr_map, num_entries + 1);
+ if (!map) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+
+ map[i].opt_name = def_map[i].opt_name;
+ map[i].def_name = def_map[i].def_name;
+ map[i].sys_name = def_map[i].sys_name;
+
+ ret = confdb_get_string(cdb, map, conf_path,
+ map[i].opt_name,
+ map[i].def_name,
+ &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to retrieve value for %s\n", map[i].opt_name);
+ talloc_zfree(map);
+ return EINVAL;
+ }
+
+ if (name) {
+ ret = sss_filter_sanitize(map, name, &map[i].name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not sanitize attribute [%s]\n", name);
+ talloc_zfree(map);
+ return EINVAL;
+ }
+ talloc_zfree(name);
+ } else {
+ map[i].name = NULL;
+ }
+
+ if (map[i].def_name && !map[i].name) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to process value for %s\n", map[i].opt_name);
+ talloc_zfree(map);
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option %s has%s value %s\n",
+ map[i].opt_name, map[i].name ? "" : " no",
+ map[i].name ? map[i].name : "");
+ }
+
+ *_map = map;
+ return EOK;
+}
+
+/* =Parse-msg============================================================= */
+
+static bool objectclass_matched(struct sdap_attr_map *map,
+ const char *objcl, int len);
+int sdap_parse_entry(TALLOC_CTX *memctx,
+ struct sdap_handle *sh, struct sdap_msg *sm,
+ struct sdap_attr_map *map, int attrs_num,
+ struct sysdb_attrs **_attrs,
+ bool disable_range_retrieval)
+{
+ struct sysdb_attrs *attrs;
+ BerElement *ber = NULL;
+ struct berval **vals;
+ struct ldb_val v;
+ char *str;
+ int lerrno;
+ int i, ret, ai;
+ int base_attr_idx = 0;
+ const char *name = NULL;
+ bool store;
+ bool base64;
+ char *base_attr;
+ uint32_t range_offset;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ lerrno = 0;
+ ret = ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "ldap_set_option failed [%s], ignored.\n",
+ sss_ldap_err2string(ret));
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ str = ldap_get_dn(sh->ldap, sm->msg);
+ if (!str) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(SSSDBG_CRIT_FAILURE, "ldap_get_dn failed: %d(%s)\n",
+ lerrno, sss_ldap_err2string(lerrno));
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "OriginalDN: [%s].\n", str);
+ PROBE(SDAP_PARSE_ENTRY, "OriginalDN", str, strlen(str));
+ ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, str);
+ ldap_memfree(str);
+ if (ret) goto done;
+
+ if (map) {
+ vals = ldap_get_values_len(sh->ldap, sm->msg, "objectClass");
+ if (!vals) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unknown entry type, no objectClasses found!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (i = 0; vals[i]; i++) {
+ if (objectclass_matched(map, vals[i]->bv_val, vals[i]->bv_len)) {
+ /* ok it's an entry of the right type */
+ break;
+ }
+ }
+ if (!vals[i]) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "objectClass not matching: %s\n",
+ map[0].name);
+ ldap_value_free_len(vals);
+ ret = EINVAL;
+ goto done;
+ }
+ ldap_value_free_len(vals);
+ }
+
+ str = ldap_first_attribute(sh->ldap, sm->msg, &ber);
+ if (!str) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ DEBUG(lerrno == LDAP_SUCCESS
+ ? SSSDBG_TRACE_LIBS
+ : SSSDBG_MINOR_FAILURE,
+ "Entry has no attributes [%d(%s)]!?\n",
+ lerrno, sss_ldap_err2string(lerrno));
+ if (map) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ while (str) {
+ base64 = false;
+
+ ret = sdap_parse_range(tmp_ctx, str, &base_attr, &range_offset,
+ disable_range_retrieval);
+ switch(ret) {
+ case EAGAIN:
+ /* This attribute contained range values and needs more to
+ * be retrieved
+ */
+ /* TODO: return the set of attributes that need additional retrieval
+ * For now, we'll continue below and treat it as regular values.
+ */
+ /* FALLTHROUGH */
+ case ECANCELED:
+ /* FALLTHROUGH */
+ case EOK:
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not determine if attribute [%s] was ranged\n", str);
+ goto done;
+ }
+
+ if (ret == ECANCELED) {
+ store = false;
+ } else if (map) {
+ for (i = 1; i < attrs_num; i++) {
+ /* check if this attr is valid with the chosen schema */
+ if (!map[i].name) continue;
+ /* check if it is an attr we are interested in */
+ if (strcasecmp(base_attr, map[i].name) == 0) break;
+ }
+ /* interesting attr */
+ if (i < attrs_num) {
+ store = true;
+ name = map[i].sys_name;
+ base_attr_idx = i;
+ if (strcmp(name, SYSDB_SSH_PUBKEY) == 0) {
+ base64 = true;
+ }
+ } else {
+ store = false;
+ }
+ } else {
+ name = base_attr;
+ store = true;
+ }
+
+ if (store) {
+ vals = ldap_get_values_len(sh->ldap, sm->msg, str);
+ if (!vals) {
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ if (lerrno != LDAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_get_values_len() failed: %d(%s)\n",
+ lerrno, sss_ldap_err2string(lerrno));
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Attribute [%s] has no values, skipping.\n", str);
+
+ } else {
+ if (!vals[0]) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing value after ldap_get_values() ??\n");
+ ldap_value_free_len(vals);
+ ret = EINVAL;
+ goto done;
+ }
+ for (i = 0; vals[i]; i++) {
+ if (vals[i]->bv_len == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Value of attribute [%s] is empty. "
+ "Skipping this value.\n", str);
+ continue;
+ }
+ if (base64) {
+ v.data = (uint8_t *) sss_base64_encode(attrs,
+ (uint8_t *) vals[i]->bv_val, vals[i]->bv_len);
+ if (!v.data) {
+ ldap_value_free_len(vals);
+ ret = ENOMEM;
+ goto done;
+ }
+ v.length = strlen((const char *)v.data);
+ } else {
+ v.data = (uint8_t *)vals[i]->bv_val;
+ v.length = vals[i]->bv_len;
+ }
+ PROBE(SDAP_PARSE_ENTRY, str, v.data, v.length);
+
+ if (map) {
+ /* The same LDAP attr might be used for more sysdb
+ * attrs in case there is a map. Find all that match
+ * and copy the value
+ */
+ for (ai = base_attr_idx; ai < attrs_num; ai++) {
+ /* check if this attr is valid with the chosen
+ * schema */
+ if (!map[ai].name) continue;
+
+ /* check if it is an attr we are interested in */
+ if (strcasecmp(base_attr, map[ai].name) == 0) {
+ ret = sysdb_attrs_add_val(attrs,
+ map[ai].sys_name,
+ &v);
+ if (ret) {
+ ldap_value_free_len(vals);
+ goto done;
+ }
+ }
+ }
+ } else {
+ /* No map, just store the attribute */
+ ret = sysdb_attrs_add_val(attrs, name, &v);
+ if (ret) {
+ ldap_value_free_len(vals);
+ goto done;
+ }
+ }
+ }
+ ldap_value_free_len(vals);
+ }
+ }
+
+ ldap_memfree(str);
+ str = ldap_next_attribute(sh->ldap, sm->msg, ber);
+ }
+ ber_free(ber, 0);
+ ber = NULL;
+
+ ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno);
+ if (lerrno) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "ldap_get_option() failed: %d(%s)\n",
+ lerrno, sss_ldap_err2string(lerrno));
+ ret = EIO;
+ goto done;
+ }
+
+ PROBE(SDAP_PARSE_ENTRY_DONE);
+ *_attrs = talloc_steal(memctx, attrs);
+ ret = EOK;
+
+done:
+ if (ber) ber_free(ber, 0);
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static bool objectclass_matched(struct sdap_attr_map *map,
+ const char *objcl, int len)
+{
+ if (len == 0) {
+ len = strlen(objcl) + 1;
+ }
+
+ if (strncasecmp(map[SDAP_OC_GROUP].name, objcl, len) == 0) {
+ return true;
+ }
+
+ if (map[SDAP_OC_GROUP_ALT].name != NULL
+ && strncasecmp(map[SDAP_OC_GROUP_ALT].name, objcl, len) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Parses an LDAPDerefRes into sdap_deref_attrs structure */
+errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx,
+ struct sdap_attr_map_info *minfo,
+ size_t num_maps,
+ LDAPDerefRes *dref,
+ struct sdap_deref_attrs ***_deref_res)
+{
+ TALLOC_CTX *tmp_ctx;
+ LDAPDerefVal *dval;
+ const char *orig_dn;
+ const char **ocs;
+ struct sdap_attr_map *map;
+ int num_attrs = 0;
+ int ret, i, a, mi;
+ const char *name;
+ size_t len;
+ struct sdap_deref_attrs **res;
+
+ if (!dref || !minfo) return EINVAL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ res = talloc_array(tmp_ctx, struct sdap_deref_attrs *, num_maps);
+ if (!res) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i=0; i < num_maps; i++) {
+ res[i] = talloc_zero(res, struct sdap_deref_attrs);
+ if (!res[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ res[i]->map = minfo[i].map;
+ }
+
+ if (!dref->derefVal.bv_val) {
+ DEBUG(SSSDBG_OP_FAILURE, "Entry has no DN?\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ orig_dn = dref->derefVal.bv_val;
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Dereferenced DN: %s\n", orig_dn);
+
+ if (!dref->attrVals) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Dereferenced entry [%s] has no attributes, skipping\n",
+ orig_dn);
+ *_deref_res = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ ocs = NULL;
+ for (dval = dref->attrVals; dval != NULL; dval = dval->next) {
+ if (strcasecmp("objectClass", dval->type) == 0) {
+ if (dval->vals == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "No value for objectClass, skipping\n");
+ continue;
+ }
+
+ for(len=0; dval->vals[len].bv_val; len++);
+
+ ocs = talloc_array(tmp_ctx, const char *, len+1);
+ if (!ocs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i=0; i<len; i++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Dereferenced objectClass value: %s\n",
+ dval->vals[i].bv_val);
+ ocs[i] = talloc_strdup(ocs, dval->vals[i].bv_val);
+ if (!ocs[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ ocs[i] = NULL;
+ break;
+ }
+ }
+ if (!ocs) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unknown entry type, no objectClasses found!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (mi = 0; mi < num_maps; mi++) {
+ map = NULL;
+
+ for (i=0; ocs[i]; i++) {
+ /* the objectclass is always the first name in the map */
+ if (objectclass_matched(minfo[mi].map, ocs[i], 0)) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Found map for objectclass '%s'\n", ocs[i]);
+ map = minfo[mi].map;
+ num_attrs = minfo[mi].num_attrs;
+ break;
+ }
+ }
+ if (!map) continue;
+
+ res[mi]->attrs = sysdb_new_attrs(res[mi]);
+ if (!res[mi]->attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(res[mi]->attrs, SYSDB_ORIG_DN,
+ orig_dn);
+ if (ret) {
+ goto done;
+ }
+
+ /* The dereference control seems to return the DN from the dereference
+ * attribute (e.g. member) so we can use it as key for the hash table
+ * later. */
+ ret = sysdb_attrs_add_string(res[mi]->attrs,
+ SYSDB_DN_FOR_MEMBER_HASH_TABLE, orig_dn);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+ goto done;
+ }
+
+ for (dval = dref->attrVals; dval != NULL; dval = dval->next) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Dereferenced attribute: %s\n", dval->type);
+
+ for (a = 1; a < num_attrs; a++) {
+ /* check if this attr is valid with the chosen schema */
+ if (!map[a].name) continue;
+ /* check if it is an attr we are interested in */
+ if (strcasecmp(dval->type, map[a].name) == 0) break;
+ }
+
+ /* interesting attr */
+ if (a < num_attrs) {
+ name = map[a].sys_name;
+ } else {
+ continue;
+ }
+
+ if (dval->vals == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "No value for attribute %s, skipping\n", name);
+ continue;
+ }
+
+ for (i=0; dval->vals[i].bv_val; i++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Dereferenced attribute value: %s\n",
+ dval->vals[i].bv_val);
+ ret = sysdb_attrs_add_mem(res[mi]->attrs, name,
+ dval->vals[i].bv_val,
+ dval->vals[i].bv_len);
+ if (ret) goto done;
+ }
+ }
+ }
+
+
+ *_deref_res = talloc_steal(mem_ctx, res);
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static void sss_ldap_debug(const char *buf)
+{
+ sss_debug_fn(__FILE__, __LINE__, __FUNCTION__, SSSDBG_TRACE_ALL,
+ "libldap: %s", buf);
+}
+
+void setup_ldap_debug(struct dp_option *basic_opts)
+{
+ int ret;
+ int ldap_debug_level;
+
+ ldap_debug_level = dp_opt_get_int(basic_opts, SDAP_LIBRARY_DEBUG_LEVEL);
+ if (ldap_debug_level == 0) {
+ return;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Setting LDAP library debug level [%d].\n",
+ ldap_debug_level);
+
+ ret = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug_level);
+ if (ret != LBER_OPT_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to set LBER_OPT_DEBUG_LEVEL, ignored .\n");
+ }
+
+ ret = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, sss_ldap_debug);
+ if (ret != LBER_OPT_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to set LBER_OPT_LOG_PRINT_FN, ignored .\n");
+ }
+
+ ret = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_debug_level);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to set LDAP_OPT_DEBUG_LEVEL, ignored .\n");
+ }
+}
+
+errno_t setup_tls_config(struct dp_option *basic_opts)
+{
+ int ret;
+ int ldap_opt_x_tls_require_cert;
+ const char *tls_opt;
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_REQCERT);
+ if (tls_opt) {
+ if (strcasecmp(tls_opt, "never") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_NEVER;
+ }
+ else if (strcasecmp(tls_opt, "allow") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_ALLOW;
+ }
+ else if (strcasecmp(tls_opt, "try") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_TRY;
+ }
+ else if (strcasecmp(tls_opt, "demand") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
+ }
+ else if (strcasecmp(tls_opt, "hard") == 0) {
+ ldap_opt_x_tls_require_cert = LDAP_OPT_X_TLS_HARD;
+ }
+ else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unknown value for tls_reqcert '%s'.\n", tls_opt);
+ return EINVAL;
+ }
+ /* LDAP_OPT_X_TLS_REQUIRE_CERT has to be set as a global option,
+ * because the SSL/TLS context is initialized from this value. */
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &ldap_opt_x_tls_require_cert);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(req_cert) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CACERT);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(cacertfile) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CACERTDIR);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(cacertdir) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CERT);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(certfile) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_KEY);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(keyfile) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ tls_opt = dp_opt_get_string(basic_opts, SDAP_TLS_CIPHER_SUITE);
+ if (tls_opt) {
+ ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, tls_opt);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "ldap_set_option(cipher) failed: %s\n",
+ sss_ldap_err2string(ret));
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+bool sdap_sasl_mech_needs_kinit(const char *sasl_mech)
+{
+ if (strcasecmp(sasl_mech, "GSSAPI") == 0
+ || strcasecmp(sasl_mech, "GSS-SPNEGO") == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+bool sdap_check_sup_list(struct sup_list *l, const char *val)
+{
+ int i;
+
+ if (!val) {
+ return false;
+ }
+
+ for (i = 0; i < l->num_vals; i++) {
+ if (strcasecmp(val, (char *)l->vals[i])) {
+ continue;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static int sdap_init_sup_list(TALLOC_CTX *memctx,
+ struct sup_list *list,
+ int num, struct ldb_val *vals)
+{
+ int i;
+
+ list->vals = talloc_array(memctx, char *, num);
+ if (!list->vals) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ list->vals[i] = talloc_strndup(list->vals,
+ (char *)vals[i].data, vals[i].length);
+ if (!list->vals[i]) {
+ return ENOMEM;
+ }
+ }
+
+ list->num_vals = num;
+
+ return EOK;
+}
+
+int sdap_set_rootdse_supported_lists(struct sysdb_attrs *rootdse,
+ struct sdap_handle *sh)
+{
+ struct ldb_message_element *el = NULL;
+ int ret;
+ int i;
+
+ for (i = 0; i < rootdse->num; i++) {
+ el = &rootdse->a[i];
+ if (strcasecmp(el->name, "supportedControl") == 0) {
+
+ ret = sdap_init_sup_list(sh, &sh->supported_controls,
+ el->num_values, el->values);
+ if (ret) {
+ return ret;
+ }
+ } else if (strcasecmp(el->name, "supportedExtension") == 0) {
+
+ ret = sdap_init_sup_list(sh, &sh->supported_extensions,
+ el->num_values, el->values);
+ if (ret) {
+ return ret;
+ }
+ } else if (strcasecmp(el->name, "supportedSASLMechanisms") == 0) {
+
+ ret = sdap_init_sup_list(sh, &sh->supported_saslmechs,
+ el->num_values, el->values);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+
+ return EOK;
+
+}
+
+static char *get_single_value_as_string(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el)
+{
+ char *str = NULL;
+
+ if (el->num_values == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Missing value.\n");
+ } else if (el->num_values == 1) {
+ str = talloc_strndup(mem_ctx, (char *) el->values[0].data,
+ el->values[0].length);
+ if (str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "More than one value found.\n");
+ }
+
+ return str;
+}
+
+static char *get_naming_context(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *rootdse)
+{
+ struct ldb_message_element *nc = NULL;
+ struct ldb_message_element *dnc = NULL;
+ int i;
+ char *naming_context = NULL;
+
+ for (i = 0; i < rootdse->num; i++) {
+ if (strcasecmp(rootdse->a[i].name,
+ SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS) == 0) {
+ nc = &rootdse->a[i];
+ } else if (strcasecmp(rootdse->a[i].name,
+ SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT) == 0) {
+ dnc = &rootdse->a[i];
+ }
+ }
+
+ if (dnc == NULL && nc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "No attributes [%s] or [%s] found in rootDSE.\n",
+ SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS,
+ SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT);
+ } else {
+ if (dnc != NULL) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Using value from [%s] as naming context.\n",
+ SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT);
+ naming_context = get_single_value_as_string(mem_ctx, dnc);
+ }
+
+ if (naming_context == NULL && nc != NULL) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Using value from [%s] as naming context.\n",
+ SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS);
+ naming_context = get_single_value_as_string(mem_ctx, nc);
+ }
+ }
+
+ /* Some directory servers such as Novell eDirectory will return
+ * a zero-length namingContexts value in some situations. In this
+ * case, we should return it as NULL so things fail gracefully.
+ */
+ if (naming_context && naming_context[0] == '\0') {
+ talloc_zfree(naming_context);
+ }
+
+ return naming_context;
+}
+
+errno_t
+sdap_create_search_base(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *unparsed_base,
+ int scope,
+ const char *filter,
+ struct sdap_search_base **_base)
+{
+ struct sdap_search_base *base;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ struct ldb_dn *ldn;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ base = talloc_zero(tmp_ctx, struct sdap_search_base);
+ if (base == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ base->basedn = talloc_strdup(base, unparsed_base);
+ if (base->basedn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Validate the basedn */
+ ldn = ldb_dn_new(base, ldb, unparsed_base);
+ if (!ldn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!ldb_dn_validate(ldn)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid base DN [%s]\n", unparsed_base);
+ ret = EINVAL;
+ goto done;
+ }
+
+ base->ldb = ldb;
+ base->ldb_basedn = ldn;
+ base->scope = scope;
+ base->filter = filter;
+
+ *_base = talloc_steal(mem_ctx, base);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sdap_set_search_base(struct sdap_options *opts,
+ struct sdap_domain *sdom,
+ enum sdap_basic_opt class,
+ char *naming_context)
+{
+ errno_t ret;
+ struct sdap_search_base ***bases;
+
+ switch(class) {
+ case SDAP_SEARCH_BASE:
+ bases = &sdom->search_bases;
+ break;
+ case SDAP_USER_SEARCH_BASE:
+ bases = &sdom->user_search_bases;
+ break;
+ case SDAP_GROUP_SEARCH_BASE:
+ bases = &sdom->group_search_bases;
+ break;
+ case SDAP_NETGROUP_SEARCH_BASE:
+ bases = &sdom->netgroup_search_bases;
+ break;
+ case SDAP_HOST_SEARCH_BASE:
+ bases = &sdom->host_search_bases;
+ break;
+ case SDAP_SUDO_SEARCH_BASE:
+ bases = &sdom->sudo_search_bases;
+ break;
+ case SDAP_SERVICE_SEARCH_BASE:
+ bases = &sdom->service_search_bases;
+ break;
+ case SDAP_AUTOFS_SEARCH_BASE:
+ bases = &sdom->autofs_search_bases;
+ break;
+ case SDAP_IPHOST_SEARCH_BASE:
+ bases = &sdom->iphost_search_bases;
+ break;
+ case SDAP_IPNETWORK_SEARCH_BASE:
+ bases = &sdom->ipnetwork_search_bases;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Setting option [%s] to [%s].\n",
+ opts->basic[class].opt_name, naming_context);
+
+ ret = dp_opt_set_string(opts->basic, class, naming_context);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
+ goto done;
+ }
+
+ ret = sdap_parse_search_base(opts, sysdb_ctx_get_ldb(sdom->dom->sysdb),
+ opts->basic, class, bases);
+ if (ret != EOK) goto done;
+
+ ret = EOK;
+done:
+ return ret;
+}
+
+errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
+ struct sdap_options *opts,
+ struct sdap_domain *sdom)
+{
+ int ret;
+ char *naming_context = NULL;
+
+ if (!sdom->search_bases
+ || !sdom->user_search_bases
+ || !sdom->group_search_bases
+ || !sdom->netgroup_search_bases
+ || !sdom->host_search_bases
+ || !sdom->sudo_search_bases
+ || !sdom->iphost_search_bases
+ || !sdom->ipnetwork_search_bases
+ || !sdom->autofs_search_bases) {
+ naming_context = get_naming_context(opts->basic, rootdse);
+ if (naming_context == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "get_naming_context failed.\n");
+
+ /* This has to be non-fatal, since some servers offer
+ * multiple namingContexts entries. We will just
+ * add NULL checks for the search bases in the lookups.
+ */
+ ret = EOK;
+ goto done;
+ }
+ }
+
+ /* Default */
+ if (!sdom->search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Users */
+ if (!sdom->user_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_USER_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Groups */
+ if (!sdom->group_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_GROUP_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Netgroups */
+ if (!sdom->netgroup_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_NETGROUP_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Hosts */
+ if (!sdom->host_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_HOST_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Sudo */
+ if (!sdom->sudo_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_SUDO_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* Services */
+ if (!sdom->service_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_SERVICE_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* autofs */
+ if (!sdom->autofs_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_AUTOFS_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* IP host */
+ if (!sdom->iphost_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_IPHOST_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ /* IP network */
+ if (!sdom->ipnetwork_search_bases) {
+ ret = sdap_set_search_base(opts, sdom,
+ SDAP_IPNETWORK_SEARCH_BASE,
+ naming_context);
+ if (ret != EOK) goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(naming_context);
+ return ret;
+}
+
+int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
+ const char *server,
+ struct sysdb_attrs *rootdse,
+ struct sdap_options *opts,
+ struct sdap_server_opts **srv_opts)
+{
+ struct sdap_server_opts *so;
+ struct {
+ const char *last_name;
+ const char *entry_name;
+ } usn_attrs[] = { { SDAP_IPA_LAST_USN, SDAP_IPA_USN },
+ { SDAP_AD_LAST_USN, SDAP_AD_USN },
+ { NULL, NULL } };
+ const char *last_usn_name;
+ const char *last_usn_value;
+ const char *entry_usn_name;
+ const char *schema_nc = NULL;
+ char *endptr = NULL;
+ int ret;
+ int i;
+ uint32_t dc_level;
+
+ so = talloc_zero(memctx, struct sdap_server_opts);
+ if (!so) {
+ return ENOMEM;
+ }
+ so->server_id = talloc_strdup(so, server);
+ if (!so->server_id) {
+ talloc_zfree(so);
+ return ENOMEM;
+ }
+
+ last_usn_name = opts->gen_map[SDAP_AT_LAST_USN].name;
+ entry_usn_name = opts->gen_map[SDAP_AT_ENTRY_USN].name;
+ if (rootdse) {
+ if (last_usn_name) {
+ ret = sysdb_attrs_get_string(rootdse,
+ last_usn_name, &last_usn_value);
+ if (ret != EOK) {
+ switch (ret) {
+ case ENOENT:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "%s configured but not found in rootdse!\n",
+ opts->gen_map[SDAP_AT_LAST_USN].opt_name);
+ break;
+ case ERANGE:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Multiple values of %s found in rootdse!\n",
+ opts->gen_map[SDAP_AT_LAST_USN].opt_name);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unknown error (%d) checking rootdse!\n", ret);
+ }
+ } else {
+ if (!entry_usn_name) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "%s found in rootdse but %s is not set!\n",
+ last_usn_name,
+ opts->gen_map[SDAP_AT_ENTRY_USN].opt_name);
+ } else {
+ so->supports_usn = true;
+ errno = 0;
+ so->last_usn = strtoul(last_usn_value, &endptr, 10);
+ if (errno || !endptr || *endptr || (endptr == last_usn_value)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "USN is not valid (value: %s)\n", last_usn_value);
+ so->last_usn = 0;
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "USN value: %s (int: %lu)\n", last_usn_value, so->last_usn);
+ }
+ }
+ }
+ } else {
+ /* no usn option configure, let's try to autodetect. */
+ for (i = 0; usn_attrs[i].last_name; i++) {
+ ret = sysdb_attrs_get_string(rootdse,
+ usn_attrs[i].last_name,
+ &last_usn_value);
+ if (ret == EOK) {
+ /* Fixate discovered configuration */
+ opts->gen_map[SDAP_AT_LAST_USN].name =
+ talloc_strdup(opts->gen_map, usn_attrs[i].last_name);
+ opts->gen_map[SDAP_AT_ENTRY_USN].name =
+ talloc_strdup(opts->gen_map, usn_attrs[i].entry_name);
+ so->supports_usn = true;
+ errno = 0;
+ so->last_usn = strtoul(last_usn_value, &endptr, 10);
+ if (errno || !endptr || *endptr || (endptr == last_usn_value)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "USN is not valid (value: %s)\n", last_usn_value);
+ so->last_usn = 0;
+ } else {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "USN value: %s (int: %lu)\n", last_usn_value, so->last_usn);
+ }
+ last_usn_name = usn_attrs[i].last_name;
+ break;
+ }
+ }
+ }
+
+ /* Detect Active Directory version if available */
+ ret = sysdb_attrs_get_uint32_t(rootdse,
+ SDAP_ROOTDSE_ATTR_AD_VERSION,
+ &dc_level);
+ if (ret == EOK) {
+ /* Validate that the DC level matches an expected value */
+ switch(dc_level) {
+ case DS_BEHAVIOR_WIN2000:
+ case DS_BEHAVIOR_WIN2003:
+ case DS_BEHAVIOR_WIN2008:
+ case DS_BEHAVIOR_WIN2008R2:
+ case DS_BEHAVIOR_WIN2012:
+ case DS_BEHAVIOR_WIN2012R2:
+ case DS_BEHAVIOR_WIN2016:
+ opts->dc_functional_level = dc_level;
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Setting AD compatibility level to [%d]\n",
+ opts->dc_functional_level);
+ break;
+ default:
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Received invalid value [%d] for AD compatibility level. "
+ "Using the lowest-common compatibility level\n",
+ dc_level);
+ opts->dc_functional_level = DS_BEHAVIOR_WIN2003;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Error detecting Active Directory compatibility level "
+ "(%s). Continuing without AD performance enhancements\n",
+ strerror(ret));
+ }
+
+ ret = sysdb_attrs_get_string(rootdse,
+ SDAP_ROOTDSE_ATTR_AD_SCHEMA_NC,
+ &schema_nc);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Will look for schema at [%s]\n", schema_nc);
+ opts->schema_basedn = talloc_strdup(opts, schema_nc);
+ }
+ }
+
+ if (!last_usn_name) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "No known USN scheme is supported by this server!\n");
+ if (!entry_usn_name) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Will use modification timestamp as usn!\n");
+ opts->gen_map[SDAP_AT_ENTRY_USN].name =
+ talloc_strdup(opts->gen_map, "modifyTimestamp");
+ }
+ }
+
+ if (!opts->user_map[SDAP_AT_USER_USN].name) {
+ opts->user_map[SDAP_AT_USER_USN].name =
+ talloc_strdup(opts->user_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+ if (!opts->group_map[SDAP_AT_GROUP_USN].name) {
+ opts->group_map[SDAP_AT_GROUP_USN].name =
+ talloc_strdup(opts->group_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+ if (!opts->service_map[SDAP_AT_SERVICE_USN].name) {
+ opts->service_map[SDAP_AT_SERVICE_USN].name =
+ talloc_strdup(opts->service_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+ if (opts->sudorule_map &&
+ !opts->sudorule_map[SDAP_AT_SUDO_USN].name) {
+ opts->sudorule_map[SDAP_AT_SUDO_USN].name =
+ talloc_strdup(opts->sudorule_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+ if (opts->iphost_map &&
+ !opts->iphost_map[SDAP_AT_IPHOST_USN].name) {
+ opts->iphost_map[SDAP_AT_IPHOST_USN].name =
+ talloc_strdup(opts->iphost_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+ if (opts->ipnetwork_map &&
+ !opts->ipnetwork_map[SDAP_AT_IPNETWORK_USN].name) {
+ opts->ipnetwork_map[SDAP_AT_IPNETWORK_USN].name =
+ talloc_strdup(opts->ipnetwork_map,
+ opts->gen_map[SDAP_AT_ENTRY_USN].name);
+ }
+
+ *srv_opts = so;
+ return EOK;
+}
+
+void sdap_steal_server_opts(struct sdap_id_ctx *id_ctx,
+ struct sdap_server_opts **srv_opts)
+{
+ if (!id_ctx || !srv_opts || !*srv_opts) {
+ return;
+ }
+
+ if (!id_ctx->srv_opts) {
+ id_ctx->srv_opts = talloc_move(id_ctx, srv_opts);
+ return;
+ }
+
+ /* discard if same as previous so we do not reset max usn values
+ * unnecessarily, only update last_usn. */
+ if (strcmp(id_ctx->srv_opts->server_id, (*srv_opts)->server_id) == 0) {
+ id_ctx->srv_opts->last_usn = (*srv_opts)->last_usn;
+ talloc_zfree(*srv_opts);
+ return;
+ }
+
+ talloc_zfree(id_ctx->srv_opts);
+ id_ctx->srv_opts = talloc_move(id_ctx, srv_opts);
+}
+
+static bool attr_is_filtered(const char *attr, const char **filter)
+{
+ int i;
+
+ if (filter) {
+ i = 0;
+ while (filter[i]) {
+ if (filter[i] == attr ||
+ strcasecmp(filter[i], attr) == 0) {
+ return true;
+ }
+ i++;
+ }
+ }
+
+ return false;
+}
+
+int build_attrs_from_map(TALLOC_CTX *memctx,
+ struct sdap_attr_map *map,
+ size_t size,
+ const char **filter,
+ const char ***_attrs,
+ size_t *attr_count)
+{
+ errno_t ret;
+ const char **attrs;
+ int i, j;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ /* Assume that all entries in the map have values */
+ attrs = talloc_zero_array(tmp_ctx, const char *, size + 1);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* first attribute is "objectclass" not the specific one */
+ attrs[0] = talloc_strdup(memctx, "objectClass");
+ if (!attrs[0]) return ENOMEM;
+
+ /* add the others */
+ for (i = j = 1; i < size; i++) {
+ if (map[i].name && !attr_is_filtered(map[i].name, filter)) {
+ attrs[j] = map[i].name;
+ j++;
+ }
+ }
+ attrs[j] = NULL;
+
+ /* Trim down the used memory if some attributes were NULL */
+ attrs = talloc_realloc(tmp_ctx, attrs, const char *, j + 1);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_attrs = talloc_steal(memctx, attrs);
+ if (attr_count) *attr_count = j;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int sdap_control_create(struct sdap_handle *sh, const char *oid, int iscritical,
+ struct berval *value, int dupval, LDAPControl **ctrlp)
+{
+ int ret;
+
+ if (sdap_is_control_supported(sh, oid)) {
+ ret = sss_ldap_control_create(oid, iscritical, value, dupval, ctrlp);
+ if (ret != LDAP_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_ldap_control_create failed [%d][%s].\n",
+ ret, sss_ldap_err2string(ret));
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Server does not support the requested control [%s].\n", oid);
+ ret = LDAP_NOT_SUPPORTED;
+ }
+
+ return ret;
+}
+
+int sdap_replace_id(struct sysdb_attrs *entry, const char *attr, id_t val)
+{
+ char *str;
+ errno_t ret;
+ struct ldb_message_element *el;
+
+ ret = sysdb_attrs_get_el_ext(entry, attr, false, &el);
+ if (ret == ENOENT) {
+ return sysdb_attrs_add_uint32(entry, attr, val);
+ } else if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get attribute [%s]\n", attr);
+ return ret;
+ }
+
+ if (el->num_values != 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Expected 1 value for %s, got %d\n", attr, el->num_values);
+ return EINVAL;
+ }
+
+ str = talloc_asprintf(entry, "%llu", (unsigned long long) val);
+ if (!str) {
+ return ENOMEM;
+ }
+
+ el->values[0].data = (uint8_t *) str;
+ el->values[0].length = strlen(str);
+
+ return EOK;
+}
+
+static errno_t sdap_get_rdn_multi(TALLOC_CTX *mem_ctx, const char *dn,
+ const char *name, char **_val)
+{
+ int ret;
+ size_t c;
+ LDAPDN ldapdn = NULL;
+
+ ret = ldap_str2dn(dn, &ldapdn, LDAP_DN_FORMAT_LDAPV3);
+ if (ret != LDAP_SUCCESS || ldapdn == NULL || ldapdn[0] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to parse DN [%s].\n", dn);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = ENOENT;
+ for (c = 0; ldapdn[0][c] != NULL; c++) {
+ if (strncasecmp(name, ldapdn[0][c]->la_attr.bv_val,
+ ldapdn[0][c]->la_attr.bv_len) == 0) {
+ *_val = talloc_strndup(mem_ctx, ldapdn[0][c]->la_value.bv_val,
+ ldapdn[0][c]->la_value.bv_len);
+ if (*_val == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy AVA value.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = EOK;
+ break;
+ }
+ }
+
+done:
+ ldap_dnfree(ldapdn);
+
+ return ret;
+}
+
+errno_t sdap_get_primary_name(const char *attr_name,
+ struct sysdb_attrs *attrs,
+ const char **_primary_name)
+{
+ errno_t ret;
+ const char *orig_name = NULL;
+ char *rdn_val = NULL;
+ struct ldb_message_element *sysdb_name_el;
+ struct ldb_message_element *orig_dn_el;
+ size_t i;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_el(attrs,
+ SYSDB_NAME,
+ &sysdb_name_el);
+ if (ret != EOK || sysdb_name_el->num_values == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (sysdb_name_el->num_values == 1) {
+ /* Entry contains only one name. Just return that */
+ orig_name = (const char *)sysdb_name_el->values[0].data;
+ ret = EOK;
+ goto done;
+ }
+
+ /* Multiple values for name. Check whether one matches the RDN */
+
+ ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &orig_dn_el);
+ if (ret) {
+ goto done;
+ }
+ if (orig_dn_el->num_values == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Original DN is not available.\n");
+ ret = EINVAL;
+ goto done;
+ } else if (orig_dn_el->num_values == 1) {
+ ret = sdap_get_rdn_multi(tmp_ctx,
+ (const char *) orig_dn_el->values[0].data,
+ attr_name, &rdn_val);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The entry has multiple names and the RDN attribute does "
+ "not match. Will use the first value [%s] as fallback.\n",
+ (const char *)sysdb_name_el->values[0].data);
+ orig_name = (const char *)sysdb_name_el->values[0].data;
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not get rdn from [%s]\n",
+ (const char *) orig_dn_el->values[0].data);
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Should not have more than one origDN\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ for (i = 0; i < sysdb_name_el->num_values; i++) {
+ if (strcasecmp(rdn_val,
+ (const char *)sysdb_name_el->values[i].data) == 0) {
+ /* This name matches the RDN. Use it */
+ break;
+ }
+ }
+ if (i < sysdb_name_el->num_values) {
+ /* Match was found */
+ orig_name = (const char *)sysdb_name_el->values[i].data;
+ } else {
+ /* If we can't match the name to the RDN, we just have to
+ * throw up our hands. There's no deterministic way to
+ * decide which name is correct.
+ */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Can't match the name to the RDN\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not determine primary name: [%d][%s]\n",
+ ret, strerror(ret));
+ }
+ talloc_free(tmp_ctx);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Processing object %s\n", orig_name);
+
+ *_primary_name = orig_name;
+
+ return ret;
+}
+
+static errno_t
+sdap_get_primary_fqdn(TALLOC_CTX *mem_ctx,
+ struct sdap_idmap_ctx *idmap_ctx,
+ const char *attr_name,
+ const char *sid_attr_name,
+ struct sysdb_attrs *attrs,
+ struct sss_domain_info *dom,
+ const char **_primary_fqdn)
+{
+ errno_t ret;
+ const char *shortname = NULL;
+ const char *primary_fqdn = NULL;
+ TALLOC_CTX *tmp_ctx;
+ char *sid_str = NULL;
+ struct sss_domain_info *subdomain = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sdap_get_primary_name(attr_name, attrs, &shortname);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* In AD scenarion, the object can be from subdomain - identify it by SID */
+ if (sid_attr_name != NULL) {
+ ret = sdap_attrs_get_sid_str(tmp_ctx,
+ idmap_ctx,
+ attrs,
+ sid_attr_name,
+ &sid_str);
+
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Group has objectSID [%s]\n", sid_str);
+ subdomain = find_domain_by_sid(dom, sid_str);
+ talloc_free(sid_str);
+ if (subdomain != NULL) {
+ dom = subdomain;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Group has name [%s]\n", dom->name);
+ }
+
+ primary_fqdn = sss_create_internal_fqname(tmp_ctx, shortname, dom->name);
+ if (primary_fqdn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ *_primary_fqdn = talloc_steal(mem_ctx, primary_fqdn);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sdap_get_user_primary_name(TALLOC_CTX *memctx,
+ struct sdap_options *opts,
+ struct sysdb_attrs *attrs,
+ struct sss_domain_info *dom,
+ const char **_user_name)
+{
+ return sdap_get_primary_fqdn(memctx,
+ opts->idmap_ctx,
+ opts->user_map[SDAP_AT_USER_NAME].name,
+ opts->group_map[SDAP_AT_USER_OBJECTSID].name,
+ attrs, dom, _user_name);
+}
+
+errno_t sdap_get_group_primary_name(TALLOC_CTX *memctx,
+ struct sdap_options *opts,
+ struct sysdb_attrs *attrs,
+ struct sss_domain_info *dom,
+ const char **_group_name)
+{
+ return sdap_get_primary_fqdn(memctx,
+ opts->idmap_ctx,
+ opts->group_map[SDAP_AT_GROUP_NAME].name,
+ opts->group_map[SDAP_AT_GROUP_OBJECTSID].name,
+ attrs, dom, _group_name);
+}
+
+errno_t sdap_get_netgroup_primary_name(struct sdap_options *opts,
+ struct sysdb_attrs *attrs,
+ const char **_netgroup_name)
+{
+ return sdap_get_primary_name(opts->netgroup_map[SDAP_AT_NETGROUP_NAME].name,
+ attrs, _netgroup_name);
+}
+
+static errno_t
+_sdap_get_primary_name_list(struct sss_domain_info *domain,
+ TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attr_list,
+ size_t attr_count,
+ const char *ldap_attr,
+ bool qualify_names,
+ const char *sid_attr,
+ struct sdap_idmap_ctx *idmap_ctx,
+ char ***name_list)
+{
+ errno_t ret;
+ size_t i, j;
+ char **list;
+ const char *name;
+
+ /* Assume that every entry has a primary name */
+ list = talloc_array(mem_ctx, char *, attr_count+1);
+ if (!list) {
+ return ENOMEM;
+ }
+
+ j = 0;
+ for (i = 0; i < attr_count; i++) {
+ if (qualify_names == false) {
+ ret = sdap_get_primary_name(ldap_attr, attr_list[i], &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine primary name\n");
+ /* Skip and continue. Don't advance 'j' */
+ continue;
+ }
+ list[j] = talloc_strdup(list, name);
+ } else {
+ ret = sdap_get_primary_fqdn(mem_ctx,
+ idmap_ctx,
+ ldap_attr,
+ sid_attr,
+ attr_list[i],
+ domain,
+ &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine primary fqdn name\n");
+ /* Skip and continue. Don't advance 'j' */
+ continue;
+ }
+ list[j] = talloc_strdup(list, name);
+ }
+ if (!list[j]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ j++;
+ }
+
+ /* NULL-terminate the list */
+ list[j] = NULL;
+
+ *name_list = list;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(list);
+ }
+ return ret;
+}
+
+errno_t sdap_get_primary_name_list(struct sss_domain_info *domain,
+ TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attr_list,
+ size_t attr_count,
+ const char *ldap_attr,
+ char ***name_list)
+{
+ return _sdap_get_primary_name_list(domain, mem_ctx, attr_list, attr_count,
+ ldap_attr, false, NULL, NULL, name_list);
+}
+
+errno_t sdap_get_primary_fqdn_list(struct sss_domain_info *domain,
+ TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs **attr_list,
+ size_t attr_count,
+ const char *ldap_attr,
+ const char *sid_attr,
+ struct sdap_idmap_ctx *idmap_ctx,
+ char ***name_list)
+{
+ return _sdap_get_primary_name_list(domain, mem_ctx, attr_list, attr_count,
+ ldap_attr, true, sid_attr, idmap_ctx, name_list);
+}
+
+
+char *sdap_make_oc_list(TALLOC_CTX *mem_ctx, struct sdap_attr_map *map)
+{
+ if (map[SDAP_OC_GROUP_ALT].name == NULL) {
+ return talloc_asprintf(mem_ctx, "objectClass=%s",
+ map[SDAP_OC_GROUP].name);
+ } else {
+ return talloc_asprintf(mem_ctx,
+ "|(objectClass=%s)(objectClass=%s)",
+ map[SDAP_OC_GROUP].name,
+ map[SDAP_OC_GROUP_ALT].name);
+ }
+}
+
+struct sss_domain_info *sdap_get_object_domain(struct sdap_options *opts,
+ struct sysdb_attrs *obj,
+ struct sss_domain_info *dom)
+{
+ errno_t ret;
+ const char *original_dn = NULL;
+ struct sdap_domain *sdmatch = NULL;
+
+ ret = sysdb_attrs_get_string(obj, SYSDB_ORIG_DN, &original_dn);
+ if (ret) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The group has no original DN, assuming our domain\n");
+ return dom;
+ }
+
+ sdmatch = sdap_domain_get_by_dn(opts, original_dn);
+ if (sdmatch == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The original DN of the group cannot "
+ "be related to any search base\n");
+ return dom;
+ }
+
+ return sdmatch->dom;
+}
+
+bool sdap_object_in_domain(struct sdap_options *opts,
+ struct sysdb_attrs *obj,
+ struct sss_domain_info *dom)
+{
+ struct sss_domain_info *obj_dom;
+
+ obj_dom = sdap_get_object_domain(opts, obj, dom);
+ if (obj_dom == NULL) {
+ return false;
+ }
+
+ return (obj_dom == dom);
+}
+
+size_t sdap_steal_objects_in_dom(struct sdap_options *opts,
+ struct sysdb_attrs **dom_objects,
+ size_t offset,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs **all_objects,
+ size_t count,
+ bool filter)
+{
+ size_t copied = 0;
+
+ /* Own objects from all_objects by dom_objects in case they belong
+ * to domain dom.
+ *
+ * Don't copy objects from other domains in case
+ * the search was for parent domain but a child domain would match,
+ * too, such as:
+ * dc=example,dc=com
+ * dc=child,dc=example,dc=com
+ * while searching for an object from dc=example.
+ */
+ for (size_t i = 0; i < count; i++) {
+ if (filter &&
+ sdap_object_in_domain(opts, all_objects[i], dom) == false) {
+ continue;
+ }
+
+ dom_objects[offset + copied] =
+ talloc_steal(dom_objects, all_objects[i]);
+ copied++;
+ }
+
+ return copied;
+}
+
+void sdap_domain_copy_search_bases(struct sdap_domain *to,
+ struct sdap_domain *from)
+{
+ to->search_bases = from->search_bases;
+ to->user_search_bases = from->user_search_bases;
+ to->group_search_bases = from->group_search_bases;
+ to->netgroup_search_bases = from->netgroup_search_bases;
+ to->sudo_search_bases = from->sudo_search_bases;
+ to->service_search_bases = from->service_search_bases;
+ to->iphost_search_bases = from->iphost_search_bases;
+ to->ipnetwork_search_bases = from->ipnetwork_search_bases;
+ to->autofs_search_bases = from->autofs_search_bases;
+}