From 6777831bc1b0d1218d635d2913326883f509f3e8 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 24 Apr 2019 20:52:11 +0200 Subject: [PATCH 57/64] AD: Implement background refresh for AD domains Split out the actual useful functionality from the AD account handler into a tevent request. This tevent request is then subsequently used by a new ad_refresh module. Related: https://pagure.io/SSSD/sssd/issue/4012 Reviewed-by: Sumit Bose (cherry picked from commit b72adfcc332b13489931483201bcc4c7ecf9ecb6) Reviewed-by: Sumit Bose --- Makefile.am | 5 +- src/providers/ad/ad_common.h | 4 + src/providers/ad/ad_id.c | 140 +++++++++++++---- src/providers/ad/ad_id.h | 10 ++ src/providers/ad/ad_init.c | 2 +- src/providers/ad/ad_refresh.c | 283 ++++++++++++++++++++++++++++++++++ 6 files changed, 412 insertions(+), 32 deletions(-) create mode 100644 src/providers/ad/ad_refresh.c diff --git a/Makefile.am b/Makefile.am index 0c24ae664..7d83b6847 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4243,7 +4243,10 @@ libsss_ad_la_SOURCES = \ src/providers/ad/ad_gpo_ndr.c \ src/providers/ad/ad_srv.c \ src/providers/ad/ad_subdomains.c \ - src/providers/ad/ad_domain_info.c + src/providers/ad/ad_domain_info.c \ + src/providers/ad/ad_refresh.c \ + $(NULL) + if BUILD_SUDO libsss_ad_la_SOURCES += \ diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h index 2f624df3d..44369288e 100644 --- a/src/providers/ad/ad_common.h +++ b/src/providers/ad/ad_common.h @@ -221,4 +221,8 @@ errno_t ad_inherit_opts_if_needed(struct dp_option *parent_opts, struct confdb_ctx *cdb, const char *subdom_conf_path, int opt_id); + +errno_t ad_refresh_init(struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx); + #endif /* AD_COMMON_H_ */ diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index c3bda1662..eb6e36824 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -360,44 +360,36 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx, return clist; } -struct ad_account_info_handler_state { - struct sss_domain_info *domain; - struct dp_reply_std reply; +struct ad_account_info_state { + const char *err_msg; + int dp_error; }; -static void ad_account_info_handler_done(struct tevent_req *subreq); +static void ad_account_info_done(struct tevent_req *subreq); struct tevent_req * -ad_account_info_handler_send(TALLOC_CTX *mem_ctx, - struct ad_id_ctx *id_ctx, - struct dp_id_data *data, - struct dp_req_params *params) +ad_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data) { - struct ad_account_info_handler_state *state; - struct sdap_id_conn_ctx **clist; - struct sdap_id_ctx *sdap_id_ctx; - struct sss_domain_info *domain; + struct sss_domain_info *domain = NULL; + struct ad_account_info_state *state = NULL; + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct sdap_id_conn_ctx **clist = NULL; + struct sdap_id_ctx *sdap_id_ctx = NULL; struct sdap_domain *sdom; - struct tevent_req *subreq; - struct tevent_req *req; - struct be_ctx *be_ctx; errno_t ret; - sdap_id_ctx = id_ctx->sdap_id_ctx; - be_ctx = params->be_ctx; - req = tevent_req_create(mem_ctx, &state, - struct ad_account_info_handler_state); + struct ad_account_info_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; - } + sdap_id_ctx = id_ctx->sdap_id_ctx; domain = be_ctx->domain; if (strcasecmp(data->domain, be_ctx->domain->name) != 0) { @@ -406,6 +398,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, } if (domain == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain\n"); ret = EINVAL; goto immediately; } @@ -413,6 +406,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, /* Determine whether to connect to GC, LDAP or try both. */ clist = get_conn_list(state, id_ctx, domain, data); if (clist == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create conn list\n"); ret = EIO; goto immediately; } @@ -423,14 +417,100 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, goto immediately; } - state->domain = sdom->dom; - subreq = ad_handle_acct_info_send(state, data, sdap_id_ctx, id_ctx->ad_options, sdom, clist); if (subreq == NULL) { ret = ENOMEM; goto immediately; } + tevent_req_set_callback(subreq, ad_account_info_done, req); + return req; + +immediately: + tevent_req_error(req, ret); + tevent_req_post(req, be_ctx->ev); + return req; +} + +static void ad_account_info_done(struct tevent_req *subreq) +{ + struct ad_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 ad_account_info_state); + + ret = ad_handle_acct_info_recv(subreq, &state->dp_error, &state->err_msg); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "ad_handle_acct_info_recv failed [%d]: %s\n", + ret, sss_strerror(ret)); + /* The caller wouldn't fail either, just report the error up */ + } + talloc_zfree(subreq); + tevent_req_done(req); +} + +errno_t ad_account_info_recv(struct tevent_req *req, + int *_dp_error, + const char **_err_msg) +{ + struct ad_account_info_state *state = NULL; + + state = tevent_req_data(req, struct ad_account_info_state); + + if (_err_msg != NULL) { + *_err_msg = state->err_msg; + } + + if (_dp_error) { + *_dp_error = state->dp_error; + } + + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +struct ad_account_info_handler_state { + struct sss_domain_info *domain; + struct dp_reply_std reply; +}; + +static void ad_account_info_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ad_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data, + struct dp_req_params *params) +{ + struct ad_account_info_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + + req = tevent_req_create(mem_ctx, &state, + struct ad_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 = ad_account_info_send(state, params->be_ctx, id_ctx, data); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } tevent_req_set_callback(subreq, ad_account_info_handler_done, req); @@ -451,13 +531,13 @@ static void ad_account_info_handler_done(struct tevent_req *subreq) struct ad_account_info_handler_state *state; struct tevent_req *req; const char *err_msg; - int dp_error; + int dp_error = DP_ERR_FATAL; errno_t ret; req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_account_info_handler_state); - ret = ad_handle_acct_info_recv(subreq, &dp_error, &err_msg); + ret = ad_account_info_recv(subreq, &dp_error, &err_msg); talloc_zfree(subreq); /* TODO For backward compatibility we always return EOK to DP now. */ @@ -466,8 +546,8 @@ static void ad_account_info_handler_done(struct tevent_req *subreq) } errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - struct dp_reply_std *data) + struct tevent_req *req, + struct dp_reply_std *data) { struct ad_account_info_handler_state *state = NULL; diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h index 5154393c5..19cc54eec 100644 --- a/src/providers/ad/ad_id.h +++ b/src/providers/ad/ad_id.h @@ -33,6 +33,16 @@ errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, struct dp_reply_std *data); +struct tevent_req * +ad_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data); + +errno_t ad_account_info_recv(struct tevent_req *req, + int *_dp_error, + const char **_err_msg); + struct tevent_req * ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, struct dp_id_data *ar, diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 42c2f150a..f5aea8904 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -408,7 +408,7 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx, return ret; } - ret = sdap_refresh_init(be_ctx, sdap_id_ctx); + ret = ad_refresh_init(be_ctx, ad_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/ad/ad_refresh.c b/src/providers/ad/ad_refresh.c new file mode 100644 index 000000000..ee541056f --- /dev/null +++ b/src/providers/ad/ad_refresh.c @@ -0,0 +1,283 @@ +/* + 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/ad/ad_common.h" +#include "providers/ad/ad_id.h" + +struct ad_refresh_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct dp_id_data *account_req; + struct ad_id_ctx *id_ctx; + char **names; + size_t index; +}; + +static errno_t ad_refresh_step(struct tevent_req *req); +static void ad_refresh_done(struct tevent_req *subreq); + +static struct tevent_req *ad_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 ad_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + uint32_t filter_type; + + req = tevent_req_create(mem_ctx, &state, + struct ad_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 ad_id_ctx); + state->names = names; + state->index = 0; + + switch (entry_type) { + case BE_REQ_NETGROUP: + filter_type = BE_FILTER_NAME; + break; + case BE_REQ_USER: + case BE_REQ_GROUP: + filter_type = BE_FILTER_SECID; + break; + default: + ret = EINVAL; + goto immediately; + } + + state->account_req = be_refresh_acct_req(state, entry_type, + filter_type, domain); + if (state->account_req == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ad_refresh_step(req); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n"); + goto immediately; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_CRIT_FAILURE, "ad_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 ad_refresh_step(struct tevent_req *req) +{ + struct ad_refresh_state *state = NULL; + struct tevent_req *subreq = NULL; + errno_t ret; + + state = tevent_req_data(req, struct ad_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; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Issuing refresh of %s %s\n", + be_req2str(state->account_req->entry_type), + state->account_req->filter_value); + + subreq = ad_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, ad_refresh_done, req); + + state->index++; + ret = EAGAIN; + +done: + return ret; +} + +static void ad_refresh_done(struct tevent_req *subreq) +{ + struct ad_refresh_state *state = NULL; + struct tevent_req *req = NULL; + const char *err_msg = NULL; + errno_t dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_refresh_state); + + ret = ad_account_info_recv(subreq, &dp_error, &err_msg); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, " + "errno: %d]: %s\n", be_req2str(state->account_req->entry_type), + dp_error, ret, err_msg); + goto done; + } + + ret = ad_refresh_step(req); + if (ret == EAGAIN) { + return; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ad_refresh_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static struct tevent_req * +ad_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 ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_USER, names, pvt); +} + +static errno_t ad_refresh_users_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +static struct tevent_req * +ad_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 ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_GROUP, names, pvt); +} + +static errno_t ad_refresh_groups_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +static struct tevent_req * +ad_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 ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_NETGROUP, names, pvt); +} + +static errno_t ad_refresh_netgroups_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +errno_t ad_refresh_init(struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx) +{ + errno_t ret; + + ret = be_refresh_ctx_init(be_ctx, SYSDB_SID_STR); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); + return ret; + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, + ad_refresh_users_send, + ad_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, + ad_refresh_groups_send, + ad_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, + ad_refresh_netgroups_send, + ad_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