/*
SSSD
IPA Identity Backend Module
Authors:
Jan Zeleny <jzeleny@redhat.com>
Copyright (C) 2011 Red Hat
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 <errno.h>
#include "util/util.h"
#include "db/sysdb.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ipa/ipa_id.h"
static struct tevent_req *
ipa_id_get_account_info_send(TALLOC_CTX *memctx, struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
struct dp_id_data *ar);
static int ipa_id_get_account_info_recv(struct tevent_req *req, int *dp_error);
static bool is_object_overridable(struct dp_id_data *ar)
{
bool ret = false;
switch (ar->entry_type & BE_REQ_TYPE_MASK) {
case BE_REQ_USER:
case BE_REQ_GROUP:
case BE_REQ_INITGROUPS:
case BE_REQ_BY_SECID:
case BE_REQ_USER_AND_GROUP:
case BE_REQ_BY_UUID:
case BE_REQ_BY_CERT:
ret = true;
break;
default:
break;
}
return ret;
}
struct ipa_resolve_user_list_state {
struct tevent_context *ev;
struct ipa_id_ctx *ipa_ctx;
struct ldb_message_element *users;
const char *domain_name;
size_t user_idx;
int dp_error;
};
static errno_t ipa_resolve_user_list_get_user_step(struct tevent_req *req);
static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq);
struct tevent_req *
ipa_resolve_user_list_send(TALLOC_CTX *memctx, struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
const char *domain_name,
struct ldb_message_element *users)
{
int ret;
struct tevent_req *req;
struct ipa_resolve_user_list_state *state;
req = tevent_req_create(memctx, &state,
struct ipa_resolve_user_list_state);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
return NULL;
}
state->ev = ev;
state->ipa_ctx = ipa_ctx;
state->domain_name = domain_name;
state->users = users;
state->user_idx = 0;
state->dp_error = DP_ERR_FATAL;
ret = ipa_resolve_user_list_get_user_step(req);
if (ret == EAGAIN) {
return req;
} else if (ret == EOK) {
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
} else {
DEBUG(SSSDBG_OP_FAILURE,
"ipa_resolve_user_list_get_user_step failed.\n");
tevent_req_error(req, ret);
}
tevent_req_post(req, ev);
return req;
}
static errno_t ipa_resolve_user_list_get_user_step(struct tevent_req *req)
{
int ret;
struct tevent_req *subreq;
struct dp_id_data *ar;
struct ipa_resolve_user_list_state *state = tevent_req_data(req,
struct ipa_resolve_user_list_state);
if (state->user_idx >= state->users->num_values) {
return EOK;
}
ret = get_dp_id_data_for_user_name(state,
(char *) state->users->values[state->user_idx].data,
state->domain_name, &ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_user_name failed.\n");
return ret;
}
DEBUG(SSSDBG_TRACE_ALL, "Trying to resolve user [%s].\n", ar->filter_value);
if (strcasecmp(state->domain_name,
state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
subreq = ipa_subdomain_account_send(state, state->ev, state->ipa_ctx,
ar);
} else {
subreq = ipa_id_get_account_info_send(state, state->ev, state->ipa_ctx,
ar);
}
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct_req_send failed.\n");
return ENOMEM;
}
tevent_req_set_callback(subreq, ipa_resolve_user_list_get_user_done, req);
return EAGAIN;
}
static void ipa_resolve_user_list_get_user_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_resolve_user_list_state *state = tevent_req_data(req,
struct ipa_resolve_user_list_state);
int ret;
if (strcasecmp(state->domain_name,
state->ipa_ctx->sdap_id_ctx->be->domain->name) != 0) {
ret = ipa_subdomain_account_recv(subreq, &state->dp_error);
} else {
ret = ipa_id_get_account_info_recv(subreq, &state->dp_error);
}
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct request failed: %d\n", ret);
goto done;
}
state->user_idx++;
ret = ipa_resolve_user_list_get_user_step(req);
if (ret == EAGAIN) {
return;
}
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ipa_resolve_user_list_get_user_step failed.\n");
}
done:
if (ret == EOK) {
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
} else {
if (state->dp_error == DP_ERR_OK) {
state->dp_error = DP_ERR_FATAL;
}
tevent_req_error(req, ret);
}
return;
}
int ipa_resolve_user_list_recv(struct tevent_req *req, int *dp_error)
{
struct ipa_resolve_user_list_state *state = tevent_req_data(req,
struct ipa_resolve_user_list_state);
if (dp_error) {
*dp_error = state->dp_error;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
struct ipa_initgr_get_overrides_state {
struct tevent_context *ev;
struct ipa_id_ctx *ipa_ctx;
struct sss_domain_info *user_dom;
const char *realm;
struct ldb_message **groups;
size_t group_count;
const char *groups_id_attr;
size_t group_idx;
struct dp_id_data *ar;
int dp_error;
};
static int ipa_initgr_get_overrides_step(struct tevent_req *req);
struct tevent_req *
ipa_initgr_get_overrides_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
struct sss_domain_info *user_dom,
size_t groups_count,
struct ldb_message **groups,
const char *groups_id_attr)
{
int ret;
struct tevent_req *req;
struct ipa_initgr_get_overrides_state *state;
req = tevent_req_create(memctx, &state,
struct ipa_initgr_get_overrides_state);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
return NULL;
}
state->ev = ev;
state->ipa_ctx = ipa_ctx;
state->user_dom = user_dom;
state->groups = groups;
state->group_count = groups_count;
state->group_idx = 0;
state->ar = NULL;
state->realm = dp_opt_get_string(state->ipa_ctx->ipa_options->basic,
IPA_KRB5_REALM);
if (state->realm == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
ret = EINVAL;
goto done;
}
state->groups_id_attr = talloc_strdup(state, groups_id_attr);
if (state->groups_id_attr == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
ret = ENOMEM;
goto done;
}
ret = ipa_initgr_get_overrides_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
tevent_req_post(req, ev);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void ipa_initgr_get_overrides_override_done(struct tevent_req *subreq);
static int ipa_initgr_get_overrides_step(struct tevent_req *req)
{
int ret;
struct tevent_req *subreq;
const char *ipa_uuid;
struct ipa_initgr_get_overrides_state *state = tevent_req_data(req,
struct ipa_initgr_get_overrides_state);
DEBUG(SSSDBG_TRACE_LIBS,
"Processing group %zu/%zu\n", state->group_idx, state->group_count);
if (state->group_idx >= state->group_count) {
return EOK;
}
ipa_uuid = ldb_msg_find_attr_as_string(state->groups[state->group_idx],
state->groups_id_attr, NULL);
if (ipa_uuid == NULL) {
/* This should never happen, the search filter used to get the list
* of groups includes "uuid=*"
*/
DEBUG(SSSDBG_OP_FAILURE,
"The group %s has no UUID attribute %s, error!\n",
ldb_dn_get_linearized(state->groups[state->group_idx]->dn),
state->groups_id_attr);
return EINVAL;
}
talloc_free(state->ar); /* Avoid spiking memory with many groups */
if (strcmp(state->groups_id_attr, SYSDB_UUID) == 0) {
ret = get_dp_id_data_for_uuid(state, ipa_uuid,
state->user_dom->name, &state->ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
return ret;
}
} else if (strcmp(state->groups_id_attr, SYSDB_SID_STR) == 0) {
ret = get_dp_id_data_for_sid(state, ipa_uuid,
state->user_dom->name, &state->ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
return ret;
}
} else {
DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported groups ID type [%s].\n",
state->groups_id_attr);
return EINVAL;
}
DEBUG(SSSDBG_TRACE_LIBS, "Fetching group %s\n", ipa_uuid);
subreq = ipa_get_ad_override_send(state, state->ev,
state->ipa_ctx->sdap_id_ctx,
state->ipa_ctx->ipa_options,
state->realm,
state->ipa_ctx->view_name,
state->ar);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
return ENOMEM;
}
tevent_req_set_callback(subreq,
ipa_initgr_get_overrides_override_done, req);
return EAGAIN;
}
static void ipa_initgr_get_overrides_override_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_initgr_get_overrides_state *state = tevent_req_data(req,
struct ipa_initgr_get_overrides_state);
int ret;
struct sysdb_attrs *override_attrs = NULL;
ret = ipa_get_ad_override_recv(subreq, &state->dp_error, state,
&override_attrs);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "IPA override lookup failed: %d\n", ret);
tevent_req_error(req, ret);
return;
}
if (is_default_view(state->ipa_ctx->view_name)) {
ret = sysdb_apply_default_override(state->user_dom, override_attrs,
state->groups[state->group_idx]->dn);
} else {
ret = sysdb_store_override(state->user_dom,
state->ipa_ctx->view_name,
SYSDB_MEMBER_GROUP,
override_attrs,
state->groups[state->group_idx]->dn);
}
talloc_free(override_attrs);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_override failed.\n");
tevent_req_error(req, ret);
return;
}
state->group_idx++;
ret = ipa_initgr_get_overrides_step(req);
if (ret == EAGAIN) {
return;
} else if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
}
int ipa_initgr_get_overrides_recv(struct tevent_req *req, int *dp_error)
{
struct ipa_initgr_get_overrides_state *state = tevent_req_data(req,
struct ipa_initgr_get_overrides_state);
if (dp_error) {
*dp_error = state->dp_error;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/* Given a user name, retrieve an array of group UUIDs of groups that have
* no overrideDN attribute but do have an UUID attribute.
*/
static errno_t ipa_id_get_group_uuids(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *sysdb,
size_t *_msgs_count,
struct ldb_message ***_msgs)
{
const char *filter;
TALLOC_CTX *tmp_ctx;
char **uuid_list = NULL;
errno_t ret;
struct ldb_dn *base_dn;
const char *attrs[] = { SYSDB_UUID, NULL };
size_t msgs_count;
struct ldb_message **msgs;
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
return ENOMEM;
}
filter = talloc_asprintf(tmp_ctx,
"(&(objectclass=%s)(!(%s=*))(%s=*))",
SYSDB_GROUP_CLASS, SYSDB_OVERRIDE_DN,
SYSDB_UUID);
if (filter == NULL) {
ret = ENOMEM;
goto done;
}
base_dn = sysdb_base_dn(sysdb, tmp_ctx);
if (base_dn == NULL) {
ret = ENOMEM;
goto done;
}
ret = sysdb_search_entry(tmp_ctx, sysdb, base_dn,
LDB_SCOPE_SUBTREE, filter, attrs,
&msgs_count, &msgs);
if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_FUNC,
"No groups without %s in sysdb\n", SYSDB_OVERRIDE_DN);
ret = EOK;
goto done;
} else if (ret != EOK) {
goto done;
}
uuid_list = talloc_zero_array(tmp_ctx, char *, msgs_count);
if (uuid_list == NULL) {
goto done;
}
*_msgs_count = msgs_count;
*_msgs = talloc_steal(mem_ctx, msgs);
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
struct ipa_id_get_account_info_state {
struct tevent_context *ev;
struct ipa_id_ctx *ipa_ctx;
struct sdap_id_ctx *ctx;
struct sdap_id_op *op;
struct sysdb_ctx *sysdb;
struct sss_domain_info *domain;
struct dp_id_data *ar;
struct dp_id_data *orig_ar;
const char *realm;
struct sysdb_attrs *override_attrs;
struct ldb_message *obj_msg;
struct ldb_message_element *ghosts;
struct ldb_message **user_groups;
size_t group_cnt;
size_t group_idx;
int dp_error;
};
static void ipa_id_get_account_info_connected(struct tevent_req *subreq);
static void ipa_id_get_account_info_got_override(struct tevent_req *subreq);
static errno_t ipa_id_get_account_info_get_original_step(struct tevent_req *req,
struct dp_id_data *ar);
static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq);
static void ipa_id_get_account_info_done(struct tevent_req *subreq);
static void ipa_id_get_user_list_done(struct tevent_req *subreq);
static struct tevent_req *
ipa_id_get_account_info_send(TALLOC_CTX *memctx, struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
struct dp_id_data *ar)
{
int ret;
struct tevent_req *req;
struct tevent_req *subreq;
struct ipa_id_get_account_info_state *state;
req = tevent_req_create(memctx, &state,
struct ipa_id_get_account_info_state);
if (req == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
return NULL;
}
state->ev = ev;
state->ipa_ctx = ipa_ctx;
state->ctx = ipa_ctx->sdap_id_ctx;
state->dp_error = DP_ERR_FATAL;
state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
if (state->op == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
ret = ENOMEM;
goto fail;
}
state->domain = find_domain_by_name(state->ctx->be->domain,
ar->domain, true);
if (state->domain == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
ret = ENOMEM;
goto fail;
}
state->sysdb = state->domain->sysdb;
state->ar = ar;
state->realm = dp_opt_get_string(state->ipa_ctx->ipa_options->basic,
IPA_KRB5_REALM);
if (state->realm == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
ret = EINVAL;
goto fail;
}
/* We can skip the override lookup and go directly to the original object
* if
* - the lookup is by SID
* - there is no view set of it is the default view
* - if the EXTRA_INPUT_MAYBE_WITH_VIEW flag is not set
*/
if (is_default_view(state->ipa_ctx->view_name)
|| state->ar->filter_type == BE_FILTER_SECID
|| state->ar->extra_value == NULL
|| strcmp(state->ar->extra_value,
EXTRA_INPUT_MAYBE_WITH_VIEW) != 0
|| ! is_object_overridable(state->ar)) {
ret = ipa_id_get_account_info_get_original_step(req, ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ipa_subdomain_account_get_original_step failed.\n");
goto fail;
}
} else {
subreq = sdap_id_op_connect_send(state->op, state, &ret);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed.\n");
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_account_info_connected, req);
}
return req;
fail:
tevent_req_error(req, ret);
tevent_req_post(req, ev);
return req;
}
static void ipa_id_get_account_info_connected(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
ret = sdap_id_op_connect_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect request failed.\n");
goto fail;
}
subreq = ipa_get_ad_override_send(state, state->ev, state->ctx,
state->ipa_ctx->ipa_options, state->realm,
state->ipa_ctx->view_name, state->ar);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_account_info_got_override, req);
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static void ipa_id_get_account_info_got_override(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
const char *anchor = NULL;
char *anchor_domain;
char *ipa_uuid;
ret = ipa_get_ad_override_recv(subreq, &dp_error, state,
&state->override_attrs);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "IPA override lookup failed: %d\n", ret);
goto fail;
}
if (state->override_attrs != NULL) {
ret = sysdb_attrs_get_string(state->override_attrs,
SYSDB_OVERRIDE_ANCHOR_UUID,
&anchor);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
goto fail;
}
ret = split_ipa_anchor(state, anchor, &anchor_domain, &ipa_uuid);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Unsupported override anchor [%s].\n", anchor);
ret = EINVAL;
goto fail;
}
if (strcmp(state->ar->domain, anchor_domain) == 0) {
state->orig_ar = state->ar;
ret = get_dp_id_data_for_uuid(state, ipa_uuid,
state->ar->domain,
&state->ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_uuid failed.\n");
goto fail;
}
if ((state->orig_ar->entry_type & BE_REQ_TYPE_MASK)
== BE_REQ_INITGROUPS) {
DEBUG(SSSDBG_TRACE_ALL,
"Switching back to BE_REQ_INITGROUPS.\n");
state->ar->entry_type = BE_REQ_INITGROUPS;
state->ar->filter_type = BE_FILTER_UUID;
state->ar->attr_type = BE_ATTR_CORE;
}
} else {
DEBUG(SSSDBG_MINOR_FAILURE,
"Anchor from a different domain [%s], expected [%s]. " \
"This is currently not supported, continue lookup in " \
"local IPA domain.\n",
anchor_domain, state->ar->domain);
}
}
ret = ipa_id_get_account_info_get_original_step(req, state->ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ipa_subdomain_account_get_original_step failed.\n");
goto fail;
}
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static errno_t ipa_id_get_account_info_get_original_step(struct tevent_req *req,
struct dp_id_data *ar)
{
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
struct tevent_req *subreq;
subreq = sdap_handle_acct_req_send(state, state->ctx->be, ar,
state->ipa_ctx->sdap_id_ctx,
state->ipa_ctx->sdap_id_ctx->opts->sdom,
state->ipa_ctx->sdap_id_ctx->conn, true);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct_req_send failed.\n");
return ENOMEM;
}
tevent_req_set_callback(subreq, ipa_id_get_account_info_orig_done, req);
return EOK;
}
static void ipa_id_get_user_groups_done(struct tevent_req *subreq);
static void ipa_id_get_account_info_orig_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
const char *uuid;
const char *class;
enum sysdb_member_type type;
ret = sdap_handle_acct_req_recv(subreq, &dp_error, NULL, NULL);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_handle_acct request failed: %d\n", ret);
goto fail;
}
if (! is_object_overridable(state->ar)) {
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
}
ret = get_object_from_cache(state, state->domain, state->ar,
&state->obj_msg);
if (ret == ENOENT) {
DEBUG(SSSDBG_MINOR_FAILURE, "Object not found, ending request\n");
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
} else if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_object_from_cache failed.\n");
goto fail;
}
class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCLASS,
NULL);
if (class == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find an objectclass.\n");
ret = EINVAL;
goto fail;
}
if (!is_default_view(state->ipa_ctx->view_name)) {
if ((state->ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_GROUP
|| ((state->ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_BY_UUID
&& strcmp(class, SYSDB_GROUP_CLASS) == 0)) {
/* check for ghost members because ghost members are not allowed
* if a view other than the default view is applied.*/
state->ghosts = ldb_msg_find_element(state->obj_msg, SYSDB_GHOST);
} else if ((state->ar->entry_type & BE_REQ_TYPE_MASK) == \
BE_REQ_INITGROUPS) {
/* Get UUID list of groups that have no overrideDN set. */
ret = ipa_id_get_group_uuids(state, state->sysdb,
&state->group_cnt,
&state->user_groups);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot get UUID list: %d\n", ret);
goto fail;
}
}
}
if (state->override_attrs == NULL) {
uuid = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_UUID, NULL);
if (uuid == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find a UUID.\n");
ret = EINVAL;
goto fail;
}
ret = get_dp_id_data_for_uuid(state, uuid, state->domain->name,
&state->ar);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
goto fail;
}
subreq = ipa_get_ad_override_send(state, state->ev,
state->ipa_ctx->sdap_id_ctx,
state->ipa_ctx->ipa_options,
state->realm,
state->ipa_ctx->view_name,
state->ar);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_account_info_done, req);
return;
} else {
if (strcmp(class, SYSDB_USER_CLASS) == 0) {
type = SYSDB_MEMBER_USER;
} else {
type = SYSDB_MEMBER_GROUP;
}
ret = sysdb_store_override(state->domain, state->ipa_ctx->view_name,
type,
state->override_attrs, state->obj_msg->dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_override failed.\n");
goto fail;
}
}
if (state->ghosts != NULL) {
/* Resolve ghost members */
subreq = ipa_resolve_user_list_send(state, state->ev,
state->ipa_ctx,
state->domain->name,
state->ghosts);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_user_list_done, req);
return;
}
if (state->user_groups != NULL) {
subreq = ipa_initgr_get_overrides_send(state, state->ev, state->ipa_ctx,
state->domain, state->group_cnt,
state->user_groups,
SYSDB_UUID);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_user_groups_done, req);
return;
}
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static void ipa_id_get_account_info_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
const char *class;
enum sysdb_member_type type;
ret = ipa_get_ad_override_recv(subreq, &dp_error, state,
&state->override_attrs);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "IPA override lookup failed: %d\n", ret);
goto fail;
}
class = ldb_msg_find_attr_as_string(state->obj_msg, SYSDB_OBJECTCLASS,
NULL);
if (class == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find an objectclass.\n");
ret = EINVAL;
goto fail;
}
if (strcmp(class, SYSDB_USER_CLASS) == 0) {
type = SYSDB_MEMBER_USER;
} else {
type = SYSDB_MEMBER_GROUP;
}
ret = sysdb_store_override(state->domain, state->ipa_ctx->view_name,
type,
state->override_attrs, state->obj_msg->dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_override failed.\n");
goto fail;
}
if (state->ghosts != NULL) {
/* Resolve ghost members */
subreq = ipa_resolve_user_list_send(state, state->ev,
state->ipa_ctx,
state->domain->name,
state->ghosts);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_user_list_done, req);
return;
}
if (state->user_groups != NULL) {
subreq = ipa_initgr_get_overrides_send(state, state->ev, state->ipa_ctx,
state->domain, state->group_cnt,
state->user_groups,
SYSDB_UUID);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ipa_resolve_user_list_send failed.\n");
ret = ENOMEM;
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_user_groups_done, req);
return;
}
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static void ipa_id_get_user_list_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
ret = ipa_resolve_user_list_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "IPA resolve user list %d\n", ret);
goto fail;
}
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static void ipa_id_get_user_groups_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
int dp_error = DP_ERR_FATAL;
int ret;
ret = ipa_initgr_get_overrides_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "IPA resolve user groups %d\n", ret);
goto fail;
}
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
fail:
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
static int ipa_id_get_account_info_recv(struct tevent_req *req, int *dp_error)
{
struct ipa_id_get_account_info_state *state = tevent_req_data(req,
struct ipa_id_get_account_info_state);
if (dp_error) {
*dp_error = state->dp_error;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/* Request for netgroups
* - first start here and then go to ipa_netgroups.c
*/
struct ipa_id_get_netgroup_state {
struct tevent_context *ev;
struct ipa_id_ctx *ctx;
struct sdap_id_op *op;
struct sysdb_ctx *sysdb;
struct sss_domain_info *domain;
const char *name;
int timeout;
char *filter;
const char **attrs;
size_t count;
struct sysdb_attrs **netgroups;
int dp_error;
};
static void ipa_id_get_netgroup_connected(struct tevent_req *subreq);
static void ipa_id_get_netgroup_done(struct tevent_req *subreq);
static struct tevent_req *ipa_id_get_netgroup_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
const char *name)
{
struct tevent_req *req;
struct ipa_id_get_netgroup_state *state;
struct tevent_req *subreq;
struct sdap_id_ctx *ctx;
char *clean_name;
int ret;
ctx = ipa_ctx->sdap_id_ctx;
req = tevent_req_create(memctx, &state, struct ipa_id_get_netgroup_state);
if (!req) return NULL;
state->ev = ev;
state->ctx = ipa_ctx;
state->dp_error = DP_ERR_FATAL;
state->op = sdap_id_op_create(state, ctx->conn->conn_cache);
if (!state->op) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
ret = ENOMEM;
goto fail;
}
state->sysdb = ctx->be->domain->sysdb;
state->domain = ctx->be->domain;
state->name = name;
state->timeout = dp_opt_get_int(ctx->opts->basic, SDAP_SEARCH_TIMEOUT);
ret = sss_filter_sanitize(state, name, &clean_name);
if (ret != EOK) {
goto fail;
}
state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
ctx->opts->netgroup_map[IPA_AT_NETGROUP_NAME].name,
clean_name,
ctx->opts->netgroup_map[IPA_OC_NETGROUP].name);
if (!state->filter) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
ret = ENOMEM;
goto fail;
}
talloc_zfree(clean_name);
ret = build_attrs_from_map(state, ctx->opts->netgroup_map,
IPA_OPTS_NETGROUP, NULL,
&state->attrs, NULL);
if (ret != EOK) goto fail;
subreq = sdap_id_op_connect_send(state->op, state, &ret);
if (!subreq) {
goto fail;
}
tevent_req_set_callback(subreq, ipa_id_get_netgroup_connected, req);
return req;
fail:
tevent_req_error(req, ret);
tevent_req_post(req, ev);
return req;
}
static void ipa_id_get_netgroup_connected(struct tevent_req *subreq)
{
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct ipa_id_get_netgroup_state *state =
tevent_req_data(req, struct ipa_id_get_netgroup_state);
int dp_error = DP_ERR_FATAL;
int ret;
struct sdap_id_ctx *sdap_ctx = state->ctx->sdap_id_ctx;
ret = sdap_id_op_connect_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
subreq = ipa_get_netgroups_send(state, state->ev, state->sysdb,
state->domain, sdap_ctx->opts,
state->ctx->ipa_options,
sdap_id_op_handle(state->op),
state->attrs, state->filter,
state->timeout);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq, ipa_id_get_netgroup_done, req);
return;
}
static void ipa_id_get_netgroup_done(struct tevent_req *subreq)
{
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct ipa_id_get_netgroup_state *state =
tevent_req_data(req, struct ipa_id_get_netgroup_state);
int dp_error = DP_ERR_FATAL;
int ret;
ret = ipa_get_netgroups_recv(subreq, state,
&state->count, &state->netgroups);
talloc_zfree(subreq);
ret = sdap_id_op_done(state->op, ret, &dp_error);
if (dp_error == DP_ERR_OK && ret != EOK) {
/* retry */
subreq = sdap_id_op_connect_send(state->op, state, &ret);
if (!subreq) {
tevent_req_error(req, ret);
return;
}
tevent_req_set_callback(subreq, ipa_id_get_netgroup_connected, req);
return;
}
if (ret && ret != ENOENT) {
state->dp_error = dp_error;
tevent_req_error(req, ret);
return;
}
if (ret == EOK && state->count > 1) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Found more than one netgroup with the name [%s].\n",
state->name);
tevent_req_error(req, EINVAL);
return;
}
if (ret == ENOENT) {
ret = sysdb_delete_netgroup(state->domain, state->name);
if (ret != EOK && ret != ENOENT) {
tevent_req_error(req, ret);
return;
}
}
state->dp_error = DP_ERR_OK;
tevent_req_done(req);
return;
}
static int ipa_id_get_netgroup_recv(struct tevent_req *req, int *dp_error)
{
struct ipa_id_get_netgroup_state *state =
tevent_req_data(req, struct ipa_id_get_netgroup_state);
if (dp_error) {
*dp_error = state->dp_error;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
enum ipa_account_info_type {
IPA_ACCOUNT_INFO_SUBDOMAIN,
IPA_ACCOUNT_INFO_NETGROUP,
IPA_ACCOUNT_INFO_OTHER
};
static enum ipa_account_info_type
ipa_decide_account_info_type(struct dp_id_data *data, struct be_ctx *be_ctx)
{
if (strcasecmp(data->domain, be_ctx->domain->name) != 0) {
return IPA_ACCOUNT_INFO_SUBDOMAIN;
} else if ((data->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_NETGROUP) {
return IPA_ACCOUNT_INFO_NETGROUP;
}
return IPA_ACCOUNT_INFO_OTHER;
}
struct ipa_account_info_handler_state {
enum ipa_account_info_type type;
struct dp_reply_std reply;
};
static void ipa_account_info_handler_done(struct tevent_req *subreq);
struct tevent_req *
ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
struct ipa_id_ctx *id_ctx,
struct dp_id_data *data,
struct dp_req_params *params)
{
struct ipa_account_info_handler_state *state;
struct tevent_req *subreq = NULL;
struct tevent_req *req;
errno_t ret;
req = tevent_req_create(mem_ctx, &state,
struct ipa_account_info_handler_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
}
state->type = ipa_decide_account_info_type(data, params->be_ctx);
if (sdap_is_enum_request(data)) {
DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
ret = EOK;
goto immediately;
}
switch (state->type) {
case IPA_ACCOUNT_INFO_SUBDOMAIN:
/* Subdomain lookups are handled differently on server and client. */
subreq = ipa_subdomain_account_send(state, params->ev, id_ctx, data);
break;
case IPA_ACCOUNT_INFO_NETGROUP:
if (data->filter_type != BE_FILTER_NAME) {
ret = EINVAL;
goto immediately;
}
subreq = ipa_id_get_netgroup_send(state, params->ev, id_ctx,
data->filter_value);
break;
case IPA_ACCOUNT_INFO_OTHER:
subreq = ipa_id_get_account_info_send(state, params->ev, id_ctx, data);
break;
}
if (subreq == NULL) {
ret = ENOMEM;
goto immediately;
}
tevent_req_set_callback(subreq, ipa_account_info_handler_done, req);
return req;
immediately:
dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL);
/* TODO For backward compatibility we always return EOK to DP now. */
tevent_req_done(req);
tevent_req_post(req, params->ev);
return req;
}
static void ipa_account_info_handler_done(struct tevent_req *subreq)
{
struct ipa_account_info_handler_state *state;
struct tevent_req *req;
int dp_error;
errno_t ret = ERR_INTERNAL;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ipa_account_info_handler_state);
switch (state->type) {
case IPA_ACCOUNT_INFO_SUBDOMAIN:
ret = ipa_subdomain_account_recv(subreq, &dp_error);
break;
case IPA_ACCOUNT_INFO_NETGROUP:
ret = ipa_id_get_netgroup_recv(subreq, &dp_error);
break;
case IPA_ACCOUNT_INFO_OTHER:
ret = ipa_id_get_account_info_recv(subreq, &dp_error);
break;
}
talloc_zfree(subreq);
/* TODO For backward compatibility we always return EOK to DP now. */
dp_reply_std_set(&state->reply, dp_error, ret, NULL);
tevent_req_done(req);
}
errno_t ipa_account_info_handler_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
struct dp_reply_std *data)
{
struct ipa_account_info_handler_state *state = NULL;
state = tevent_req_data(req, struct ipa_account_info_handler_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
*data = state->reply;
return EOK;
}