f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder/*
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder SSSD
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder Async LDAP Helper routines - retrieving groups
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder Copyright (C) Jan Zeleny <jzeleny@redhat.com> - 2011
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder This program is free software; you can redistribute it and/or modify
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder it under the terms of the GNU General Public License as published by
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder the Free Software Foundation; either version 3 of the License, or
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder (at your option) any later version.
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder This program is distributed in the hope that it will be useful,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
3ab1e7a18f3fc3eb004464bc54b7df4483f1f060Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder GNU General Public License for more details.
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder You should have received a copy of the GNU General Public License
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang*/
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder#include "util/util.h"
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder#include "util/probes.h"
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder#include "db/sysdb.h"
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang#include "providers/ldap/sdap_async_private.h"
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang#include "providers/ldap/ldap_common.h"
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang#include "providers/ldap/sdap_idmap.h"
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang#include "providers/ad/ad_common.h"
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder/* ==Group-Parsing Routines=============================================== */
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wangstatic int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang struct sysdb_ctx *ctx,
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang struct sss_domain_info *domain,
57d9ffd4f0d821632c5dd116a5301c3305599b19Christian Maeder const char *orig_dn,
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang char **_localdn,
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang bool *_is_group)
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang{
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang TALLOC_CTX *tmpctx;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang const char *attrs[] = {SYSDB_OBJECTCLASS, NULL};
8519df804b37f95a2394a6cd5662da02efa3400bChristian Maeder struct ldb_dn *base_dn;
8519df804b37f95a2394a6cd5662da02efa3400bChristian Maeder char *filter;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang struct ldb_message **msgs;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang size_t num_msgs;
8519df804b37f95a2394a6cd5662da02efa3400bChristian Maeder int ret;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang char *sanitized_dn;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang const char *objectclass;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang
8519df804b37f95a2394a6cd5662da02efa3400bChristian Maeder tmpctx = talloc_new(NULL);
8519df804b37f95a2394a6cd5662da02efa3400bChristian Maeder if (!tmpctx) {
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang return ENOMEM;
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang }
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang
264b794970b6f2bd437f14233f367f1067565728Jian Chun Wang ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder if (ret != EOK) {
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder ret = ENOMEM;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder goto done;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder }
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder filter = talloc_asprintf(tmpctx, "%s=%s", SYSDB_ORIG_DN, sanitized_dn);
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder if (!filter) {
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder ret = ENOMEM;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder goto done;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder }
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder base_dn = sysdb_domain_dn(tmpctx, domain);
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder if (!base_dn) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder ret = ENOMEM;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder goto done;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "Searching cache for [%s].\n", sanitized_dn);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder ret = sysdb_search_entry(tmpctx, ctx,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder base_dn, LDB_SCOPE_SUBTREE, filter, attrs,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder &num_msgs, &msgs);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ret) {
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder goto done;
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder }
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder if (num_msgs != 1) {
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder ret = ENOENT;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder goto done;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder }
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder *_localdn = talloc_strdup(memctx, ldb_dn_get_linearized(msgs[0]->dn));
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder if (!*_localdn) {
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder ret = ENOENT;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder goto done;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder }
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder if (_is_group != NULL) {
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder objectclass = ldb_msg_find_attr_as_string(msgs[0], SYSDB_OBJECTCLASS,
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder NULL);
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder if (objectclass == NULL) {
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "An antry without a %s?\n",
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder SYSDB_OBJECTCLASS);
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder ret = EINVAL;
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder goto done;
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder }
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder *_is_group = strcmp(SYSDB_GROUP_CLASS, objectclass) == 0;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder }
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder ret = EOK;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maederdone:
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder talloc_zfree(tmpctx);
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder return ret;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder}
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maederstatic errno_t
0015e1756b734b34d4b550318c078f9a0c585611Christian Maedersdap_get_members_with_primary_gid(TALLOC_CTX *mem_ctx,
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder struct sss_domain_info *domain,
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder gid_t gid, char ***_localdn, size_t *_ndn)
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder{
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder static const char *search_attrs[] = { SYSDB_NAME, NULL };
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder char *filter;
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder struct ldb_message **msgs;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder size_t count;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder size_t i;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder errno_t ret;
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder char **localdn;
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder /* Don't search if the group is non-posix */
0015e1756b734b34d4b550318c078f9a0c585611Christian Maeder if (!gid) return EOK;
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder filter = talloc_asprintf(mem_ctx, "(%s=%llu)", SYSDB_GIDNUM,
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder (unsigned long long) gid);
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder if (!filter) {
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder return ENOMEM;
c4a8059d0469a85bb58c28ac66729ac19d743d3cChristian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder ret = sysdb_search_users(mem_ctx, domain, filter,
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder search_attrs, &count, &msgs);
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder talloc_free(filter);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ret == ENOENT) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder *_localdn = NULL;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder *_ndn = 0;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return EOK;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder } else if (ret != EOK) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return ret;
50ed946595d60c06f773e73bb22b21f5cf1199caChristian Maeder }
50ed946595d60c06f773e73bb22b21f5cf1199caChristian Maeder
50ed946595d60c06f773e73bb22b21f5cf1199caChristian Maeder localdn = talloc_array(mem_ctx, char *, count);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (!localdn) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder talloc_free(msgs);
50ed946595d60c06f773e73bb22b21f5cf1199caChristian Maeder return ENOMEM;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder for (i=0; i < count; i++) {
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder localdn[i] = talloc_strdup(localdn,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder ldb_dn_get_linearized(msgs[i]->dn));
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (!localdn[i]) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder talloc_free(localdn);
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder talloc_free(msgs);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return ENOMEM;
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder }
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder }
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder talloc_free(msgs);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder *_localdn = localdn;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder *_ndn = count;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return EOK;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder}
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maederstatic errno_t
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maedersdap_dn_by_primary_gid(TALLOC_CTX *mem_ctx, struct sysdb_attrs *ldap_attrs,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct sss_domain_info *domain,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct sdap_options *opts,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder char ***_dn_list, size_t *_count)
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder{
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder gid_t gid;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder errno_t ret;
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder ret = sysdb_attrs_get_uint32_t(ldap_attrs,
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder opts->group_map[SDAP_AT_GROUP_GID].sys_name,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder &gid);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ret == ENOENT) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder /* Non-posix AD group. Skip. */
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder *_dn_list = NULL;
84855a862ab77950c0c5059b1bba98cce0fb8ac3Christian Maeder *_count = 0;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return EOK;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder } else if (ret && ret != ENOENT) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return ret;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder ret = sdap_get_members_with_primary_gid(mem_ctx, domain, gid,
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder _dn_list, _count);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ret) return ret;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return EOK;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder}
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maederstatic bool has_member(struct ldb_message_element *member_el,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder char *member)
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder{
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct ldb_val val;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder val.data = (uint8_t *) member;
c26ff5708c4a855bf9503b3001bcc19e5fd6286fChristian Maeder val.length = strlen(member);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder /* This is bad complexity, but the this loop should only be invoked in
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder * the very rare scenario of AD POSIX group that is primary group of
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder * some users but has user member attributes at the same time
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder */
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ldb_msg_find_val(member_el, &val) != NULL) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return true;
c26ff5708c4a855bf9503b3001bcc19e5fd6286fChristian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder return false;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder}
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maederstatic void link_pgroup_members(struct sysdb_attrs *group_attrs,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct ldb_message_element *member_el,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder char **userdns,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder size_t nuserdns)
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder{
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder int i, j;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder j = 0;
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder for (i=0; i < nuserdns; i++) {
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder if (has_member(member_el, userdns[i])) {
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder DEBUG(SSSDBG_TRACE_INTERNAL,
479da8506f391abe070ced2fb93c9759a280fa68Christian Maeder "Member %s already included, skipping\n", userdns[i]);
b68f7c26243f3f99df2ddf8de966c73ad78a3741Christian Maeder continue;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder member_el->values[member_el->num_values + j].data = (uint8_t *) \
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder talloc_steal(group_attrs, userdns[i]);
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder member_el->values[member_el->num_values + j].length = \
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder strlen(userdns[i]);
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder j++;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder member_el->num_values += j;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder}
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maederstatic int sdap_fill_memberships(struct sdap_options *opts,
03a6bbff551286168d0b15dc53476c2ede4e60d0Christian Maeder struct sysdb_attrs *group_attrs,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct sysdb_ctx *ctx,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder struct sss_domain_info *domain,
ce944c156ca6b4a56e81e232d7a22e582fbdcf33Christian Maeder hash_table_t *ghosts,
ce944c156ca6b4a56e81e232d7a22e582fbdcf33Christian Maeder struct ldb_val *values,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder int num_values,
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder char **userdns,
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder size_t nuserdns)
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder{
3ab1e7a18f3fc3eb004464bc54b7df4483f1f060Christian Maeder struct ldb_message_element *el;
3ab1e7a18f3fc3eb004464bc54b7df4483f1f060Christian Maeder int i, j;
3ab1e7a18f3fc3eb004464bc54b7df4483f1f060Christian Maeder int ret;
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder errno_t hret;
3ab1e7a18f3fc3eb004464bc54b7df4483f1f060Christian Maeder hash_key_t key;
d06598e0c310f65ab552ca55626c2f7694ffd5e3Christian Maeder hash_value_t value;
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder struct sdap_domain *sdom;
2344f16936f5b31c9530d0cafb3838e9df3f3644Christian Maeder struct sysdb_ctx *member_sysdb;
2344f16936f5b31c9530d0cafb3838e9df3f3644Christian Maeder struct sss_domain_info *member_dom;
2344f16936f5b31c9530d0cafb3838e9df3f3644Christian Maeder
2344f16936f5b31c9530d0cafb3838e9df3f3644Christian Maeder ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (ret) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed\n");
2344f16936f5b31c9530d0cafb3838e9df3f3644Christian Maeder goto done;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder /* Just allocate both big enough to contain all members for now */
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder el->values = talloc_realloc(group_attrs, el->values, struct ldb_val,
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder el->num_values + num_values + nuserdns);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (!el->values) {
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder DEBUG(SSSDBG_MINOR_FAILURE, "No memory to allocate group attrs\n");
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder ret = ENOMEM;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder goto done;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder }
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder j = el->num_values;
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder for (i = 0; i < num_values; i++) {
690e4ab8f298d9cff3803316cda70ad9b98e9c43Christian Maeder if (ghosts == NULL) {
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder hret = HASH_ERROR_KEY_NOT_FOUND;
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder } else {
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder key.type = HASH_KEY_STRING;
abcb1baa565c878598d732d0aa7724f474c9265cChristian Maeder key.str = (char *)values[i].data;
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder hret = hash_lookup(ghosts, &key, &value);
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder }
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder if (hret == HASH_ERROR_KEY_NOT_FOUND) {
f799084b320209cdd71a29e74fff1be054c1d342Christian Maeder sdom = sdap_domain_get_by_dn(opts, (char *)values[i].data);
if (sdom == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] is it out of domain "
"scope?\n", (char *)values[i].data);
member_sysdb = ctx;
member_dom = domain;
} else {
member_sysdb = sdom->dom->sysdb;
member_dom = sdom->dom;
}
/* sync search entry with this as origDN */
ret = sdap_find_entry_by_origDN(el->values, member_sysdb,
member_dom, (char *)values[i].data,
(char **)&el->values[j].data,
NULL);
if (ret == ENOENT) {
/* member may be outside of the configured search bases
* or out of scope of nesting limit */
DEBUG(SSSDBG_MINOR_FAILURE, "Member [%s] was not found in "
"cache. Is it out of scope?\n", (char *)values[i].data);
continue;
}
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"'sdap_find_entry_by_origDN' failed for member [%s].\n",
(char *)values[i].data);
goto done;
}
DEBUG(SSSDBG_TRACE_LIBS, " member #%d (%s): [%s]\n",
i, (char *)values[i].data,
(char *)el->values[j].data);
el->values[j].length = strlen((char *)el->values[j].data);
j++;
} else if (hret != HASH_SUCCESS) {
DEBUG(SSSDBG_MINOR_FAILURE,
"hash_lookup failed: [%d]: %s\n", hret, strerror(hret));
ret = EFAULT;
goto done;
}
/* If the member is in ghost table, it has
* already been processed - just skip it */
}
el->num_values = j;
link_pgroup_members(group_attrs, el, userdns, nuserdns);
ret = EOK;
done:
return ret;
}
/* ==Save-Group-Entry===================================================== */
/* FIXME: support non legacy */
/* FIXME: support storing additional attributes */
static errno_t
sdap_store_group_with_gid(struct sss_domain_info *domain,
const char *name,
gid_t gid,
struct sysdb_attrs *group_attrs,
uint64_t cache_timeout,
bool posix_group,
time_t now)
{
errno_t ret;
/* make sure that non-posix (empty or explicit gid=0) groups have the
* gidNumber set to zero even if updating existing group */
if (!posix_group) {
ret = sysdb_attrs_add_uint32(group_attrs, SYSDB_GIDNUM, 0);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Could not set explicit GID 0 for %s\n", name);
return ret;
}
}
ret = sysdb_store_group(domain, name, gid, group_attrs,
cache_timeout, now);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Could not store group %s\n", name);
return ret;
}
return ret;
}
static errno_t
sdap_process_ghost_members(struct sysdb_attrs *attrs,
struct sdap_options *opts,
hash_table_t *ghosts,
bool populate_members,
bool store_original_member,
struct sysdb_attrs *sysdb_attrs)
{
errno_t ret;
struct ldb_message_element *gh;
struct ldb_message_element *memberel;
struct ldb_message_element *sysdb_memberel;
struct ldb_message_element *ghostel;
size_t cnt;
int i;
int hret;
hash_key_t key;
hash_value_t value;
ret = sysdb_attrs_get_el(attrs, SYSDB_GHOST, &gh);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error reading ghost attributes: [%s]\n",
strerror(ret));
return ret;
}
ret = sysdb_attrs_get_el_ext(attrs,
opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
false, &memberel);
if (ret == ENOENT) {
/* Create a dummy element with no values in order for the loop to just
* fall through and make sure the attrs array is not reallocated.
*/
memberel = talloc(attrs, struct ldb_message_element);
if (memberel == NULL) {
return ENOMEM;
}
memberel->num_values = 0;
memberel->values = NULL;
} else if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error reading members: [%s]\n", strerror(ret));
return ret;
}
if (store_original_member) {
DEBUG(SSSDBG_TRACE_FUNC, "The group has %d members\n", memberel->num_values);
for (i = 0; i < memberel->num_values; i++) {
ret = sysdb_attrs_add_string(sysdb_attrs, SYSDB_ORIG_MEMBER,
(const char *) memberel->values[i].data);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Could not add member [%s]\n",
(const char *) memberel->values[i].data);
return ret;
}
}
}
if (populate_members) {
ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_MEMBER, &sysdb_memberel);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error reading group members from group_attrs: [%s]\n",
strerror(ret));
return ret;
}
sysdb_memberel->values = memberel->values;
sysdb_memberel->num_values = memberel->num_values;
}
ret = sysdb_attrs_get_el(sysdb_attrs, SYSDB_GHOST, &ghostel);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error getting ghost element: [%s]\n", strerror(ret));
return ret;
}
ghostel->values = gh->values;
ghostel->num_values = gh->num_values;
cnt = ghostel->num_values + memberel->num_values;
DEBUG(SSSDBG_TRACE_FUNC, "Group has %zu members\n", cnt);
/* Now process RFC2307bis ghost hash table */
if (ghosts && cnt > 0) {
ghostel->values = talloc_realloc(sysdb_attrs, ghostel->values,
struct ldb_val, cnt);
if (ghostel->values == NULL) {
return ENOMEM;
}
for (i = 0; i < memberel->num_values; i++) {
key.type = HASH_KEY_STRING;
key.str = (char *) memberel->values[i].data;
hret = hash_lookup(ghosts, &key, &value);
if (hret == HASH_ERROR_KEY_NOT_FOUND) {
continue;
} else if (hret != HASH_SUCCESS) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error checking hash table: [%s]\n",
hash_error_string(hret));
return EFAULT;
}
DEBUG(SSSDBG_TRACE_FUNC,
"Adding ghost member for group [%s]\n", (char *) value.ptr);
ghostel->values[ghostel->num_values].data = \
(uint8_t *) talloc_strdup(ghostel->values, value.ptr);
if (ghostel->values[ghostel->num_values].data == NULL) {
return ENOMEM;
}
ghostel->values[ghostel->num_values].length = strlen(value.ptr);
ghostel->num_values++;
}
}
return EOK;
}
static int sdap_save_group(TALLOC_CTX *memctx,
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
bool populate_members,
bool store_original_member,
hash_table_t *ghosts,
char **_usn_value,
time_t now)
{
struct ldb_message_element *el;
struct sysdb_attrs *group_attrs;
const char *group_name = NULL;
gid_t gid;
errno_t ret;
char *usn_value = NULL;
TALLOC_CTX *tmpctx = NULL;
bool posix_group;
bool use_id_mapping;
bool need_filter;
char *sid_str;
struct sss_domain_info *subdomain;
tmpctx = talloc_new(NULL);
if (!tmpctx) {
ret = ENOMEM;
goto done;
}
group_attrs = sysdb_new_attrs(tmpctx);
if (group_attrs == NULL) {
ret = ENOMEM;
goto done;
}
/* Always store SID string if available */
ret = sdap_attrs_get_sid_str(tmpctx, opts->idmap_ctx, attrs,
opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
&sid_str);
if (ret == EOK) {
ret = sysdb_attrs_add_string(group_attrs, SYSDB_SID_STR, sid_str);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, "Could not add SID string: [%s]\n",
sss_strerror(ret));
goto done;
}
} else if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_ALL, "objectSID: not available for group [%s].\n",
group_name);
sid_str = NULL;
} else {
DEBUG(SSSDBG_MINOR_FAILURE, "Could not identify objectSID: [%s]\n",
sss_strerror(ret));
sid_str = NULL;
}
/* Always store UUID if available */
ret = sysdb_handle_original_uuid(
opts->group_map[SDAP_AT_GROUP_UUID].def_name,
attrs,
opts->group_map[SDAP_AT_GROUP_UUID].sys_name,
group_attrs, SYSDB_UUID);
if (ret != EOK) {
DEBUG((ret == ENOENT) ? SSSDBG_TRACE_ALL : SSSDBG_MINOR_FAILURE,
"Failed to retrieve UUID [%d][%s].\n", ret, sss_strerror(ret));
}
/* If this object has a SID available, we will determine the correct
* domain by its SID. */
if (sid_str != NULL) {
subdomain = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
sid_str);
if (subdomain) {
dom = subdomain;
} else {
DEBUG(SSSDBG_TRACE_FUNC, "SID %s does not belong to any known "
"domain\n", sid_str);
}
}
ret = sdap_get_group_primary_name(tmpctx, opts, attrs, dom, &group_name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
posix_group = true;
ret = sdap_check_ad_group_type(dom, opts, attrs, group_name,
&need_filter);
if (ret != EOK) {
goto done;
}
if (need_filter) {
posix_group = false;
gid = 0;
ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, false);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Error: Failed to mark group as non-posix!\n");
goto done;
}
}
if (posix_group) {
use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(opts->idmap_ctx,
dom->name,
sid_str);
if (use_id_mapping) {
posix_group = true;
if (sid_str == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE, "SID not available, cannot map a " \
"unix ID to group [%s].\n", group_name);
ret = ENOENT;
goto done;
}
DEBUG(SSSDBG_TRACE_LIBS,
"Mapping group [%s] objectSID [%s] to unix ID\n",
group_name, sid_str);
/* Convert the SID into a UNIX group ID */
ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, sid_str, &gid);
if (ret == ENOTSUP) {
/* ENOTSUP is returned if built-in SID was provided
* => do not store the group, but return EOK */
DEBUG(SSSDBG_TRACE_FUNC, "Skipping built-in object.\n");
ret = EOK;
goto done;
} else if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Could not convert SID string: [%s]\n",
sss_strerror(ret));
goto done;
}
/* Store the GID in the ldap_attrs so it doesn't get
* treated as a missing attribute from LDAP and removed.
*/
ret = sdap_replace_id(attrs, SYSDB_GIDNUM, gid);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot set the id-mapped GID\n");
goto done;
}
} else {
ret = sysdb_attrs_get_bool(attrs, SYSDB_POSIX, &posix_group);
if (ret == ENOENT) {
posix_group = true;
} else if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error reading posix attribute: [%s]\n",
sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_INTERNAL,
"This is%s a posix group\n", (posix_group)?"":" not");
ret = sysdb_attrs_add_bool(group_attrs, SYSDB_POSIX, posix_group);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error setting posix attribute: [%s]\n",
sss_strerror(ret));
goto done;
}
ret = sysdb_attrs_get_uint32_t(attrs,
opts->group_map[SDAP_AT_GROUP_GID].sys_name,
&gid);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"no gid provided for [%s] in domain [%s].\n",
group_name, dom->name);
ret = EINVAL;
goto done;
}
}
}
/* check that the gid is valid for this domain */
if (posix_group) {
if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Group [%s] filtered out! (id out of range)\n", group_name);
ret = EINVAL;
goto done;
}
/* Group ID OK */
}
ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN",
group_name, group_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error setting original DN: [%s]\n",
sss_strerror(ret));
goto done;
}
ret = sdap_attrs_add_string(attrs,
opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
"original mod-Timestamp",
group_name, group_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error setting mod timestamp: [%s]\n",
sss_strerror(ret));
goto done;
}
ret = sysdb_attrs_get_el(attrs,
opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
if (ret) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error looking up group USN: [%s]\n",
sss_strerror(ret));
goto done;
}
if (el->num_values == 0) {
DEBUG(SSSDBG_TRACE_FUNC,
"Original USN value is not available for [%s].\n", group_name);
} else {
ret = sysdb_attrs_add_string(group_attrs,
opts->group_map[SDAP_AT_GROUP_USN].sys_name,
(const char*)el->values[0].data);
if (ret) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Error setting group USN: [%s]\n",
sss_strerror(ret));
goto done;
}
usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
if (!usn_value) {
ret = ENOMEM;
goto done;
}
}
ret = sdap_process_ghost_members(attrs, opts, ghosts,
populate_members, store_original_member,
group_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to save ghost members\n");
goto done;
}
ret = sdap_save_all_names(group_name, attrs, dom,
SYSDB_MEMBER_GROUP, group_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to save group names\n");
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, "Storing info for group %s\n", group_name);
ret = sdap_store_group_with_gid(dom, group_name, gid, group_attrs,
dom->group_timeout,
posix_group, now);
if (ret) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Could not store group with GID: [%s]\n",
sss_strerror(ret));
goto done;
}
if (_usn_value) {
*_usn_value = talloc_steal(memctx, usn_value);
}
talloc_steal(memctx, group_attrs);
ret = EOK;
done:
if (ret) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Failed to save group [%s]: [%s]\n",
group_name ? group_name : "Unknown",
sss_strerror(ret));
}
talloc_free(tmpctx);
return ret;
}
static errno_t
are_sids_from_same_dom(const char *sid1, const char *sid2, bool *_result)
{
size_t len_prefix_sid1;
size_t len_prefix_sid2;
char *rid1, *rid2;
bool result;
rid1 = strrchr(sid1, '-');
if (rid1 == NULL) {
return EINVAL;
}
rid2 = strrchr(sid2, '-');
if (rid2 == NULL) {
return EINVAL;
}
len_prefix_sid1 = rid1 - sid1;
len_prefix_sid2 = rid2 - sid2;
result = (len_prefix_sid1 == len_prefix_sid2) &&
(strncmp(sid1, sid2, len_prefix_sid1) == 0);
*_result = result;
return EOK;
}
static errno_t
retain_extern_members(TALLOC_CTX *mem_ctx,
struct sss_domain_info *dom,
const char *group_name,
const char *group_sid,
char ***_userdns,
size_t *_nuserdns)
{
TALLOC_CTX *tmp_ctx;
const char **sids, **dns;
bool same_domain;
errno_t ret;
size_t i, n;
size_t nuserdns = 0;
const char **userdns = NULL;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return ENOMEM;
}
ret = sysdb_get_sids_of_members(tmp_ctx, dom, group_name, &sids, &dns, &n);
if (ret != EOK) {
if (ret != ENOENT) {
DEBUG(SSSDBG_TRACE_ALL,
"get_sids_of_members failed: %d [%s]\n",
ret, sss_strerror(ret));
}
goto done;
}
for (i=0; i < n; i++) {
ret = are_sids_from_same_dom(group_sid, sids[i], &same_domain);
if (ret == EOK && !same_domain) {
DEBUG(SSSDBG_TRACE_ALL, "extern member: %s\n", dns[i]);
nuserdns++;
userdns = talloc_realloc(tmp_ctx, userdns, const char*, nuserdns);
if (userdns == NULL) {
ret = ENOMEM;
goto done;
}
userdns[nuserdns-1] = talloc_steal(userdns, dns[i]);
}
}
*_nuserdns = nuserdns;
*_userdns = discard_const(talloc_steal(mem_ctx, userdns));
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/* ==Save-Group-Memebrs=================================================== */
/* FIXME: support non legacy */
/* FIXME: support storing additional attributes */
static int sdap_save_grpmem(TALLOC_CTX *memctx,
struct sysdb_ctx *ctx,
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
hash_table_t *ghosts,
time_t now)
{
struct ldb_message_element *el;
struct sysdb_attrs *group_attrs = NULL;
const char *group_sid;
const char *group_name;
char **userdns = NULL;
size_t nuserdns = 0;
struct sss_domain_info *group_dom = NULL;
int ret;
const char *remove_attrs[] = {SYSDB_MEMBER, SYSDB_ORIG_MEMBER, SYSDB_GHOST,
NULL};
if (dom->ignore_group_members) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Group members are ignored, nothing to do. If you see this " \
"message it might indicate an error in the group processing " \
"logic.\n");
return EOK;
}
ret = sysdb_attrs_get_string(attrs, SYSDB_SID_STR, &group_sid);
if (ret != EOK) {
/* Try harder. */
ret = sdap_attrs_get_sid_str(memctx, opts->idmap_ctx, attrs,
opts->group_map[SDAP_AT_GROUP_OBJECTSID].sys_name,
discard_const(&group_sid));
if (ret != EOK) {
DEBUG(SSSDBG_TRACE_FUNC, "Failed to get group sid\n");
group_sid = NULL;
}
}
if (group_sid != NULL) {
group_dom = sss_get_domain_by_sid_ldap_fallback(get_domains_head(dom),
group_sid);
if (group_dom == NULL) {
DEBUG(SSSDBG_TRACE_FUNC, "SID [%s] does not belong to any known "
"domain, using [%s].\n", group_sid,
dom->name);
}
}
if (group_dom == NULL) {
group_dom = dom;
}
ret = sdap_get_group_primary_name(memctx, opts, attrs, group_dom,
&group_name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to get group name\n");
goto fail;
}
DEBUG(SSSDBG_TRACE_FUNC, "Processing group %s\n", group_name);
/* With AD we also want to merge in parent groups of primary GID as they
* are reported with tokenGroups, too
*/
if (opts->schema_type == SDAP_SCHEMA_AD) {
ret = sdap_dn_by_primary_gid(memctx, attrs, group_dom, opts,
&userdns, &nuserdns);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"sdap_dn_by_primary_gid failed: [%d][%s].\n",
ret, strerror(ret));
goto fail;
}
}
/* This is a temporal solution until the IPA provider is able to
* resolve external group membership.
* https://fedorahosted.org/sssd/ticket/2522
*/
if (opts->schema_type == SDAP_SCHEMA_IPA_V1) {
if (group_sid != NULL) {
ret = retain_extern_members(memctx, group_dom, group_name,
group_sid, &userdns, &nuserdns);
if (ret != EOK) {
DEBUG(SSSDBG_TRACE_INTERNAL,
"retain_extern_members failed: %d:[%s].\n",
ret, sss_strerror(ret));
}
}
}
ret = sysdb_attrs_get_el(attrs,
opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_attrs_get_el failed: [%d][%s].\n",
ret, strerror(ret));
goto fail;
}
if (el->num_values == 0 && nuserdns == 0) {
DEBUG(SSSDBG_TRACE_FUNC,
"No members for group [%s]\n", group_name);
ret = sysdb_remove_attrs(group_dom, group_name, SYSDB_MEMBER_GROUP,
discard_const(remove_attrs));
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_remove_attrs failed.\n");
goto fail;
}
} else {
DEBUG(SSSDBG_TRACE_FUNC,
"Adding member users to group [%s]\n", group_name);
group_attrs = sysdb_new_attrs(memctx);
if (!group_attrs) {
DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_new_attrs failed\n");
ret = ENOMEM;
goto fail;
}
ret = sdap_fill_memberships(opts, group_attrs, ctx, group_dom, ghosts,
el->values, el->num_values,
userdns, nuserdns);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE,
"sdap_fill_memberships failed with [%d]: %s\n", ret,
strerror(ret));
goto fail;
}
}
ret = sysdb_store_group(group_dom, group_name, 0, group_attrs,
group_dom->group_timeout, now);
if (ret) {
DEBUG(SSSDBG_MINOR_FAILURE, "sysdb_store_group failed: [%d][%s].\n",
ret, strerror(ret));
goto fail;
}
return EOK;
fail:
DEBUG(SSSDBG_OP_FAILURE,
"Failed to save members of group %s\n", group_name);
return ret;
}
/* ==Generic-Function-to-save-multiple-groups============================= */
static int sdap_save_groups(TALLOC_CTX *memctx,
struct sysdb_ctx *sysdb,
struct sss_domain_info *dom,
struct sdap_options *opts,
struct sysdb_attrs **groups,
int num_groups,
bool populate_members,
hash_table_t *ghosts,
bool save_orig_member,
char **_usn_value)
{
TALLOC_CTX *tmpctx;
char *higher_usn = NULL;
char *usn_value;
bool twopass;
bool has_nesting = false;
int ret;
errno_t sret;
int i;
struct sysdb_attrs **saved_groups = NULL;
int nsaved_groups = 0;
time_t now;
bool in_transaction = false;
switch (opts->schema_type) {
case SDAP_SCHEMA_RFC2307:
twopass = false;
break;
case SDAP_SCHEMA_RFC2307BIS:
case SDAP_SCHEMA_IPA_V1:
case SDAP_SCHEMA_AD:
twopass = true;
has_nesting = true;
break;
default:
return EINVAL;
}
tmpctx = talloc_new(memctx);
if (!tmpctx) {
return ENOMEM;
}
ret = sysdb_transaction_start(sysdb);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
goto done;
}
in_transaction = true;
if (twopass && !populate_members) {
saved_groups = talloc_array(tmpctx, struct sysdb_attrs *,
num_groups);
if (!saved_groups) {
ret = ENOMEM;
goto done;
}
}
now = time(NULL);
for (i = 0; i < num_groups; i++) {
usn_value = NULL;
/* if 2 pass savemembers = false */
ret = sdap_save_group(tmpctx, opts, dom, groups[i],
populate_members,
has_nesting && save_orig_member,
ghosts, &usn_value, now);
/* Do not fail completely on errors.
* Just report the failure to save and go on */
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to store group %d. Ignoring.\n", i);
} else {
DEBUG(SSSDBG_TRACE_ALL, "Group %d processed!\n", i);
if (twopass && !populate_members) {
saved_groups[nsaved_groups] = groups[i];
nsaved_groups++;
}
}
if (usn_value) {
if (higher_usn) {
if ((strlen(usn_value) > strlen(higher_usn)) ||
(strcmp(usn_value, higher_usn) > 0)) {
talloc_zfree(higher_usn);
higher_usn = usn_value;
} else {
talloc_zfree(usn_value);
}
} else {
higher_usn = usn_value;
}
}
}
if (twopass && !populate_members) {
for (i = 0; i < nsaved_groups; i++) {
ret = sdap_save_grpmem(tmpctx, sysdb, opts, dom, saved_groups[i],
ghosts, now);
/* Do not fail completely on errors.
* Just report the failure to save and go on */
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to store group %d members.\n", i);
} else {
DEBUG(SSSDBG_TRACE_ALL, "Group %d members processed!\n", i);
}
}
}
ret = sysdb_transaction_commit(sysdb);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
goto done;
}
in_transaction = false;
if (_usn_value) {
*_usn_value = talloc_steal(memctx, higher_usn);
}
done:
if (in_transaction) {
sret = sysdb_transaction_cancel(sysdb);
if (sret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
}
}
talloc_zfree(tmpctx);
return ret;
}
/* ==Process-Groups======================================================= */
struct sdap_process_group_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
struct sss_domain_info *dom;
struct sysdb_ctx *sysdb;
struct sysdb_attrs *group;
struct ldb_message_element* sysdb_dns;
struct ldb_message_element* ghost_dns;
char **queued_members;
int queue_len;
const char **attrs;
const char *filter;
size_t queue_idx;
size_t count;
size_t check_count;
bool enumeration;
};
#define GROUPMEMBER_REQ_PARALLEL 50
static void sdap_process_group_members(struct tevent_req *subreq);
static int sdap_process_group_members_2307bis(struct tevent_req *req,
struct sdap_process_group_state *state,
struct ldb_message_element *memberel);
static int sdap_process_group_members_2307(struct sdap_process_group_state *state,
struct ldb_message_element *memberel,
struct ldb_message_element *ghostel);
static errno_t sdap_process_group_create_dns(TALLOC_CTX *mem_ctx,
size_t num_values,
struct ldb_message_element **_dns)
{
struct ldb_message_element *dns;
dns = talloc(mem_ctx, struct ldb_message_element);
if (dns == NULL) {
return ENOMEM;
}
dns->num_values = 0;
dns->values = talloc_array(dns, struct ldb_val,
num_values);
if (dns->values == NULL) {
talloc_zfree(dns);
return ENOMEM;
}
*_dns = dns;
return EOK;
}
static struct tevent_req *
sdap_process_group_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
struct sss_domain_info *dom,
struct sysdb_ctx *sysdb,
struct sdap_options *opts,
struct sdap_handle *sh,
struct sysdb_attrs *group,
bool enumeration)
{
struct ldb_message_element *el;
struct ldb_message_element *ghostel;
struct sdap_process_group_state *grp_state;
struct tevent_req *req = NULL;
const char **attrs;
char* filter;
int ret;
req = tevent_req_create(memctx, &grp_state,
struct sdap_process_group_state);
if (!req) return NULL;
ret = build_attrs_from_map(grp_state, opts->user_map, opts->user_map_cnt,
NULL, &attrs, NULL);
if (ret) {
goto done;
}
/* FIXME: we ignore nested rfc2307bis groups for now */
filter = talloc_asprintf(grp_state, "(objectclass=%s)",
opts->user_map[SDAP_OC_USER].name);
if (!filter) {
talloc_zfree(req);
return NULL;
}
grp_state->ev = ev;
grp_state->opts = opts;
grp_state->dom = dom;
grp_state->sh = sh;
grp_state->sysdb = sysdb;
grp_state->group = group;
grp_state->check_count = 0;
grp_state->queue_idx = 0;
grp_state->queued_members = NULL;
grp_state->queue_len = 0;
grp_state->filter = filter;
grp_state->attrs = attrs;
grp_state->enumeration = enumeration;
ret = sysdb_attrs_get_el(group,
opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
&el);
if (ret) {
goto done;
}
/* Group without members */
if (el->num_values == 0) {
DEBUG(SSSDBG_OP_FAILURE, "No Members. Done!\n");
ret = EOK;
goto done;
}
ret = sysdb_attrs_get_el(group,
SYSDB_GHOST,
&ghostel);
if (ret) {
goto done;
}
if (ghostel->num_values == 0) {
/* Element was probably newly created, look for "member" again */
ret = sysdb_attrs_get_el(group,
opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
&el);
if (ret != EOK) {
goto done;
}
}
ret = sdap_process_group_create_dns(grp_state, el->num_values,
&grp_state->sysdb_dns);
if (ret != EOK) {
goto done;
}
ret = sdap_process_group_create_dns(grp_state, el->num_values,
&grp_state->ghost_dns);
if (ret != EOK) {
goto done;
}
switch (opts->schema_type) {
case SDAP_SCHEMA_RFC2307:
ret = sdap_process_group_members_2307(grp_state, el, ghostel);
break;
case SDAP_SCHEMA_IPA_V1:
case SDAP_SCHEMA_AD:
case SDAP_SCHEMA_RFC2307BIS:
/* Note that this code branch will be used only if
* ldap_nesting_level = 0 is set in config file
*/
ret = sdap_process_group_members_2307bis(req, grp_state, el);
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE,
"Unknown schema type %d\n", opts->schema_type);
ret = EINVAL;
break;
}
done:
/* We managed to process all the entries */
/* EBUSY means we need to wait for entries in LDAP */
if (ret == EOK) {
DEBUG(SSSDBG_TRACE_LIBS, "All group members processed\n");
tevent_req_done(req);
tevent_req_post(req, ev);
}
if (ret != EOK && ret != EBUSY) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static int
sdap_process_missing_member_2307bis(struct tevent_req *req,
char *user_dn,
unsigned num_users)
{
struct sdap_process_group_state *grp_state =
tevent_req_data(req, struct sdap_process_group_state);
struct tevent_req *subreq;
/*
* Issue at most GROUPMEMBER_REQ_PARALLEL LDAP searches at once.
* The rest is sent while the results are being processed.
* We limit the number as of request here, as the Server might
* enforce limits on the number of pending operations per
* connection.
*/
if (grp_state->check_count > GROUPMEMBER_REQ_PARALLEL) {
DEBUG(SSSDBG_TRACE_LIBS, " queueing search for: %s\n", user_dn);
if (!grp_state->queued_members) {
DEBUG(SSSDBG_TRACE_LIBS,
"Allocating queue for %zu members\n",
num_users - grp_state->check_count);
grp_state->queued_members = talloc_array(grp_state, char *,
num_users - grp_state->check_count + 1);
if (!grp_state->queued_members) {
return ENOMEM;
}
}
grp_state->queued_members[grp_state->queue_len] = user_dn;
grp_state->queue_len++;
} else {
subreq = sdap_get_generic_send(grp_state,
grp_state->ev,
grp_state->opts,
grp_state->sh,
user_dn,
LDAP_SCOPE_BASE,
grp_state->filter,
grp_state->attrs,
grp_state->opts->user_map,
grp_state->opts->user_map_cnt,
dp_opt_get_int(grp_state->opts->basic,
SDAP_SEARCH_TIMEOUT),
false);
if (!subreq) {
return ENOMEM;
}
tevent_req_set_callback(subreq, sdap_process_group_members, req);
}
grp_state->check_count++;
return EOK;
}
static int
sdap_process_group_members_2307bis(struct tevent_req *req,
struct sdap_process_group_state *state,
struct ldb_message_element *memberel)
{
char *member_dn;
char *strdn;
int ret;
int i;
int nesting_level;
bool is_group;
nesting_level = dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL);
for (i=0; i < memberel->num_values; i++) {
member_dn = (char *)memberel->values[i].data;
ret = sdap_find_entry_by_origDN(state->sysdb_dns->values,
state->sysdb,
state->dom,
member_dn,
&strdn,
&is_group);
if (ret == EOK) {
if (nesting_level == 0 && is_group) {
/* Ignore group members which are groups themselves. */
continue;
}
/*
* User already cached in sysdb. Remember the sysdb DN for later
* use by sdap_save_groups()
*/
DEBUG(SSSDBG_TRACE_LIBS, "sysdbdn: %s\n", strdn);
state->sysdb_dns->values[state->sysdb_dns->num_values].data =
(uint8_t*) strdn;
state->sysdb_dns->values[state->sysdb_dns->num_values].length =
strlen(strdn);
state->sysdb_dns->num_values++;
} else if (ret == ENOENT) {
if (!state->enumeration) {
/* The user is not in sysdb, need to add it
* We don't need to do this if we're in an enumeration,
* because all real members should all be populated
* already by the first pass of the enumeration.
* Also, we don't want to be holding the sysdb
* transaction while we're performing LDAP lookups.
*/
DEBUG(SSSDBG_TRACE_LIBS,
"Searching LDAP for missing user entry\n");
ret = sdap_process_missing_member_2307bis(req,
member_dn,
memberel->num_values);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Error processing missing member #%d (%s):\n",
i, member_dn);
return ret;
}
}
} else {
DEBUG(SSSDBG_CRIT_FAILURE,
"Error checking cache for member #%d (%s):\n",
i, (char *)memberel->values[i].data);
return ret;
}
}
if (state->queue_len > 0) {
state->queued_members[state->queue_len]=NULL;
}
if (state->check_count == 0) {
/*
* All group members are already cached in sysdb, we are done
* with this group. To avoid redundant sysdb lookups, populate the
* "member" attribute of the group entry with the sysdb DNs of
* the members.
*/
ret = EOK;
memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
memberel->num_values = state->sysdb_dns->num_values;
} else {
state->count = state->check_count;
ret = EBUSY;
}
return ret;
}
static int
sdap_add_group_member_2307(struct ldb_message_element *sysdb_dns,
const char *username)
{
sysdb_dns->values[sysdb_dns->num_values].data =
(uint8_t *) talloc_strdup(sysdb_dns->values, username);
if (sysdb_dns->values[sysdb_dns->num_values].data == NULL) {
return ENOMEM;
}
sysdb_dns->values[sysdb_dns->num_values].length =
strlen(username);
sysdb_dns->num_values++;
return EOK;
}
static int
sdap_process_missing_member_2307(struct sdap_process_group_state *state,
char *member_name)
{
int ret;
TALLOC_CTX *tmp_ctx;
const char *filter;
const char *username;
const char *user_dn;
char *sanitized_name;
size_t count;
struct ldb_message **msgs = NULL;
static const char *attrs[] = { SYSDB_NAME, NULL };
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) return ENOMEM;
ret = sss_filter_sanitize(tmp_ctx, member_name, &sanitized_name);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to sanitize the given name:'%s'.\n", member_name);
goto done;
}
/* Check for the alias in the sysdb */
filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_NAME_ALIAS,
sanitized_name);
if (!filter) {
ret = ENOMEM;
goto done;
}
ret = sysdb_search_users(tmp_ctx, state->dom, filter,
attrs, &count, &msgs);
if (ret == EOK && count > 0) {
/* Entry exists but the group references it with an alias. */
if (count != 1) {
DEBUG(SSSDBG_CRIT_FAILURE,
"More than one entry with this alias?\n");
ret = EIO;
goto done;
}
/* fill username with primary name */
username = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
if (username == NULL) {
ret = EINVAL;
DEBUG(SSSDBG_MINOR_FAILURE, "Inconsistent sysdb: user "
"without primary name?\n");
goto done;
}
user_dn = sysdb_user_strdn(tmp_ctx, state->dom->name, username);
if (user_dn == NULL) {
return ENOMEM;
}
ret = sdap_add_group_member_2307(state->sysdb_dns, user_dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", username);
}
} else if (ret == ENOENT) {
/* The entry really does not exist, add a ghost */
DEBUG(SSSDBG_TRACE_FUNC, "Adding a ghost entry\n");
ret = sdap_add_group_member_2307(state->ghost_dns, member_name);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Could not add group member %s\n", member_name);
}
} else {
ret = EIO;
}
done:
talloc_free(tmp_ctx);
return ret;
}
static int
sdap_process_group_members_2307(struct sdap_process_group_state *state,
struct ldb_message_element *memberel,
struct ldb_message_element *ghostel)
{
struct ldb_message *msg;
char *member_attr_val;
char *member_name;
char *userdn;
int ret;
int i;
for (i=0; i < memberel->num_values; i++) {
member_attr_val = (char *)memberel->values[i].data;
/* We need to skip over zero-length usernames */
if (member_attr_val[0] == '\0') continue;
/* RFC2307 stores members as plain usernames in the member attribute.
* Internally, we use fqdns in the cache..
*/
member_name = sss_create_internal_fqname(state, member_attr_val,
state->dom->name);
if (member_name == NULL) {
return ENOMEM;
}
ret = sysdb_search_user_by_name(state, state->dom, member_name,
NULL, &msg);
if (ret == EOK) {
/*
* User already cached in sysdb. Remember the sysdb DN for later
* use by sdap_save_groups()
*/
DEBUG(SSSDBG_TRACE_LIBS,
"Member already cached in sysdb: %s\n", member_name);
userdn = sysdb_user_strdn(state->sysdb_dns, state->dom->name, member_name);
if (userdn == NULL) {
return ENOMEM;
}
ret = sdap_add_group_member_2307(state->sysdb_dns, userdn);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Could not add member %s into sysdb\n", member_name);
goto done;
}
} else if (ret == ENOENT) {
/* The user is not in sysdb, need to add it */
DEBUG(SSSDBG_TRACE_LIBS, "member #%d (%s): not found in sysdb\n",
i, member_name);
ret = sdap_process_missing_member_2307(state, member_name);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Error processing missing member #%d (%s):\n",
i, member_name);
goto done;
}
} else {
DEBUG(SSSDBG_CRIT_FAILURE,
"Error checking cache for member #%d (%s):\n",
i, (char *) memberel->values[i].data);
goto done;
}
}
ret = EOK;
talloc_free(memberel->values);
memberel->values = talloc_steal(state->group, state->sysdb_dns->values);
memberel->num_values = state->sysdb_dns->num_values;
talloc_free(ghostel->values);
ghostel->values = talloc_steal(state->group, state->ghost_dns->values);
ghostel->num_values = state->ghost_dns->num_values;
done:
return ret;
}
static void sdap_process_group_members(struct tevent_req *subreq)
{
struct sysdb_attrs **usr_attrs;
size_t count;
int ret;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct sdap_process_group_state *state =
tevent_req_data(req, struct sdap_process_group_state);
struct ldb_message_element *el;
char *name_string;
state->check_count--;
DEBUG(SSSDBG_TRACE_ALL, "Members remaining: %zu\n", state->check_count);
ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
talloc_zfree(subreq);
if (ret) {
goto next;
}
if (count != 1) {
ret = EINVAL;
DEBUG(SSSDBG_TRACE_LIBS,
"Expected one user entry and got %zu\n", count);
goto next;
}
ret = sysdb_attrs_get_el(usr_attrs[0],
state->opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
if (el->num_values == 0) {
ret = EINVAL;
}
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to get the member's name\n");
goto next;
}
name_string = sss_create_internal_fqname(state,
(const char *) el[0].values[0].data,
state->dom->name);
if (name_string == NULL) {
ret = ENOMEM;
goto next;
}
state->ghost_dns->values[state->ghost_dns->num_values].data =
talloc_steal(state->ghost_dns->values, (uint8_t *) name_string);
state->ghost_dns->values[state->ghost_dns->num_values].length =
strlen(name_string);
state->ghost_dns->num_values++;
next:
if (ret) {
DEBUG(SSSDBG_TRACE_FUNC,
"Error reading group member[%d]: %s. Skipping\n",
ret, strerror(ret));
state->count--;
}
/* Are there more searches for uncached users to submit ? */
if (state->queued_members && state->queued_members[state->queue_idx]) {
subreq = sdap_get_generic_send(state,
state->ev, state->opts, state->sh,
state->queued_members[state->queue_idx],
LDAP_SCOPE_BASE,
state->filter,
state->attrs,
state->opts->user_map,
state->opts->user_map_cnt,
dp_opt_get_int(state->opts->basic,
SDAP_SEARCH_TIMEOUT),
false);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq,
sdap_process_group_members, req);
state->queue_idx++;
}
if (state->check_count == 0) {
/*
* To avoid redundant sysdb lookups, populate the "member" attribute
* of the group entry with the sysdb DNs of the members.
*/
ret = sysdb_attrs_get_el(state->group,
state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
&el);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to get the group member attribute [%d]: %s\n",
ret, strerror(ret));
tevent_req_error(req, ret);
return;
}
el->values = talloc_steal(state->group, state->sysdb_dns->values);
el->num_values = state->sysdb_dns->num_values;
ret = sysdb_attrs_get_el(state->group, SYSDB_GHOST, &el);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
el->values = talloc_steal(state->group, state->ghost_dns->values);
el->num_values = state->ghost_dns->num_values;
DEBUG(SSSDBG_TRACE_ALL, "Processed Group - Done\n");
tevent_req_done(req);
}
}
static int sdap_process_group_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/* ==Search-Groups-with-filter============================================ */
struct sdap_get_groups_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
struct sss_domain_info *dom;
struct sdap_domain *sdom;
struct sysdb_ctx *sysdb;
const char **attrs;
const char *base_filter;
char *filter;
int timeout;
enum sdap_entry_lookup_type lookup_type;
bool no_members;
char *higher_usn;
struct sysdb_attrs **groups;
size_t count;
size_t check_count;
hash_table_t *missing_external;
hash_table_t *user_hash;
hash_table_t *group_hash;
size_t base_iter;
struct sdap_search_base **search_bases;
struct sdap_handle *ldap_sh;
struct sdap_id_op *op;
};
static errno_t sdap_get_groups_next_base(struct tevent_req *req);
static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq);
static void sdap_get_groups_process(struct tevent_req *subreq);
static void sdap_get_groups_done(struct tevent_req *subreq);
struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
struct sdap_domain *sdom,
struct sdap_options *opts,
struct sdap_handle *sh,
const char **attrs,
const char *filter,
int timeout,
enum sdap_entry_lookup_type lookup_type,
bool no_members)
{
errno_t ret;
struct tevent_req *req;
struct tevent_req *subreq;
struct sdap_get_groups_state *state;
struct ad_id_ctx *subdom_id_ctx;
req = tevent_req_create(memctx, &state, struct sdap_get_groups_state);
if (!req) return NULL;
state->ev = ev;
state->opts = opts;
state->sdom = sdom;
state->dom = sdom->dom;
state->sh = sh;
state->sysdb = sdom->dom->sysdb;
state->attrs = attrs;
state->higher_usn = NULL;
state->groups = NULL;
state->count = 0;
state->timeout = timeout;
state->lookup_type = lookup_type;
state->no_members = no_members;
state->base_filter = filter;
state->base_iter = 0;
state->search_bases = sdom->group_search_bases;
if (!state->search_bases) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Group lookup request without a search base\n");
ret = EINVAL;
goto done;
}
/* With AD by default the Global Catalog is used for lookup. But the GC
* group object might not have full group membership data. To make sure we
* connect to an LDAP server of the group's domain. */
if (state->opts->schema_type == SDAP_SCHEMA_AD && sdom->pvt != NULL) {
subdom_id_ctx = talloc_get_type(sdom->pvt, struct ad_id_ctx);
state->op = sdap_id_op_create(state, subdom_id_ctx->ldap_ctx->conn_cache);
if (!state->op) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
ret = ENOMEM;
goto done;
}
subreq = sdap_id_op_connect_send(state->op, state, &ret);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq,
sdap_get_groups_ldap_connect_done,
req);
return req;
}
ret = sdap_get_groups_next_base(req);
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void sdap_get_groups_ldap_connect_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct sdap_get_groups_state *state;
int ret;
int dp_error;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct sdap_get_groups_state);
ret = sdap_id_op_connect_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
state->ldap_sh = sdap_id_op_handle(state->op);
ret = sdap_get_groups_next_base(req);
if (ret != EOK) {
tevent_req_error(req, ret);
}
return;
}
static errno_t sdap_get_groups_next_base(struct tevent_req *req)
{
struct tevent_req *subreq;
struct sdap_get_groups_state *state;
bool need_paging = false;
int sizelimit = 0;
state = tevent_req_data(req, struct sdap_get_groups_state);
talloc_zfree(state->filter);
state->filter = sdap_combine_filters(state, state->base_filter,
state->search_bases[state->base_iter]->filter);
if (!state->filter) {
return ENOMEM;
}
DEBUG(SSSDBG_TRACE_FUNC,
"Searching for groups with base [%s]\n",
state->search_bases[state->base_iter]->basedn);
switch (state->lookup_type) {
case SDAP_LOOKUP_SINGLE:
break;
/* Only requests that can return multiple entries should require
* the paging control
*/
case SDAP_LOOKUP_WILDCARD:
sizelimit = dp_opt_get_int(state->opts->basic, SDAP_WILDCARD_LIMIT);
need_paging = true;
break;
case SDAP_LOOKUP_ENUMERATE:
need_paging = true;
break;
}
subreq = sdap_get_and_parse_generic_send(
state, state->ev, state->opts,
state->ldap_sh != NULL ? state->ldap_sh : state->sh,
state->search_bases[state->base_iter]->basedn,
state->search_bases[state->base_iter]->scope,
state->filter, state->attrs,
state->opts->group_map, SDAP_OPTS_GROUP,
0, NULL, NULL, sizelimit, state->timeout,
need_paging);
if (!subreq) {
return ENOMEM;
}
tevent_req_set_callback(subreq, sdap_get_groups_process, req);
return EOK;
}
static void sdap_nested_done(struct tevent_req *req);
static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
struct sysdb_attrs **groups,
size_t count);
static void sdap_ad_match_rule_members_process(struct tevent_req *subreq);
static void sdap_get_groups_process(struct tevent_req *subreq)
{
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct sdap_get_groups_state *state =
tevent_req_data(req, struct sdap_get_groups_state);
int ret;
int i;
bool next_base = false;
size_t count;
struct sysdb_attrs **groups;
char **sysdb_groupnamelist;
ret = sdap_get_and_parse_generic_recv(subreq, state,
&count, &groups);
talloc_zfree(subreq);
if (ret) {
tevent_req_error(req, ret);
return;
}
DEBUG(SSSDBG_TRACE_FUNC,
"Search for groups, returned %zu results.\n", count);
if (state->lookup_type == SDAP_LOOKUP_WILDCARD || \
state->lookup_type == SDAP_LOOKUP_ENUMERATE || \
count == 0) {
/* No users found in this search or looking up multiple entries */
next_base = true;
}
/* Add this batch of groups to the list */
if (count > 0) {
state->groups =
talloc_realloc(state,
state->groups,
struct sysdb_attrs *,
state->count + count + 1);
if (!state->groups) {
tevent_req_error(req, ENOMEM);
return;
}
sdap_search_group_copy_batch(state, groups, count);
}
if (next_base) {
state->base_iter++;
if (state->search_bases[state->base_iter]) {
/* There are more search bases to try */
ret = sdap_get_groups_next_base(req);
if (ret != EOK) {
tevent_req_error(req, ret);
}
return;
}
}
/* No more search bases
* Return ENOENT if no groups were found
*/
if (state->count == 0) {
tevent_req_error(req, ENOENT);
return;
}
if (state->no_members) {
ret = sysdb_attrs_primary_fqdn_list(state->dom, state,
state->groups, state->count,
state->opts->group_map[SDAP_AT_GROUP_NAME].name,
&sysdb_groupnamelist);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_primary_name_list failed.\n");
tevent_req_error(req, ret);
return;
}
ret = sdap_add_incomplete_groups(state->sysdb, state->dom, state->opts,
sysdb_groupnamelist, state->groups,
state->count);
if (ret == EOK) {
DEBUG(SSSDBG_TRACE_LIBS,
"Writing only group data without members was successful.\n");
tevent_req_done(req);
} else {
DEBUG(SSSDBG_OP_FAILURE, "sdap_add_incomplete_groups failed.\n");
tevent_req_error(req, ret);
}
return;
}
/* Check whether we need to do nested searches
* for RFC2307bis/FreeIPA/ActiveDirectory
* We don't need to do this for enumeration,
* because all groups will be picked up anyway.
*
* We can also skip this if we're using the
* LDAP_MATCHING_RULE_IN_CHAIN available in
* AD 2008 and later
*/
if (state->lookup_type == SDAP_LOOKUP_SINGLE) {
if ((state->opts->schema_type != SDAP_SCHEMA_RFC2307)
&& (dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0)
&& !dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
subreq = sdap_nested_group_send(state, state->ev, state->sdom,
state->opts, state->sh,
state->groups[0]);
if (!subreq) {
tevent_req_error(req, EIO);
return;
}
tevent_req_set_callback(subreq, sdap_nested_done, req);
return;
}
}
/* We have all of the groups. Save them to the sysdb */
state->check_count = state->count;
/* If we're using LDAP_MATCHING_RULE_IN_CHAIN, start a subreq to
* retrieve the members so we can save them in a single step.
*/
if (state->lookup_type == SDAP_LOOKUP_SINGLE
&& (state->opts->schema_type != SDAP_SCHEMA_RFC2307)
&& state->opts->support_matching_rule
&& dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
subreq = sdap_get_ad_match_rule_members_send(
state, state->ev, state->opts, state->sh,
state->groups[0], state->timeout);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq,
sdap_ad_match_rule_members_process,
req);
return;
}
ret = sysdb_transaction_start(state->sysdb);
if (ret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, "Failed to start transaction\n");
tevent_req_error(req, ret);
return;
}
if ((state->lookup_type == SDAP_LOOKUP_ENUMERATE
|| state->lookup_type == SDAP_LOOKUP_WILDCARD)
&& state->opts->schema_type != SDAP_SCHEMA_RFC2307
&& dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0) {
DEBUG(SSSDBG_TRACE_ALL, "Saving groups without members first "
"to allow unrolling of nested groups.\n");
ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
state->groups, state->count, false,
NULL, true, NULL);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
tevent_req_error(req, ret);
return;
}
}
for (i = 0; i < state->count; i++) {
subreq = sdap_process_group_send(state, state->ev, state->dom,
state->sysdb, state->opts,
state->sh, state->groups[i],
state->lookup_type == SDAP_LOOKUP_ENUMERATE);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq, sdap_get_groups_done, req);
}
}
static void sdap_search_group_copy_batch(struct sdap_get_groups_state *state,
struct sysdb_attrs **groups,
size_t count)
{
size_t copied;
bool filter;
/* Always copy all objects for wildcard lookups. */
filter = state->lookup_type == SDAP_LOOKUP_SINGLE ? true : false;
copied = sdap_steal_objects_in_dom(state->opts,
state->groups,
state->count,
state->dom,
groups, count, filter);
state->count += copied;
state->groups[state->count] = NULL;
}
static void sdap_get_groups_done(struct tevent_req *subreq)
{
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct sdap_get_groups_state *state =
tevent_req_data(req, struct sdap_get_groups_state);
int ret;
errno_t sysret;
ret = sdap_process_group_recv(subreq);
talloc_zfree(subreq);
if (ret) {
sysret = sysdb_transaction_cancel(state->sysdb);
if (sysret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, "Could not cancel sysdb transaction\n");
}
tevent_req_error(req, ret);
return;
}
state->check_count--;
DEBUG(SSSDBG_TRACE_ALL, "Groups remaining: %zu\n", state->check_count);
if (state->check_count == 0) {
DEBUG(SSSDBG_TRACE_ALL, "All groups processed\n");
/* If ignore_group_members is set for the domain, don't update
* group memberships in the cache.
*
* If enumeration is on, don't overwrite orig_members as they've been
* saved earlier.
*/
ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
state->groups, state->count,
!state->dom->ignore_group_members, NULL,
state->lookup_type == SDAP_LOOKUP_SINGLE,
&state->higher_usn);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to store groups.\n");
tevent_req_error(req, ret);
return;
}
DEBUG(SSSDBG_TRACE_ALL, "Saving %zu Groups - Done\n", state->count);
sysret = sysdb_transaction_commit(state->sysdb);
if (sysret != EOK) {
DEBUG(SSSDBG_FATAL_FAILURE, "Couldn't commit transaction\n");
tevent_req_error(req, sysret);
} else {
tevent_req_done(req);
}
}
}
static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *sysdb,
struct sss_domain_info *domain,
struct sdap_options *opts,
struct sysdb_attrs **users,
int num_users,
hash_table_t **_ghosts);
static void sdap_ad_match_rule_members_process(struct tevent_req *subreq)
{
errno_t ret;
TALLOC_CTX *tmp_ctx = NULL;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct sdap_get_groups_state *state = tevent_req_data(req,
struct sdap_get_groups_state);
struct sysdb_attrs **users;
struct sysdb_attrs *group = state->groups[0];
struct ldb_message_element *member_el;
struct ldb_message_element *orig_dn_el;
size_t count = 0;
size_t i;
hash_table_t *ghosts;
ret = sdap_get_ad_match_rule_members_recv(subreq, state,
&count, &users);
talloc_zfree(subreq);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Could not retrieve members using AD match rule. [%s]\n",
strerror(ret));
goto done;
}
/* Save the group and users to the cache */
/* Truncate the member attribute of the group.
* It will be repopulated below, and it may currently
* be incomplete anyway, thanks to the range extension.
*/
ret = sysdb_attrs_get_el(group, SYSDB_MEMBER, &member_el);
if (ret != EOK) {
goto done;
}
member_el->num_values = 0;
talloc_zfree(member_el->values);
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
ret = ENOMEM;
goto done;
}
/* Figure out which users are already cached in the sysdb and
* which ones need to be added as ghost users.
*/
ret = sdap_nested_group_populate_users(tmp_ctx, state->sysdb, state->dom,
state->opts, users, count,
&ghosts);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Could not determine which users are ghosts: [%s]\n",
strerror(ret));
goto done;
}
/* Add any entries that aren't in the ghost hash table to the
* member element of the group. This will get converted to a
* native sysdb representation later in sdap_save_groups().
*/
/* Add all of the users as members
*/
member_el->values = talloc_zero_array(tmp_ctx, struct ldb_val, count);
if (!member_el->values) {
ret = ENOMEM;
goto done;
}
/* Copy the origDN values of the users into the member element */
for (i = 0; i < count; i++) {
ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN,
&orig_dn_el);
if (ret != EOK) {
/* This should never happen. Every entry should have
* an originalDN.
*/
DEBUG(SSSDBG_MINOR_FAILURE,
"BUG: Missing originalDN for user?\n");
goto done;
}
/* These values will have the same lifespan, so instead
* of copying them, just point at the data.
*/
member_el->values[i].data = orig_dn_el->values[0].data;
member_el->values[i].length = orig_dn_el->values[0].length;
}
member_el->num_values = count;
/* Now save the group, users and ghosts to the cache */
ret = sdap_save_groups(tmp_ctx, state->sysdb, state->dom,
state->opts, state->groups, 1,
false, ghosts, true, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"Could not save group to the cache: [%s]\n",
strerror(ret));
goto done;
}
ret = EOK;
done:
talloc_free(tmp_ctx);
if (ret == EOK) {
tevent_req_done(req);
} else {
tevent_req_error(req, ret);
}
}
int sdap_get_groups_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx, char **usn_value)
{
struct sdap_get_groups_state *state = tevent_req_data(req,
struct sdap_get_groups_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
if (usn_value) {
*usn_value = talloc_steal(mem_ctx, state->higher_usn);
}
return EOK;
}
static void sdap_nested_ext_done(struct tevent_req *subreq);
static void sdap_nested_done(struct tevent_req *subreq)
{
errno_t ret, tret;
unsigned long user_count;
unsigned long group_count;
bool in_transaction = false;
struct sysdb_attrs **users = NULL;
struct sysdb_attrs **groups = NULL;
hash_table_t *ghosts;
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct sdap_get_groups_state *state = tevent_req_data(req,
struct sdap_get_groups_state);
ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
&group_count, &groups,
&state->missing_external);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed: [%d][%s]\n",
ret, strerror(ret));
goto fail;
}
/* Save all of the users first so that they are in
* place for the groups to add them.
*/
ret = sysdb_transaction_start(state->sysdb);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
goto fail;
}
in_transaction = true;
PROBE(SDAP_NESTED_GROUP_POPULATE_PRE);
ret = sdap_nested_group_populate_users(state, state->sysdb,
state->dom, state->opts,
users, user_count, &ghosts);
PROBE(SDAP_NESTED_GROUP_POPULATE_POST);
if (ret != EOK) {
goto fail;
}
PROBE(SDAP_NESTED_GROUP_SAVE_PRE);
ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
groups, group_count, false, ghosts, true,
&state->higher_usn);
PROBE(SDAP_NESTED_GROUP_SAVE_POST);
if (ret != EOK) {
goto fail;
}
ret = sysdb_transaction_commit(state->sysdb);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
goto fail;
}
in_transaction = false;
if (hash_count(state->missing_external) == 0) {
/* No external members. Processing complete */
DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
tevent_req_done(req);
return;
}
/* At the moment, we need to save the direct groups & members in one
* transaction and then query the others in a separate requests
*/
subreq = sdap_nested_group_lookup_external_send(state, state->ev,
state->dom,
state->opts->ext_ctx,
state->missing_external);
if (subreq == NULL) {
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
return;
fail:
if (in_transaction) {
tret = sysdb_transaction_cancel(state->sysdb);
if (tret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
}
}
tevent_req_error(req, ret);
}
static void sdap_nested_ext_done(struct tevent_req *subreq)
{
errno_t ret;
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct sdap_get_groups_state *state = tevent_req_data(req,
struct sdap_get_groups_state);
ret = sdap_nested_group_lookup_external_recv(state, subreq);
talloc_free(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot resolve external members [%d]: %s\n",
ret, sss_strerror(ret));
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
return;
}
static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *sysdb,
struct sss_domain_info *domain,
struct sdap_options *opts,
struct sysdb_attrs **users,
int num_users,
hash_table_t **_ghosts)
{
int i;
errno_t ret, sret;
struct ldb_message_element *el;
const char *username;
char *clean_orig_dn;
const char *original_dn;
struct sss_domain_info *user_dom;
struct sdap_domain *sdap_dom;
TALLOC_CTX *tmp_ctx;
struct ldb_message **msgs;
char *filter;
const char *sysdb_name;
struct sysdb_attrs *attrs;
static const char *search_attrs[] = { SYSDB_NAME, NULL };
hash_table_t *ghosts;
hash_key_t key;
hash_value_t value;
size_t count;
bool in_transaction = false;
if (_ghosts == NULL) {
return EINVAL;
}
if (num_users == 0) {
/* Nothing to do if there are no users */
*_ghosts = NULL;
return EOK;
}
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) return ENOMEM;
ret = sss_hash_create(tmp_ctx, num_users, &ghosts);
if (ret != HASH_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = sysdb_transaction_start(sysdb);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction!\n");
goto done;
}
in_transaction = true;
for (i = 0; i < num_users; i++) {
ret = sysdb_attrs_get_el(users[i], SYSDB_ORIG_DN, &el);
if (el->num_values == 0) {
ret = EINVAL;
}
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"User entry %d has no originalDN attribute\n", i);
goto done;
}
original_dn = (const char *) el->values[0].data;
ret = sss_filter_sanitize(tmp_ctx, original_dn,
&clean_orig_dn);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Cannot sanitize originalDN [%s]\n", original_dn);
goto done;
}
sdap_dom = sdap_domain_get_by_dn(opts, original_dn);
user_dom = sdap_dom == NULL ? domain : sdap_dom->dom;
ret = sdap_get_user_primary_name(tmp_ctx, opts, users[i],
user_dom, &username);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE,
"User entry %d has no name attribute. Skipping\n", i);
continue;
}
/* Check for the specified origDN in the sysdb */
filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
SYSDB_ORIG_DN,
clean_orig_dn);
if (!filter) {
ret = ENOMEM;
goto done;
}
PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_PRE);
ret = sysdb_search_users(tmp_ctx, user_dom, filter,
search_attrs, &count, &msgs);
PROBE(SDAP_NESTED_GROUP_POPULATE_SEARCH_USERS_POST);
talloc_zfree(filter);
talloc_zfree(clean_orig_dn);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error checking cache for user entry\n");
goto done;
} else if (ret == EOK) {
/* The entry is cached but expired. Update the username
* if needed. */
if (count != 1) {
DEBUG(SSSDBG_CRIT_FAILURE,
"More than one entry with this origDN? Skipping\n");
continue;
}
sysdb_name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
if (strcmp(sysdb_name, username) == 0) {
/* Username is correct, continue */
continue;
}
attrs = sysdb_new_attrs(tmp_ctx);
if (!attrs) {
ret = ENOMEM;
goto done;
}
ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, username);
if (ret) goto done;
ret = sysdb_set_entry_attr(user_dom->sysdb, msgs[0]->dn, attrs,
SYSDB_MOD_REP);
if (ret != EOK) goto done;
} else {
key.type = HASH_KEY_STRING;
key.str = talloc_steal(ghosts, discard_const(original_dn));
value.type = HASH_VALUE_PTR;
/* Already qualified from sdap_get_user_primary_name() */
value.ptr = talloc_steal(ghosts, discard_const(username));
ret = hash_enter(ghosts, &key, &value);
if (ret != HASH_SUCCESS) {
talloc_free(key.str);
talloc_free(value.ptr);
ret = ENOMEM;
goto done;
}
}
}
ret = sysdb_transaction_commit(sysdb);
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction!\n");
goto done;
}
in_transaction = false;
ret = EOK;
done:
if (in_transaction) {
sret = sysdb_transaction_cancel(sysdb);
if (sret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
}
}
if (ret != EOK) {
*_ghosts = NULL;
} else {
*_ghosts = talloc_steal(mem_ctx, ghosts);
}
talloc_zfree(tmp_ctx);
return ret;
}