From 7c4055f0701c9be44f478a713c45e43e5d5914a9 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 8 May 2019 14:39:23 +0200 Subject: [PATCH 58/64] IPA: Implement background refresh for IPA domains Split out the actual useful functionality from the IPA account lookup handler into a tevent request. This tevent request is then used in a new ipa_refresh module. Related: https://pagure.io/SSSD/sssd/issue/4012 Reviewed-by: Sumit Bose (cherry picked from commit d76756ef472da9593c691f94186d09226bb49916) Reviewed-by: Sumit Bose --- Makefile.am | 1 + src/providers/ipa/ipa_common.h | 3 + src/providers/ipa/ipa_id.c | 140 +++++++++++++---- src/providers/ipa/ipa_id.h | 8 + src/providers/ipa/ipa_init.c | 2 +- src/providers/ipa/ipa_refresh.c | 264 ++++++++++++++++++++++++++++++++ 6 files changed, 386 insertions(+), 32 deletions(-) create mode 100644 src/providers/ipa/ipa_refresh.c diff --git a/Makefile.am b/Makefile.am index 7d83b6847..e74de422d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4171,6 +4171,7 @@ libsss_ipa_la_SOURCES = \ src/providers/ipa/ipa_srv.c \ src/providers/ipa/ipa_idmap.c \ src/providers/ipa/ipa_dn.c \ + src/providers/ipa/ipa_refresh.c \ src/providers/ad/ad_opts.c \ src/providers/ad/ad_common.c \ src/providers/ad/ad_dyndns.c \ diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 31e671eb5..6bb1739ef 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -301,4 +301,7 @@ errno_t ipa_get_host_attrs(struct dp_option *ipa_options, struct sysdb_attrs **hosts, struct sysdb_attrs **_ipa_host); +errno_t ipa_refresh_init(struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx); + #endif /* _IPA_COMMON_H_ */ diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c index e644af5ff..9abee34cb 100644 --- a/src/providers/ipa/ipa_id.c +++ b/src/providers/ipa/ipa_id.c @@ -1344,43 +1344,39 @@ ipa_decide_account_info_type(struct dp_id_data *data, struct be_ctx *be_ctx) return IPA_ACCOUNT_INFO_OTHER; } -struct ipa_account_info_handler_state { +struct ipa_account_info_state { enum ipa_account_info_type type; - struct dp_reply_std reply; + + const char *err_msg; + int dp_error; }; -static void ipa_account_info_handler_done(struct tevent_req *subreq); +static void ipa_account_info_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) +ipa_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_id_data *data) { - struct ipa_account_info_handler_state *state; + struct ipa_account_info_state *state = NULL; + struct tevent_req *req = NULL; 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); + struct ipa_account_info_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; - } + state->type = ipa_decide_account_info_type(data, be_ctx); 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); + subreq = ipa_subdomain_account_send(state, be_ctx->ev, id_ctx, data); break; case IPA_ACCOUNT_INFO_NETGROUP: if (data->filter_type != BE_FILTER_NAME) { @@ -1388,11 +1384,11 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, goto immediately; } - subreq = ipa_id_get_netgroup_send(state, params->ev, id_ctx, + subreq = ipa_id_get_netgroup_send(state, be_ctx->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); + subreq = ipa_id_get_account_info_send(state, be_ctx->ev, id_ctx, data); break; } @@ -1400,7 +1396,99 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, ret = ENOMEM; goto immediately; } + tevent_req_set_callback(subreq, ipa_account_info_done, req); + return req; + +immediately: + tevent_req_error(req, ret); + tevent_req_post(req, be_ctx->ev); + return req; +} + +static void ipa_account_info_done(struct tevent_req *subreq) +{ + struct ipa_account_info_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_account_info_state); + + switch (state->type) { + case IPA_ACCOUNT_INFO_SUBDOMAIN: + ret = ipa_subdomain_account_recv(subreq, &state->dp_error); + break; + case IPA_ACCOUNT_INFO_NETGROUP: + ret = ipa_id_get_netgroup_recv(subreq, &state->dp_error); + break; + case IPA_ACCOUNT_INFO_OTHER: + ret = ipa_id_get_account_info_recv(subreq, &state->dp_error); + break; + default: + ret = EINVAL; + break; + } + talloc_zfree(subreq); + + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} +errno_t ipa_account_info_recv(struct tevent_req *req, + int *_dp_error) +{ + struct ipa_account_info_state *state = NULL; + + state = tevent_req_data(req, struct ipa_account_info_state); + + /* Fail the request after collecting the dp_error */ + if (_dp_error) { + *_dp_error = state->dp_error; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct ipa_account_info_handler_state { + 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; + } + + if (sdap_is_enum_request(data)) { + DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); + ret = EOK; + goto immediately; + } + + subreq = ipa_account_info_send(state, params->be_ctx, id_ctx, data); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } tevent_req_set_callback(subreq, ipa_account_info_handler_done, req); return req; @@ -1425,17 +1513,7 @@ static void ipa_account_info_handler_done(struct tevent_req *subreq) 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; - } + ret = ipa_account_info_recv(subreq, &dp_error); talloc_zfree(subreq); /* TODO For backward compatibility we always return EOK to DP now. */ diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h index 4b2549882..fe9acfeef 100644 --- a/src/providers/ipa/ipa_id.h +++ b/src/providers/ipa/ipa_id.h @@ -33,6 +33,14 @@ #define IPA_DEFAULT_VIEW_NAME "Default Trust View" +struct tevent_req * +ipa_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_id_data *data); +errno_t ipa_account_info_recv(struct tevent_req *req, + int *_dp_error); + struct tevent_req * ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, struct ipa_id_ctx *id_ctx, diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index b3060e228..cdfd11d7a 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -594,7 +594,7 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx, } } - ret = sdap_refresh_init(be_ctx, sdap_id_ctx); + ret = ipa_refresh_init(be_ctx, ipa_id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " "will not work [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ipa/ipa_refresh.c b/src/providers/ipa/ipa_refresh.c new file mode 100644 index 000000000..72051cfdd --- /dev/null +++ b/src/providers/ipa/ipa_refresh.c @@ -0,0 +1,264 @@ +/* + Copyright (C) 2019 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 . +*/ + +#include +#include + +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_id.h" + +struct ipa_refresh_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct dp_id_data *account_req; + struct ipa_id_ctx *id_ctx; + char **names; + size_t index; +}; + +static errno_t ipa_refresh_step(struct tevent_req *req); +static void ipa_refresh_done(struct tevent_req *subreq); + +static struct tevent_req *ipa_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + int entry_type, + char **names, + void *pvt) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (names == NULL) { + ret = EOK; + goto immediately; + } + + state->ev = ev; + state->be_ctx = be_ctx; + state->id_ctx = talloc_get_type(pvt, struct ipa_id_ctx); + state->names = names; + state->index = 0; + + state->account_req = be_refresh_acct_req(state, entry_type, + BE_FILTER_NAME, domain); + if (state->account_req == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ipa_refresh_step(req); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n"); + goto immediately; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_refresh_step() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto immediately; + } + + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + + return req; +} + +static errno_t ipa_refresh_step(struct tevent_req *req) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *subreq = NULL; + errno_t ret; + + state = tevent_req_data(req, struct ipa_refresh_state); + + if (state->names == NULL) { + ret = EOK; + goto done; + } + + state->account_req->filter_value = state->names[state->index]; + if (state->account_req->filter_value == NULL) { + ret = EOK; + goto done; + } + + subreq = ipa_account_info_send(state, state->be_ctx, state->id_ctx, + state->account_req); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_refresh_done, req); + + state->index++; + ret = EAGAIN; + +done: + return ret; +} + +static void ipa_refresh_done(struct tevent_req *subreq) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_refresh_state); + + ret = ipa_account_info_recv(subreq, &dp_error); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, " + "errno: %d]\n", be_req2str(state->account_req->entry_type), + dp_error, ret); + goto done; + } + + ret = ipa_refresh_step(req); + if (ret == EAGAIN) { + return; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ipa_refresh_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static struct tevent_req * +ipa_refresh_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_USER, names, pvt); +} + +static errno_t ipa_refresh_users_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +static struct tevent_req * +ipa_refresh_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_GROUP, names, pvt); +} + +static errno_t ipa_refresh_groups_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +static struct tevent_req * +ipa_refresh_netgroups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_NETGROUP, names, pvt); +} + +static errno_t ipa_refresh_netgroups_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +errno_t ipa_refresh_init(struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx) +{ + errno_t ret; + + ret = be_refresh_ctx_init(be_ctx, SYSDB_NAME); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); + return ENOMEM; + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, + ipa_refresh_users_send, + ipa_refresh_users_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of users " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_GROUPS, + ipa_refresh_groups_send, + ipa_refresh_groups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of groups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_NETGROUPS, + ipa_refresh_netgroups_send, + ipa_refresh_netgroups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of netgroups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + return ret; +} -- 2.20.1